Reputation: 469
Hi I am following this example of using the DjangoObjectPermissionsFilter.
I want to create class SampleModelPermissions(permissions.DjangoObjectPermissions)
so that it satisfies the following description in my "self-documented" DRF API:
This is my code:
in 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
in 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
in 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
But I get the following in POSTMAN:
Any tips on what I should be doing to get my API permissions to work as I describe they should in the self-documentation?
Upvotes: 1
Views: 3224
Reputation: 469
The solution was to recognize that has_permission
covers GET
list and POST
, and has_object_permissions
covers GET
detail, PATCH
, and DELETE
. I had to split my code into those 2 functions accordingly (bear in mind still working out other issues with the code though but the specific problem addressed in this question is covered):
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
Upvotes: 0
Reputation: 1215
Is there a different way to manage permission and achieve results as you describe in documentation.
In the most way you have to assign permissions after create object. You can for example use signals for assign permissions.
Example 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'],
}
Example signals:
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)
Upvotes: 1