mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-02-02 20:54:42 +03:00
Merge master
This commit is contained in:
commit
e8db504a98
|
@ -90,12 +90,17 @@ This permission is suitable if you want to your API to allow read permissions to
|
|||
|
||||
## DjangoModelPermissions
|
||||
|
||||
This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. When applied to a view that has a `.model` property, authorization will only be granted if the user has the relevant model permissions assigned.
|
||||
This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. When applied to a view that has a `.model` property, authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned.
|
||||
|
||||
* `POST` requests require the user to have the `add` permission on the model.
|
||||
* `PUT` and `PATCH` requests require the user to have the `change` permission on the model.
|
||||
* `DELETE` requests require the user to have the `delete` permission on the model.
|
||||
|
||||
If you want to use `DjangoModelPermissions` but also allow unauthenticated users to have read permission, override the class and set the `authenticated_users_only` property to `False`. For example:
|
||||
|
||||
class HasModelPermissionsOrReadOnly(DjangoModelPermissions):
|
||||
authenticated_users_only = False
|
||||
|
||||
The default behaviour can also be overridden to support custom model permissions. For example, you might want to include a `view` model permission for `GET` requests.
|
||||
|
||||
To use custom model permissions, override `DjangoModelPermissions` and set the `.perms_map` property. Refer to the source code for details.
|
||||
|
|
|
@ -109,6 +109,7 @@ The following people have helped make REST framework great.
|
|||
* Wiliam Souza - [waa]
|
||||
* Jonas Braun - [iekadou]
|
||||
* Ian Dash - [bitmonkey]
|
||||
* Bouke Haarsma - [bouke]
|
||||
* Pierre Dulac - [dulaccc]
|
||||
|
||||
Many thanks to everyone who's contributed to the project.
|
||||
|
@ -253,4 +254,5 @@ You can also contact [@_tomchristie][twitter] directly on twitter.
|
|||
[waa]: https://github.com/wiliamsouza
|
||||
[iekadou]: https://github.com/iekadou
|
||||
[bitmonkey]: https://github.com/bitmonkey
|
||||
[bouke]: https://github.com/bouke
|
||||
[dulaccc]: https://github.com/dulaccc
|
||||
|
|
|
@ -44,6 +44,8 @@ You can determine your currently installed version using `pip freeze`:
|
|||
|
||||
* Filtering backends are now applied to the querysets for object lookups as well as lists. (Eg you can use a filtering backend to control which objects should 404)
|
||||
* Deal with error data nicely when deserializing lists of objects.
|
||||
* Extra override hook to configure `DjangoModelPermissions` for unauthenticated users.
|
||||
* Bugfix: Fix pk relationship bug for some types of 1-to-1 relations.
|
||||
* Bugfix: Workaround for Django bug causing case where `Authtoken` could be registered for cascade delete from `User` even if not installed.
|
||||
|
||||
### 2.2.3
|
||||
|
|
|
@ -104,6 +104,8 @@ class DjangoModelPermissions(BasePermission):
|
|||
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
|
||||
}
|
||||
|
||||
authenticated_users_only = True
|
||||
|
||||
def get_required_permissions(self, method, model_cls):
|
||||
"""
|
||||
Given a model and an HTTP method, return the list of permission
|
||||
|
@ -117,13 +119,18 @@ class DjangoModelPermissions(BasePermission):
|
|||
|
||||
def has_permission(self, request, view):
|
||||
model_cls = getattr(view, 'model', None)
|
||||
if not model_cls:
|
||||
return True
|
||||
queryset = getattr(view, 'queryset', None)
|
||||
|
||||
if model_cls is None and queryset is not None:
|
||||
model_cls = queryset.model
|
||||
|
||||
assert model_cls, ('Cannot apply DjangoModelPermissions on a view that'
|
||||
' does not have `.model` or `.queryset` property.')
|
||||
|
||||
perms = self.get_required_permissions(request.method, model_cls)
|
||||
|
||||
if (request.user and
|
||||
request.user.is_authenticated() and
|
||||
(request.user.is_authenticated() or not self.authenticated_users_only) and
|
||||
request.user.has_perms(perms)):
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -235,7 +235,6 @@ class PrimaryKeyRelatedField(RelatedField):
|
|||
pk = getattr(obj, self.source or field_name).pk
|
||||
except ObjectDoesNotExist:
|
||||
return None
|
||||
return self.to_native(obj.pk)
|
||||
|
||||
# Forward relationship
|
||||
return self.to_native(pk)
|
||||
|
|
|
@ -391,11 +391,17 @@ class BaseSerializer(Field):
|
|||
|
||||
return self._data
|
||||
|
||||
def save_object(self, obj):
|
||||
obj.save()
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save the deserialized object and return it.
|
||||
"""
|
||||
self.object.save()
|
||||
if isinstance(self.object, list):
|
||||
[self.save_object(item) for item in self.object]
|
||||
else:
|
||||
self.save_object(self.object)
|
||||
return self.object
|
||||
|
||||
|
||||
|
@ -612,11 +618,11 @@ class ModelSerializer(Serializer):
|
|||
if instance:
|
||||
return self.full_clean(instance)
|
||||
|
||||
def save(self):
|
||||
def save_object(self, obj):
|
||||
"""
|
||||
Save the deserialized object and return it.
|
||||
"""
|
||||
self.object.save()
|
||||
obj.save()
|
||||
|
||||
if getattr(self, 'm2m_data', None):
|
||||
for accessor_name, object_list in self.m2m_data.items():
|
||||
|
@ -628,8 +634,6 @@ class ModelSerializer(Serializer):
|
|||
setattr(self.object, accessor_name, object_list)
|
||||
self.related_data = {}
|
||||
|
||||
return self.object
|
||||
|
||||
|
||||
class HyperlinkedModelSerializerOptions(ModelSerializerOptions):
|
||||
"""
|
||||
|
|
|
@ -407,14 +407,14 @@ class PKNullableOneToOneTests(TestCase):
|
|||
target.save()
|
||||
new_target = OneToOneTarget(name='target-2')
|
||||
new_target.save()
|
||||
source = NullableOneToOneSource(name='source-1', target=target)
|
||||
source = NullableOneToOneSource(name='source-1', target=new_target)
|
||||
source.save()
|
||||
|
||||
def test_reverse_foreign_key_retrieve_with_null(self):
|
||||
queryset = OneToOneTarget.objects.all()
|
||||
serializer = NullableOneToOneTargetSerializer(queryset, many=True)
|
||||
expected = [
|
||||
{'id': 1, 'name': 'target-1', 'nullable_source': 1},
|
||||
{'id': 2, 'name': 'target-2', 'nullable_source': None},
|
||||
{'id': 1, 'name': 'target-1', 'nullable_source': None},
|
||||
{'id': 2, 'name': 'target-2', 'nullable_source': 1},
|
||||
]
|
||||
self.assertEqual(serializer.data, expected)
|
||||
|
|
Loading…
Reference in New Issue
Block a user