Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Kevin Chang 2014-11-13 16:28:51 -08:00 committed by Kevin Chang
commit f7704b8652
20 changed files with 168 additions and 354 deletions

View File

@ -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

View File

@ -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.

View File

@ -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,14 +42,18 @@ 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):
return cls.many_init(*args, **kwargs)
return super(RelatedField, cls).__new__(cls, *args, **kwargs)
@classmethod
def many_init(cls, *args, **kwargs):
list_kwargs = {'child_relation': cls(*args, **kwargs)} list_kwargs = {'child_relation': cls(*args, **kwargs)}
for key in kwargs.keys(): for key in kwargs.keys():
if key in MANY_RELATION_KWARGS: if key in MANY_RELATION_KWARGS:
list_kwargs[key] = kwargs[key] list_kwargs[key] = kwargs[key]
return ManyRelation(**list_kwargs) return ManyRelatedField(**list_kwargs)
return super(RelatedField, cls).__new__(cls, *args, **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.
@ -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):

View File

@ -387,7 +387,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: {

View File

@ -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
# -------------------------------------------- # --------------------------------------------

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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',

View File

@ -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)

View File

@ -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)

View File

@ -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'

View File

@ -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')])

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -1,8 +0,0 @@
from rest_framework import serializers
from tests.users.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User