Это модель:
class Car(models.Model):
user = models.ForeignKey(User, related_name='cars')
name = models.CharField(max_length=64)
Шаблон URL выглядит примерно так:
url(r'^car/(?P<pk>\d+)/$', login_required(CarDetails.as_view()), name='car_details)
И представление:
class CarDetail(DetailView):
context_object_name = 'car'
template_name = 'my_app/car_details.html'
model = models.Car
def get_object(self, *args, **kwargs):
car = super(CarDetail, self).get_object(*args, **kwargs)
if car.user != self.request.user:
raise PermissionDenied()
else:
return car
Работает нормально, нов каждом классе я должен переопределить get_object
, чтобы пользователь не связывался с чужими объектами.Это включает в себя редактирование и удаление для каждой модели, которая у меня есть, и это серьезное нарушение принципа СУХОЙ.
Есть ли лучший способ сделать это?Может быть, что-то вроде декоратора login_required?
РЕДАКТИРОВАТЬ :
Решение было более или менее простым, как предложил Д-р Тырса в своем ответе, с одним небольшим отличием.Я создал базовый класс CurUserOnly
, который наследует object
вместо DetailView
(я хотел использовать этот класс с DeleteView
и UpdateView
тоже), и теперь CarDetail
наследует CurUserOnly
и DetailView
,CarDelete
наследует CurUserOnly
и DeleteView
и так далее ...
Забавно, что я пробовал это раньше, но это не сработало, потому что я забыл MRO в Python и DetailView
был первым всписок наследования, когда CurUserOnly
должно быть!
В конце концов, вот CurUserOnly
class:
class CurUserOnly(object):
def get_object(self, *args, **kwargs):
obj = super(CurUserOnly, self).get_object(*args, **kwargs)
user_attribute = getattr(self, 'user_attribute', 'user')
user = obj
for part in user_attribute.split('.'):
user = getattr(user, part, None)
if user != self.request.user:
raise PermissionDenied()
else:
return obj
И если у меня есть модель, которая не имеет прямого контакта со всеми пользователямиМне нужно сделать, это добавить user_attribute
поле.Например, если у меня есть модель Tyre
с ForeignKey для Car
, ее DeleteView будет выглядеть так:
class TyreDelete(CurUserOnly, DeleteView):
model = models.Tyre
user_attribute = 'car.user'