Django - 对象级权限与基于类的通用视图
这是模型:
class Car(models.Model):
user = models.ForeignKey(User, related_name='cars')
name = models.CharField(max_length=64)
网址模式大致是这样的:
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
方法,以防止用户去修改别人的对象。这包括我所有模型的编辑和删除,这样做严重违反了DRY原则(不要重复自己)。
有没有更好的方法来解决这个问题?比如说像 login_required
装饰器那样?
2 个回答
0
这个解决方案基本上很简单,就像DrTyrsa在他的回答中提到的那样,只是有一点小不同。我创建了一个基础类CurUserOnly
,它继承自object
,而不是DetailView
(因为我也想把这个类用在DeleteView
和UpdateView
上),现在CarDetail
类继承了CurUserOnly
和DetailView
,CarDelete
类继承了CurUserOnly
和DeleteView
,依此类推……
有趣的是,我之前也试过这样做,但没有成功,因为我忘记了Python的MRO(方法解析顺序),导致DetailView
在继承列表中排在前面,而CurUserOnly
应该在前面!
最后,这就是CurUserOnly
类的代码:
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
,它有一个指向Car
的外键,那么它的DeleteView看起来会是这样的:
class TyreDelete(CurUserOnly, DeleteView):
model = models.Tyre
user_attribute = 'car.user'
这个回答作为对问题的编辑发布,问题是Django - 对象级权限和基于类的通用视图,由提问者del-boy发布,采用CC BY-SA 3.0协议。
6
关于定义基类(或者混入类)并使用继承的事情,你怎么看?
class CurUserOnlyDetailView(DetailView):
def get_object(self, *args, **kwargs):
obj = super(CurUserOnlyDetailView, self).get_object(*args, **kwargs)
if obj.user != self.request.user:
raise PermissionDenied()
else:
return obj
class CarDetail(CurUserOnlyDetailView):
context_object_name = 'car'
template_name = 'my_app/car_details.html'
model = models.Car
# another view, no DRY violation
class BikeDetail(CurUserOnlyDetailView):
context_object_name = 'bike'
template_name = 'my_app/bike_details.html'
model = models.Bike