mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-05 04:50:12 +03:00
Merge remote-tracking branch 'reference/master' into bugfix/1936
This commit is contained in:
commit
b523524d8e
|
@ -1,6 +1,7 @@
|
||||||
# Django REST framework
|
# Django REST framework
|
||||||
|
|
||||||
[![build-status-image]][travis]
|
[![build-status-image]][travis]
|
||||||
|
[![pypi-version]][pypi]
|
||||||
|
|
||||||
**Awesome web-browseable Web APIs.**
|
**Awesome web-browseable Web APIs.**
|
||||||
|
|
||||||
|
@ -181,6 +182,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
[build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=master
|
[build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=master
|
||||||
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=master
|
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=master
|
||||||
|
[pypi-version]: https://pypip.in/version/djangorestframework/badge.svg
|
||||||
|
[pypi]: https://pypi.python.org/pypi/djangorestframework
|
||||||
[twitter]: https://twitter.com/_tomchristie
|
[twitter]: https://twitter.com/_tomchristie
|
||||||
[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
|
[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
|
||||||
[0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X
|
[0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X
|
||||||
|
|
|
@ -506,9 +506,9 @@ We now use the following:
|
||||||
|
|
||||||
REST framework now has more explicit and clear control over validating empty values for fields.
|
REST framework now has more explicit and clear control over validating empty values for fields.
|
||||||
|
|
||||||
Previously the meaning of the `required=False` keyword argument was underspecified. In practice its use meant that a field could either be not included in the input, or it could be included, but be `None`.
|
Previously the meaning of the `required=False` keyword argument was underspecified. In practice its use meant that a field could either be not included in the input, or it could be included, but be `None` or the empty string.
|
||||||
|
|
||||||
We now have a better separation, with separate `required` and `allow_none` arguments.
|
We now have a better separation, with separate `required`, `allow_none` and `allow_blank` arguments.
|
||||||
|
|
||||||
The following set of arguments are used to control validation of empty values:
|
The following set of arguments are used to control validation of empty values:
|
||||||
|
|
||||||
|
@ -519,7 +519,7 @@ The following set of arguments are used to control validation of empty values:
|
||||||
|
|
||||||
Typically you'll want to use `required=False` if the corresponding model field has a default value, and additionally set either `allow_none=True` or `allow_blank=True` if required.
|
Typically you'll want to use `required=False` if the corresponding model field has a default value, and additionally set either `allow_none=True` or `allow_blank=True` if required.
|
||||||
|
|
||||||
The `default` argument is there if you need it, but you'll more typically want defaults to be set on model fields, rather than serializer fields.
|
The `default` argument is also available and always implies that the field is not required to be in the input. It is unnecessary to use the `required` argument when a default is specified, and doing so will result in an error.
|
||||||
|
|
||||||
#### Coercing output types.
|
#### Coercing output types.
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,17 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class PKOnlyObject(object):
|
class PKOnlyObject(object):
|
||||||
|
"""
|
||||||
|
This is a mock object, used for when we only need the pk of the object
|
||||||
|
instance, but still want to return an object with a .pk attribute,
|
||||||
|
in order to keep the same interface as a regular model instance.
|
||||||
|
"""
|
||||||
def __init__(self, pk):
|
def __init__(self, pk):
|
||||||
self.pk = pk
|
self.pk = pk
|
||||||
|
|
||||||
|
|
||||||
|
# We assume that 'validators' are intended for the child serializer,
|
||||||
|
# rather than the parent serializer.
|
||||||
MANY_RELATION_KWARGS = (
|
MANY_RELATION_KWARGS = (
|
||||||
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
||||||
'label', 'help_text', 'style', 'error_messages'
|
'label', 'help_text', 'style', 'error_messages'
|
||||||
|
@ -34,15 +42,19 @@ class RelatedField(Field):
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
# We override this method in order to automagically create
|
# We override this method in order to automagically create
|
||||||
# `ManyRelation` classes instead when `many=True` is set.
|
# `ManyRelatedField` classes instead when `many=True` is set.
|
||||||
if kwargs.pop('many', False):
|
if kwargs.pop('many', False):
|
||||||
list_kwargs = {'child_relation': cls(*args, **kwargs)}
|
return cls.many_init(*args, **kwargs)
|
||||||
for key in kwargs.keys():
|
|
||||||
if key in MANY_RELATION_KWARGS:
|
|
||||||
list_kwargs[key] = kwargs[key]
|
|
||||||
return ManyRelation(**list_kwargs)
|
|
||||||
return super(RelatedField, cls).__new__(cls, *args, **kwargs)
|
return super(RelatedField, cls).__new__(cls, *args, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def many_init(cls, *args, **kwargs):
|
||||||
|
list_kwargs = {'child_relation': cls(*args, **kwargs)}
|
||||||
|
for key in kwargs.keys():
|
||||||
|
if key in MANY_RELATION_KWARGS:
|
||||||
|
list_kwargs[key] = kwargs[key]
|
||||||
|
return ManyRelatedField(**list_kwargs)
|
||||||
|
|
||||||
def run_validation(self, data=empty):
|
def run_validation(self, data=empty):
|
||||||
# We force empty strings to None values for relational fields.
|
# We force empty strings to None values for relational fields.
|
||||||
if data == '':
|
if data == '':
|
||||||
|
@ -286,12 +298,12 @@ class SlugRelatedField(RelatedField):
|
||||||
return getattr(obj, self.slug_field)
|
return getattr(obj, self.slug_field)
|
||||||
|
|
||||||
|
|
||||||
class ManyRelation(Field):
|
class ManyRelatedField(Field):
|
||||||
"""
|
"""
|
||||||
Relationships with `many=True` transparently get coerced into instead being
|
Relationships with `many=True` transparently get coerced into instead being
|
||||||
a ManyRelation with a child relationship.
|
a ManyRelatedField with a child relationship.
|
||||||
|
|
||||||
The `ManyRelation` class is responsible for handling iterating through
|
The `ManyRelatedField` class is responsible for handling iterating through
|
||||||
the values and passing each one to the child relationship.
|
the values and passing each one to the child relationship.
|
||||||
|
|
||||||
You shouldn't need to be using this class directly yourself.
|
You shouldn't need to be using this class directly yourself.
|
||||||
|
@ -302,7 +314,7 @@ class ManyRelation(Field):
|
||||||
def __init__(self, child_relation=None, *args, **kwargs):
|
def __init__(self, child_relation=None, *args, **kwargs):
|
||||||
self.child_relation = child_relation
|
self.child_relation = child_relation
|
||||||
assert child_relation is not None, '`child_relation` is a required argument.'
|
assert child_relation is not None, '`child_relation` is a required argument.'
|
||||||
super(ManyRelation, self).__init__(*args, **kwargs)
|
super(ManyRelatedField, self).__init__(*args, **kwargs)
|
||||||
self.child_relation.bind(field_name='', parent=self)
|
self.child_relation.bind(field_name='', parent=self)
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
|
|
|
@ -383,7 +383,10 @@ class HTMLFormRenderer(BaseRenderer):
|
||||||
serializers.MultipleChoiceField: {
|
serializers.MultipleChoiceField: {
|
||||||
'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html'
|
'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html'
|
||||||
},
|
},
|
||||||
serializers.ManyRelation: {
|
serializers.RelatedField: {
|
||||||
|
'base_template': 'select.html', # Also valid: 'radio.html'
|
||||||
|
},
|
||||||
|
serializers.ManyRelatedField: {
|
||||||
'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html'
|
'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html'
|
||||||
},
|
},
|
||||||
serializers.Serializer: {
|
serializers.Serializer: {
|
||||||
|
|
|
@ -46,6 +46,9 @@ import warnings
|
||||||
from rest_framework.relations import * # NOQA
|
from rest_framework.relations import * # NOQA
|
||||||
from rest_framework.fields import * # NOQA
|
from rest_framework.fields import * # NOQA
|
||||||
|
|
||||||
|
|
||||||
|
# We assume that 'validators' are intended for the child serializer,
|
||||||
|
# rather than the parent serializer.
|
||||||
LIST_SERIALIZER_KWARGS = (
|
LIST_SERIALIZER_KWARGS = (
|
||||||
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
||||||
'label', 'help_text', 'style', 'error_messages',
|
'label', 'help_text', 'style', 'error_messages',
|
||||||
|
@ -73,13 +76,25 @@ class BaseSerializer(Field):
|
||||||
# We override this method in order to automagically create
|
# We override this method in order to automagically create
|
||||||
# `ListSerializer` classes instead when `many=True` is set.
|
# `ListSerializer` classes instead when `many=True` is set.
|
||||||
if kwargs.pop('many', False):
|
if kwargs.pop('many', False):
|
||||||
list_kwargs = {'child': cls(*args, **kwargs)}
|
return cls.many_init(*args, **kwargs)
|
||||||
for key in kwargs.keys():
|
|
||||||
if key in LIST_SERIALIZER_KWARGS:
|
|
||||||
list_kwargs[key] = kwargs[key]
|
|
||||||
return ListSerializer(*args, **list_kwargs)
|
|
||||||
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
|
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def many_init(cls, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
This method implements the creation of a `ListSerializer` parent
|
||||||
|
class when `many=True` is used. You can customize it if you need to
|
||||||
|
control which keyword arguments are passed to the parent, and
|
||||||
|
which are passed to the child.
|
||||||
|
"""
|
||||||
|
child_serializer = cls(*args, **kwargs)
|
||||||
|
list_kwargs = {'child': child_serializer}
|
||||||
|
list_kwargs.update(dict([
|
||||||
|
(key, value) for key, value in kwargs.items()
|
||||||
|
if key in LIST_SERIALIZER_KWARGS
|
||||||
|
]))
|
||||||
|
return ListSerializer(*args, **list_kwargs)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
raise NotImplementedError('`to_internal_value()` must be implemented.')
|
raise NotImplementedError('`to_internal_value()` must be implemented.')
|
||||||
|
|
||||||
|
@ -230,18 +245,18 @@ class Serializer(BaseSerializer):
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
if self._initial_data is not None:
|
if self._initial_data is not None:
|
||||||
return ReturnDict([
|
return OrderedDict([
|
||||||
(field_name, field.get_value(self._initial_data))
|
(field_name, field.get_value(self._initial_data))
|
||||||
for field_name, field in self.fields.items()
|
for field_name, field in self.fields.items()
|
||||||
if field.get_value(self._initial_data) is not empty
|
if field.get_value(self._initial_data) is not empty
|
||||||
and not field.read_only
|
and not field.read_only
|
||||||
], serializer=self)
|
])
|
||||||
|
|
||||||
return ReturnDict([
|
return OrderedDict([
|
||||||
(field.field_name, field.get_initial())
|
(field.field_name, field.get_initial())
|
||||||
for field in self.fields.values()
|
for field in self.fields.values()
|
||||||
if not field.read_only
|
if not field.read_only
|
||||||
], serializer=self)
|
])
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
# We override the default field access in order to support
|
# We override the default field access in order to support
|
||||||
|
@ -304,8 +319,8 @@ class Serializer(BaseSerializer):
|
||||||
"""
|
"""
|
||||||
Dict of native values <- Dict of primitive datatypes.
|
Dict of native values <- Dict of primitive datatypes.
|
||||||
"""
|
"""
|
||||||
ret = {}
|
ret = OrderedDict()
|
||||||
errors = ReturnDict(serializer=self)
|
errors = OrderedDict()
|
||||||
fields = [
|
fields = [
|
||||||
field for field in self.fields.values()
|
field for field in self.fields.values()
|
||||||
if (not field.read_only) or (field.default is not empty)
|
if (not field.read_only) or (field.default is not empty)
|
||||||
|
@ -334,7 +349,7 @@ class Serializer(BaseSerializer):
|
||||||
"""
|
"""
|
||||||
Object instance -> Dict of primitive datatypes.
|
Object instance -> Dict of primitive datatypes.
|
||||||
"""
|
"""
|
||||||
ret = ReturnDict(serializer=self)
|
ret = OrderedDict()
|
||||||
fields = [field for field in self.fields.values() if not field.write_only]
|
fields = [field for field in self.fields.values() if not field.write_only]
|
||||||
|
|
||||||
for field in fields:
|
for field in fields:
|
||||||
|
@ -373,6 +388,19 @@ class Serializer(BaseSerializer):
|
||||||
return NestedBoundField(field, value, error)
|
return NestedBoundField(field, value, error)
|
||||||
return BoundField(field, value, error)
|
return BoundField(field, value, error)
|
||||||
|
|
||||||
|
# Include a backlink to the serializer class on return objects.
|
||||||
|
# Allows renderers such as HTMLFormRenderer to get the full field info.
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
ret = super(Serializer, self).data
|
||||||
|
return ReturnDict(ret, serializer=self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def errors(self):
|
||||||
|
ret = super(Serializer, self).errors
|
||||||
|
return ReturnDict(ret, serializer=self)
|
||||||
|
|
||||||
|
|
||||||
# There's some replication of `ListField` here,
|
# There's some replication of `ListField` here,
|
||||||
# but that's probably better than obfuscating the call hierarchy.
|
# but that's probably better than obfuscating the call hierarchy.
|
||||||
|
@ -395,7 +423,7 @@ class ListSerializer(BaseSerializer):
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
if self._initial_data is not None:
|
if self._initial_data is not None:
|
||||||
return self.to_representation(self._initial_data)
|
return self.to_representation(self._initial_data)
|
||||||
return ReturnList(serializer=self)
|
return []
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
"""
|
"""
|
||||||
|
@ -423,7 +451,7 @@ class ListSerializer(BaseSerializer):
|
||||||
})
|
})
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
errors = ReturnList(serializer=self)
|
errors = []
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
try:
|
try:
|
||||||
|
@ -444,37 +472,64 @@ class ListSerializer(BaseSerializer):
|
||||||
List of object instances -> List of dicts of primitive datatypes.
|
List of object instances -> List of dicts of primitive datatypes.
|
||||||
"""
|
"""
|
||||||
iterable = data.all() if (hasattr(data, 'all')) else data
|
iterable = data.all() if (hasattr(data, 'all')) else data
|
||||||
return ReturnList(
|
return [
|
||||||
[self.child.to_representation(item) for item in iterable],
|
self.child.to_representation(item) for item in iterable
|
||||||
serializer=self
|
]
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Serializers with many=True do not support multiple update by "
|
||||||
|
"default, only multiple create. For updates it is unclear how to "
|
||||||
|
"deal with insertions and deletions. If you need to support "
|
||||||
|
"multiple update, use a `ListSerializer` class and override "
|
||||||
|
"`.update()` so you can specify the behavior exactly."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
return [
|
||||||
|
self.child.create(attrs) for attrs in validated_data
|
||||||
|
]
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Save and return a list of object instances.
|
Save and return a list of object instances.
|
||||||
"""
|
"""
|
||||||
assert self.instance is None, (
|
|
||||||
"Serializers do not support multiple update by default, only "
|
|
||||||
"multiple create. For updates it is unclear how to deal with "
|
|
||||||
"insertions and deletions. If you need to support multiple update, "
|
|
||||||
"use a `ListSerializer` class and override `.save()` so you can "
|
|
||||||
"specify the behavior exactly."
|
|
||||||
)
|
|
||||||
|
|
||||||
validated_data = [
|
validated_data = [
|
||||||
dict(list(attrs.items()) + list(kwargs.items()))
|
dict(list(attrs.items()) + list(kwargs.items()))
|
||||||
for attrs in self.validated_data
|
for attrs in self.validated_data
|
||||||
]
|
]
|
||||||
|
|
||||||
self.instance = [
|
if self.instance is not None:
|
||||||
self.child.create(attrs) for attrs in validated_data
|
self.instance = self.update(self.instance, validated_data)
|
||||||
]
|
assert self.instance is not None, (
|
||||||
|
'`update()` did not return an object instance.'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.instance = self.create(validated_data)
|
||||||
|
assert self.instance is not None, (
|
||||||
|
'`create()` did not return an object instance.'
|
||||||
|
)
|
||||||
|
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return representation.list_repr(self, indent=1)
|
return representation.list_repr(self, indent=1)
|
||||||
|
|
||||||
|
# Include a backlink to the serializer class on return objects.
|
||||||
|
# Allows renderers such as HTMLFormRenderer to get the full field info.
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
ret = super(ListSerializer, self).data
|
||||||
|
return ReturnList(ret, serializer=self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def errors(self):
|
||||||
|
ret = super(ListSerializer, self).errors
|
||||||
|
if isinstance(ret, dict):
|
||||||
|
return ReturnDict(ret, serializer=self)
|
||||||
|
return ReturnList(ret, serializer=self)
|
||||||
|
|
||||||
|
|
||||||
# ModelSerializer & HyperlinkedModelSerializer
|
# ModelSerializer & HyperlinkedModelSerializer
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
|
|
|
@ -88,7 +88,7 @@ def get_field_kwargs(field_name, model_field):
|
||||||
kwargs['read_only'] = True
|
kwargs['read_only'] = True
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
if model_field.has_default():
|
if model_field.has_default() or model_field.blank or model_field.null:
|
||||||
kwargs['required'] = False
|
kwargs['required'] = False
|
||||||
|
|
||||||
if model_field.flatchoices:
|
if model_field.flatchoices:
|
||||||
|
@ -215,7 +215,7 @@ def get_relation_kwargs(field_name, relation_info):
|
||||||
# If this field is read-only, then return early.
|
# If this field is read-only, then return early.
|
||||||
# No further keyword arguments are valid.
|
# No further keyword arguments are valid.
|
||||||
return kwargs
|
return kwargs
|
||||||
if model_field.has_default():
|
if model_field.has_default() or model_field.null:
|
||||||
kwargs['required'] = False
|
kwargs['required'] = False
|
||||||
if model_field.null:
|
if model_field.null:
|
||||||
kwargs['allow_null'] = True
|
kwargs['allow_null'] = True
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
from tests.users.models import User
|
|
||||||
|
|
||||||
|
|
||||||
class Account(models.Model):
|
|
||||||
owner = models.ForeignKey(User, related_name='accounts_owned')
|
|
||||||
admins = models.ManyToManyField(User, blank=True, null=True, related_name='accounts_administered')
|
|
|
@ -1,11 +0,0 @@
|
||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
from tests.accounts.models import Account
|
|
||||||
from tests.users.serializers import UserSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class AccountSerializer(serializers.ModelSerializer):
|
|
||||||
admins = UserSerializer(many=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Account
|
|
|
@ -33,9 +33,6 @@ def pytest_configure():
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
'tests',
|
'tests',
|
||||||
'tests.accounts',
|
|
||||||
'tests.records',
|
|
||||||
'tests.users',
|
|
||||||
),
|
),
|
||||||
PASSWORD_HASHERS=(
|
PASSWORD_HASHERS=(
|
||||||
'django.contrib.auth.hashers.SHA1PasswordHasher',
|
'django.contrib.auth.hashers.SHA1PasswordHasher',
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
# From test_validation...
|
|
||||||
|
|
||||||
class TestPreSaveValidationExclusions(TestCase):
|
|
||||||
def test_pre_save_validation_exclusions(self):
|
|
||||||
"""
|
|
||||||
Somewhat weird test case to ensure that we don't perform model
|
|
||||||
validation on read only fields.
|
|
||||||
"""
|
|
||||||
obj = ValidationModel.objects.create(blank_validated_field='')
|
|
||||||
request = factory.put('/', {}, format='json')
|
|
||||||
view = UpdateValidationModel().as_view()
|
|
||||||
response = view(request, pk=obj.pk).render()
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
|
|
||||||
|
|
||||||
# From test_permissions...
|
|
||||||
|
|
||||||
class ModelPermissionsIntegrationTests(TestCase):
|
|
||||||
def setUp(...):
|
|
||||||
...
|
|
||||||
|
|
||||||
def test_has_put_as_create_permissions(self):
|
|
||||||
# User only has update permissions - should be able to update an entity.
|
|
||||||
request = factory.put('/1', {'text': 'foobar'}, format='json',
|
|
||||||
HTTP_AUTHORIZATION=self.updateonly_credentials)
|
|
||||||
response = instance_view(request, pk='1')
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
|
|
||||||
# But if PUTing to a new entity, permission should be denied.
|
|
||||||
request = factory.put('/2', {'text': 'foobar'}, format='json',
|
|
||||||
HTTP_AUTHORIZATION=self.updateonly_credentials)
|
|
||||||
response = instance_view(request, pk='2')
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
@ -1,6 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class Record(models.Model):
|
|
||||||
account = models.ForeignKey('accounts.Account', blank=True, null=True)
|
|
||||||
owner = models.ForeignKey('users.User', blank=True, null=True)
|
|
|
@ -1,165 +0,0 @@
|
||||||
# Django settings for testproject project.
|
|
||||||
|
|
||||||
DEBUG = True
|
|
||||||
TEMPLATE_DEBUG = DEBUG
|
|
||||||
DEBUG_PROPAGATE_EXCEPTIONS = True
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
|
||||||
|
|
||||||
ADMINS = (
|
|
||||||
# ('Your Name', 'your_email@domain.com'),
|
|
||||||
)
|
|
||||||
|
|
||||||
MANAGERS = ADMINS
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
|
||||||
'NAME': 'sqlite.db', # Or path to database file if using sqlite3.
|
|
||||||
'USER': '', # Not used with sqlite3.
|
|
||||||
'PASSWORD': '', # Not used with sqlite3.
|
|
||||||
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
|
|
||||||
'PORT': '', # Set to empty string for default. Not used with sqlite3.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CACHES = {
|
|
||||||
'default': {
|
|
||||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Local time zone for this installation. Choices can be found here:
|
|
||||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
|
||||||
# although not all choices may be available on all operating systems.
|
|
||||||
# On Unix systems, a value of None will cause Django to use the same
|
|
||||||
# timezone as the operating system.
|
|
||||||
# If running in a Windows environment this must be set to the same as your
|
|
||||||
# system time zone.
|
|
||||||
TIME_ZONE = 'Europe/London'
|
|
||||||
|
|
||||||
# Language code for this installation. All choices can be found here:
|
|
||||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
|
||||||
LANGUAGE_CODE = 'en-uk'
|
|
||||||
|
|
||||||
SITE_ID = 1
|
|
||||||
|
|
||||||
# If you set this to False, Django will make some optimizations so as not
|
|
||||||
# to load the internationalization machinery.
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
# If you set this to False, Django will not format dates, numbers and
|
|
||||||
# calendars according to the current locale
|
|
||||||
USE_L10N = True
|
|
||||||
|
|
||||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
|
||||||
# Example: "/home/media/media.lawrence.com/"
|
|
||||||
MEDIA_ROOT = ''
|
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
|
||||||
# trailing slash if there is a path component (optional in other cases).
|
|
||||||
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
|
||||||
MEDIA_URL = ''
|
|
||||||
|
|
||||||
# Make this unique, and don't share it with anybody.
|
|
||||||
SECRET_KEY = 'u@x-aj9(hoh#rb-^ymf#g2jx_hp0vj7u5#b@ag1n^seu9e!%cy'
|
|
||||||
|
|
||||||
# List of callables that know how to import templates from various sources.
|
|
||||||
TEMPLATE_LOADERS = (
|
|
||||||
'django.template.loaders.filesystem.Loader',
|
|
||||||
'django.template.loaders.app_directories.Loader',
|
|
||||||
)
|
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
)
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'tests.urls'
|
|
||||||
|
|
||||||
TEMPLATE_DIRS = (
|
|
||||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
|
||||||
# Always use forward slashes, even on Windows.
|
|
||||||
# Don't forget to use absolute paths, not relative paths.
|
|
||||||
)
|
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.sites',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'rest_framework',
|
|
||||||
'rest_framework.authtoken',
|
|
||||||
'tests',
|
|
||||||
'tests.accounts',
|
|
||||||
'tests.records',
|
|
||||||
'tests.users',
|
|
||||||
)
|
|
||||||
|
|
||||||
# OAuth is optional and won't work if there is no oauth_provider & oauth2
|
|
||||||
try:
|
|
||||||
import oauth_provider # NOQA
|
|
||||||
import oauth2 # NOQA
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
INSTALLED_APPS += (
|
|
||||||
'oauth_provider',
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
import provider # NOQA
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
INSTALLED_APPS += (
|
|
||||||
'provider',
|
|
||||||
'provider.oauth2',
|
|
||||||
)
|
|
||||||
|
|
||||||
# guardian is optional
|
|
||||||
try:
|
|
||||||
import guardian # NOQA
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
ANONYMOUS_USER_ID = -1
|
|
||||||
AUTHENTICATION_BACKENDS = (
|
|
||||||
'django.contrib.auth.backends.ModelBackend', # default
|
|
||||||
'guardian.backends.ObjectPermissionBackend',
|
|
||||||
)
|
|
||||||
INSTALLED_APPS += (
|
|
||||||
'guardian',
|
|
||||||
)
|
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
|
|
||||||
PASSWORD_HASHERS = (
|
|
||||||
'django.contrib.auth.hashers.SHA1PasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.CryptPasswordHasher',
|
|
||||||
)
|
|
||||||
|
|
||||||
AUTH_USER_MODEL = 'auth.User'
|
|
||||||
|
|
||||||
import django
|
|
||||||
|
|
||||||
if django.VERSION < (1, 3):
|
|
||||||
INSTALLED_APPS += ('staticfiles',)
|
|
||||||
|
|
||||||
|
|
||||||
# If we're running on the Jenkins server we want to archive the coverage reports as XML.
|
|
||||||
import os
|
|
||||||
if os.environ.get('HUDSON_URL', None):
|
|
||||||
TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
|
|
||||||
TEST_OUTPUT_VERBOSE = True
|
|
||||||
TEST_OUTPUT_DESCRIPTIONS = True
|
|
||||||
TEST_OUTPUT_DIR = 'xmlrunner'
|
|
|
@ -90,7 +90,7 @@ class TestRegularFieldMappings(TestCase):
|
||||||
email_field = EmailField(max_length=100)
|
email_field = EmailField(max_length=100)
|
||||||
float_field = FloatField()
|
float_field = FloatField()
|
||||||
integer_field = IntegerField()
|
integer_field = IntegerField()
|
||||||
null_boolean_field = NullBooleanField()
|
null_boolean_field = NullBooleanField(required=False)
|
||||||
positive_integer_field = IntegerField()
|
positive_integer_field = IntegerField()
|
||||||
positive_small_integer_field = IntegerField()
|
positive_small_integer_field = IntegerField()
|
||||||
slug_field = SlugField(max_length=100)
|
slug_field = SlugField(max_length=100)
|
||||||
|
@ -112,8 +112,8 @@ class TestRegularFieldMappings(TestCase):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
value_limit_field = IntegerField(max_value=10, min_value=1)
|
value_limit_field = IntegerField(max_value=10, min_value=1)
|
||||||
length_limit_field = CharField(max_length=12, min_length=3)
|
length_limit_field = CharField(max_length=12, min_length=3)
|
||||||
blank_field = CharField(allow_blank=True, max_length=10)
|
blank_field = CharField(allow_blank=True, max_length=10, required=False)
|
||||||
null_field = IntegerField(allow_null=True)
|
null_field = IntegerField(allow_null=True, required=False)
|
||||||
default_field = IntegerField(required=False)
|
default_field = IntegerField(required=False)
|
||||||
descriptive_field = IntegerField(help_text='Some help text', label='A label')
|
descriptive_field = IntegerField(help_text='Some help text', label='A label')
|
||||||
choices_field = ChoiceField(choices=[('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')])
|
choices_field = ChoiceField(choices=[('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')])
|
||||||
|
|
|
@ -95,59 +95,59 @@ class ModelPermissionsIntegrationTests(TestCase):
|
||||||
response = instance_view(request, pk=1)
|
response = instance_view(request, pk=1)
|
||||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
# def test_options_permitted(self):
|
def test_options_permitted(self):
|
||||||
# request = factory.options(
|
request = factory.options(
|
||||||
# '/',
|
'/',
|
||||||
# HTTP_AUTHORIZATION=self.permitted_credentials
|
HTTP_AUTHORIZATION=self.permitted_credentials
|
||||||
# )
|
)
|
||||||
# response = root_view(request, pk='1')
|
response = root_view(request, pk='1')
|
||||||
# self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
# self.assertIn('actions', response.data)
|
self.assertIn('actions', response.data)
|
||||||
# self.assertEqual(list(response.data['actions'].keys()), ['POST'])
|
self.assertEqual(list(response.data['actions'].keys()), ['POST'])
|
||||||
|
|
||||||
# request = factory.options(
|
request = factory.options(
|
||||||
# '/1',
|
'/1',
|
||||||
# HTTP_AUTHORIZATION=self.permitted_credentials
|
HTTP_AUTHORIZATION=self.permitted_credentials
|
||||||
# )
|
)
|
||||||
# response = instance_view(request, pk='1')
|
response = instance_view(request, pk='1')
|
||||||
# self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
# self.assertIn('actions', response.data)
|
self.assertIn('actions', response.data)
|
||||||
# self.assertEqual(list(response.data['actions'].keys()), ['PUT'])
|
self.assertEqual(list(response.data['actions'].keys()), ['PUT'])
|
||||||
|
|
||||||
# def test_options_disallowed(self):
|
def test_options_disallowed(self):
|
||||||
# request = factory.options(
|
request = factory.options(
|
||||||
# '/',
|
'/',
|
||||||
# HTTP_AUTHORIZATION=self.disallowed_credentials
|
HTTP_AUTHORIZATION=self.disallowed_credentials
|
||||||
# )
|
)
|
||||||
# response = root_view(request, pk='1')
|
response = root_view(request, pk='1')
|
||||||
# self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
# self.assertNotIn('actions', response.data)
|
self.assertNotIn('actions', response.data)
|
||||||
|
|
||||||
# request = factory.options(
|
request = factory.options(
|
||||||
# '/1',
|
'/1',
|
||||||
# HTTP_AUTHORIZATION=self.disallowed_credentials
|
HTTP_AUTHORIZATION=self.disallowed_credentials
|
||||||
# )
|
)
|
||||||
# response = instance_view(request, pk='1')
|
response = instance_view(request, pk='1')
|
||||||
# self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
# self.assertNotIn('actions', response.data)
|
self.assertNotIn('actions', response.data)
|
||||||
|
|
||||||
# def test_options_updateonly(self):
|
def test_options_updateonly(self):
|
||||||
# request = factory.options(
|
request = factory.options(
|
||||||
# '/',
|
'/',
|
||||||
# HTTP_AUTHORIZATION=self.updateonly_credentials
|
HTTP_AUTHORIZATION=self.updateonly_credentials
|
||||||
# )
|
)
|
||||||
# response = root_view(request, pk='1')
|
response = root_view(request, pk='1')
|
||||||
# self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
# self.assertNotIn('actions', response.data)
|
self.assertNotIn('actions', response.data)
|
||||||
|
|
||||||
# request = factory.options(
|
request = factory.options(
|
||||||
# '/1',
|
'/1',
|
||||||
# HTTP_AUTHORIZATION=self.updateonly_credentials
|
HTTP_AUTHORIZATION=self.updateonly_credentials
|
||||||
# )
|
)
|
||||||
# response = instance_view(request, pk='1')
|
response = instance_view(request, pk='1')
|
||||||
# self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
# self.assertIn('actions', response.data)
|
self.assertIn('actions', response.data)
|
||||||
# self.assertEqual(list(response.data['actions'].keys()), ['PUT'])
|
self.assertEqual(list(response.data['actions'].keys()), ['PUT'])
|
||||||
|
|
||||||
|
|
||||||
class BasicPermModel(models.Model):
|
class BasicPermModel(models.Model):
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
# from django.test import TestCase
|
|
||||||
|
|
||||||
# from rest_framework import serializers
|
|
||||||
# from tests.accounts.serializers import AccountSerializer
|
|
||||||
|
|
||||||
|
|
||||||
# class ImportingModelSerializerTests(TestCase):
|
|
||||||
# """
|
|
||||||
# In some situations like, GH #1225, it is possible, especially in
|
|
||||||
# testing, to import a serializer who's related models have not yet
|
|
||||||
# been resolved by Django. `AccountSerializer` is an example of such
|
|
||||||
# a serializer (imported at the top of this file).
|
|
||||||
# """
|
|
||||||
# def test_import_model_serializer(self):
|
|
||||||
# """
|
|
||||||
# The serializer at the top of this file should have been
|
|
||||||
# imported successfully, and we should be able to instantiate it.
|
|
||||||
# """
|
|
||||||
# self.assertIsInstance(AccountSerializer(), serializers.ModelSerializer)
|
|
|
@ -1,6 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class User(models.Model):
|
|
||||||
account = models.ForeignKey('accounts.Account', blank=True, null=True, related_name='users')
|
|
||||||
active_record = models.ForeignKey('records.Record', blank=True, null=True)
|
|
|
@ -1,8 +0,0 @@
|
||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
from tests.users.models import User
|
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
Loading…
Reference in New Issue
Block a user