如何自定义 DjangoObjectPermissions?
How to customize DjangoObjectPermissions?
您好,我正在关注 this 使用 DjangoObjectPermissionsFilter 的示例。
我想创建 class SampleModelPermissions(permissions.DjangoObjectPermissions)
以便它满足我的 "self-documented" DRF API 中的以下描述:
这是我的代码:
在models.py中:
class Sample(models.Model):
created = models.DateTimeField(default=datetime.datetime.utcnow, blank=True, null=True)
last_modified = models.DateTimeField(default=datetime.datetime.utcnow, blank=True, null=True)
owner = models.ForeignKey(User, blank=True, null=True, related_name='sample_owner')
text = models.TextField(default='', blank=True, null=True)
class Meta:
permissions = (
('view_sample', "can view sample"),
)
def __unicode__(self):
return self.text
def __str__(self):
return self.text
在views.py中:
class SampleViewSet(viewsets.ModelViewSet):
'''
* Model Description: Sample is a sample model.
* CRUD on Sample model
* C - CREATE - POST /sample/ - allowed as long as owner is the user creating the object
* R - READ - GET /sample/ (list) - user can see objects it owns
* R - READ - GET /sample/[id]/ (detail) - user can see detail page of objects it owns
* U - UPDATE - PATCH /sample/[id]/ - allowed for owner
* D - DELETE - DELETE /sample/[id]/ - allowed for owner
* Note in the case of a nested model A where a field f points to an instance of another model B, you can set f's value to an instance b of B by PATCHing or POSTing with f_id = [the id of b]. Yes, whenever f points to a foreign model, f is read only and f_id is write only.
'''
queryset = Sample.objects.all()
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter, DjangoObjectPermissionsFilter,)
permission_classes = (SampleModelPermissions,)
filter_fields = '__all__'
serializer_class = SampleSerializer
在permisions.py
class SampleModelPermissions(permissions.DjangoObjectPermissions):
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
logger.info('in SampleModelPermissions')
def has_object_permission(self, request, view, obj):
logger.info('in SampleModelPermissions has_object_permission')
print 'permissions.SAFE_METHODS: ', permissions.SAFE_METHODS
if request.method in permissions.SAFE_METHODS:
return request.user == obj.owner or True # need to modify so can see own stuff
elif request.method == 'POST':
print 'checking if user has perm to create obj'
return True # request.user == obj.owner
elif request.method == 'PATCH':
return request.user == obj.owner
elif request.method == 'DELETE':
return request.user == obj.owner
return False
但我在 POSTMAN 中得到以下信息:
有什么提示可以让我的 API 权限按照我在自我文档中描述的那样工作吗?
是否有不同的方法来管理权限并实现您在文档中描述的结果。
在大多数情况下,您必须在创建对象后分配权限。例如,您可以使用信号来分配权限。
示例DjangoObjectPermission
:
class SampleModelPermissions(permissions.DjangoObjectPermissions):
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
示例信号:
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Sample
@receiver(post_save, sender=Sample, dispatch_uid="sample_assign_permission")
def permission_assign(sender, instance, created, **kwargs):
if created:
assign_perm('view_sample' self.request.user, instance)
assign_perm('change_sample', self.request.user, instance)
assign_perm('add_sample', self.request.user, instance)
assign_perm('delete_sample', self.request.user, instance)
解决方案是认识到 has_permission
涵盖 GET
列表和 POST
,has_object_permissions
涵盖 GET
详细信息,PATCH
,和 DELETE
。我不得不相应地将我的代码拆分为这两个函数(请记住,尽管仍在解决代码的其他问题,但涵盖了此问题中解决的具体问题):
class SampleModelPermissions(permissions.DjangoObjectPermissions):
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
logger.info('in SampleModelPermissions')
def has_permission(self, request, view):
logger.info('in SampleModelPermissions has_permission')
if request.method in permissions.SAFE_METHODS:
logger.info('SampleModelPermissions: has_permission: listing samples for user: ' + str(request.user.id))
return True
elif request.method == 'POST':
suggested_owner = None
try:
logger.info('SampleModelPermissions: has_permission: request dict should have a suggested owner: ' + str(dict(request.data.iterlists())))
suggested_owner = int(dict(request.data.iterlists())['owner_id'][0])
except:
logger.error('SampleModelPermissions: has_permission: request made without owner_id: ' + str(dict(request.data.iterlists())))
return False
return request.user.id == suggested_owner
def has_object_permission(self, request, view, obj):
logger.info('in SampleModelPermissions has_object_permission')
print 'permissions.SAFE_METHODS: ', permissions.SAFE_METHODS
if request.method in permissions.SAFE_METHODS:
return request.user == obj.owner or True # need to modify so can see own stuff
elif request.method == 'PATCH':
return request.user == obj.owner
elif request.method == 'DELETE':
return request.user == obj.owner
return False
您好,我正在关注 this 使用 DjangoObjectPermissionsFilter 的示例。
我想创建 class SampleModelPermissions(permissions.DjangoObjectPermissions)
以便它满足我的 "self-documented" DRF API 中的以下描述:
这是我的代码:
在models.py中:
class Sample(models.Model):
created = models.DateTimeField(default=datetime.datetime.utcnow, blank=True, null=True)
last_modified = models.DateTimeField(default=datetime.datetime.utcnow, blank=True, null=True)
owner = models.ForeignKey(User, blank=True, null=True, related_name='sample_owner')
text = models.TextField(default='', blank=True, null=True)
class Meta:
permissions = (
('view_sample', "can view sample"),
)
def __unicode__(self):
return self.text
def __str__(self):
return self.text
在views.py中:
class SampleViewSet(viewsets.ModelViewSet):
'''
* Model Description: Sample is a sample model.
* CRUD on Sample model
* C - CREATE - POST /sample/ - allowed as long as owner is the user creating the object
* R - READ - GET /sample/ (list) - user can see objects it owns
* R - READ - GET /sample/[id]/ (detail) - user can see detail page of objects it owns
* U - UPDATE - PATCH /sample/[id]/ - allowed for owner
* D - DELETE - DELETE /sample/[id]/ - allowed for owner
* Note in the case of a nested model A where a field f points to an instance of another model B, you can set f's value to an instance b of B by PATCHing or POSTing with f_id = [the id of b]. Yes, whenever f points to a foreign model, f is read only and f_id is write only.
'''
queryset = Sample.objects.all()
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter, DjangoObjectPermissionsFilter,)
permission_classes = (SampleModelPermissions,)
filter_fields = '__all__'
serializer_class = SampleSerializer
在permisions.py
class SampleModelPermissions(permissions.DjangoObjectPermissions):
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
logger.info('in SampleModelPermissions')
def has_object_permission(self, request, view, obj):
logger.info('in SampleModelPermissions has_object_permission')
print 'permissions.SAFE_METHODS: ', permissions.SAFE_METHODS
if request.method in permissions.SAFE_METHODS:
return request.user == obj.owner or True # need to modify so can see own stuff
elif request.method == 'POST':
print 'checking if user has perm to create obj'
return True # request.user == obj.owner
elif request.method == 'PATCH':
return request.user == obj.owner
elif request.method == 'DELETE':
return request.user == obj.owner
return False
但我在 POSTMAN 中得到以下信息:
有什么提示可以让我的 API 权限按照我在自我文档中描述的那样工作吗?
是否有不同的方法来管理权限并实现您在文档中描述的结果。
在大多数情况下,您必须在创建对象后分配权限。例如,您可以使用信号来分配权限。
示例DjangoObjectPermission
:
class SampleModelPermissions(permissions.DjangoObjectPermissions):
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
示例信号:
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Sample
@receiver(post_save, sender=Sample, dispatch_uid="sample_assign_permission")
def permission_assign(sender, instance, created, **kwargs):
if created:
assign_perm('view_sample' self.request.user, instance)
assign_perm('change_sample', self.request.user, instance)
assign_perm('add_sample', self.request.user, instance)
assign_perm('delete_sample', self.request.user, instance)
解决方案是认识到 has_permission
涵盖 GET
列表和 POST
,has_object_permissions
涵盖 GET
详细信息,PATCH
,和 DELETE
。我不得不相应地将我的代码拆分为这两个函数(请记住,尽管仍在解决代码的其他问题,但涵盖了此问题中解决的具体问题):
class SampleModelPermissions(permissions.DjangoObjectPermissions):
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
logger.info('in SampleModelPermissions')
def has_permission(self, request, view):
logger.info('in SampleModelPermissions has_permission')
if request.method in permissions.SAFE_METHODS:
logger.info('SampleModelPermissions: has_permission: listing samples for user: ' + str(request.user.id))
return True
elif request.method == 'POST':
suggested_owner = None
try:
logger.info('SampleModelPermissions: has_permission: request dict should have a suggested owner: ' + str(dict(request.data.iterlists())))
suggested_owner = int(dict(request.data.iterlists())['owner_id'][0])
except:
logger.error('SampleModelPermissions: has_permission: request made without owner_id: ' + str(dict(request.data.iterlists())))
return False
return request.user.id == suggested_owner
def has_object_permission(self, request, view, obj):
logger.info('in SampleModelPermissions has_object_permission')
print 'permissions.SAFE_METHODS: ', permissions.SAFE_METHODS
if request.method in permissions.SAFE_METHODS:
return request.user == obj.owner or True # need to modify so can see own stuff
elif request.method == 'PATCH':
return request.user == obj.owner
elif request.method == 'DELETE':
return request.user == obj.owner
return False