diff options
Diffstat (limited to 'tests')
44 files changed, 3701 insertions, 6871 deletions
diff --git a/tests/accounts/__init__.py b/tests/accounts/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/tests/accounts/__init__.py +++ /dev/null diff --git a/tests/accounts/models.py b/tests/accounts/models.py deleted file mode 100644 index 3bf4a0c3..00000000 --- a/tests/accounts/models.py +++ /dev/null @@ -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') diff --git a/tests/accounts/serializers.py b/tests/accounts/serializers.py deleted file mode 100644 index 57a91b92..00000000 --- a/tests/accounts/serializers.py +++ /dev/null @@ -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 diff --git a/tests/conftest.py b/tests/conftest.py index 67986621..44ed070b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,9 +33,6 @@ def pytest_configure(): 'rest_framework', 'rest_framework.authtoken', 'tests', - 'tests.accounts', - 'tests.records', - 'tests.users', ), PASSWORD_HASHERS=( 'django.contrib.auth.hashers.SHA1PasswordHasher', diff --git a/tests/put_as_create_workspace.txt b/tests/put_as_create_workspace.txt deleted file mode 100644 index 6bc5218e..00000000 --- a/tests/put_as_create_workspace.txt +++ /dev/null @@ -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) diff --git a/tests/records/__init__.py b/tests/records/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/tests/records/__init__.py +++ /dev/null diff --git a/tests/records/models.py b/tests/records/models.py deleted file mode 100644 index 76954807..00000000 --- a/tests/records/models.py +++ /dev/null @@ -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) diff --git a/tests/settings.py b/tests/settings.py deleted file mode 100644 index 6a01669c..00000000 --- a/tests/settings.py +++ /dev/null @@ -1,144 +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', -) - -# 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' diff --git a/tests/test_authentication.py b/tests/test_authentication.py index ece6eff5..bf0fd6fb 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -108,7 +108,7 @@ class SessionAuthTests(TestCase): cf. [#1810](https://github.com/tomchristie/django-rest-framework/pull/1810) """ response = self.csrf_client.get('/auth/login/') - self.assertContains(response, '<Label class="span4">Username:</label>') + self.assertContains(response, '<label class="span4">Username:</label>') def test_post_form_session_auth_failing_csrf(self): """ diff --git a/tests/test_bound_fields.py b/tests/test_bound_fields.py new file mode 100644 index 00000000..469437e4 --- /dev/null +++ b/tests/test_bound_fields.py @@ -0,0 +1,69 @@ +from rest_framework import serializers + + +class TestSimpleBoundField: + def test_empty_bound_field(self): + class ExampleSerializer(serializers.Serializer): + text = serializers.CharField(max_length=100) + amount = serializers.IntegerField() + + serializer = ExampleSerializer() + + assert serializer['text'].value == '' + assert serializer['text'].errors is None + assert serializer['text'].name == 'text' + assert serializer['amount'].value is None + assert serializer['amount'].errors is None + assert serializer['amount'].name == 'amount' + + def test_populated_bound_field(self): + class ExampleSerializer(serializers.Serializer): + text = serializers.CharField(max_length=100) + amount = serializers.IntegerField() + + serializer = ExampleSerializer(data={'text': 'abc', 'amount': 123}) + + assert serializer['text'].value == 'abc' + assert serializer['text'].errors is None + assert serializer['text'].name == 'text' + assert serializer['amount'].value is 123 + assert serializer['amount'].errors is None + assert serializer['amount'].name == 'amount' + + def test_error_bound_field(self): + class ExampleSerializer(serializers.Serializer): + text = serializers.CharField(max_length=100) + amount = serializers.IntegerField() + + serializer = ExampleSerializer(data={'text': 'x' * 1000, 'amount': 123}) + serializer.is_valid() + + assert serializer['text'].value == 'x' * 1000 + assert serializer['text'].errors == ['Ensure this field has no more than 100 characters.'] + assert serializer['text'].name == 'text' + assert serializer['amount'].value is 123 + assert serializer['amount'].errors is None + assert serializer['amount'].name == 'amount' + + +class TestNestedBoundField: + def test_nested_empty_bound_field(self): + class Nested(serializers.Serializer): + more_text = serializers.CharField(max_length=100) + amount = serializers.IntegerField() + + class ExampleSerializer(serializers.Serializer): + text = serializers.CharField(max_length=100) + nested = Nested() + + serializer = ExampleSerializer() + + assert serializer['text'].value == '' + assert serializer['text'].errors is None + assert serializer['text'].name == 'text' + assert serializer['nested']['more_text'].value == '' + assert serializer['nested']['more_text'].errors is None + assert serializer['nested']['more_text'].name == 'nested.more_text' + assert serializer['nested']['amount'].value is None + assert serializer['nested']['amount'].errors is None + assert serializer['nested']['amount'].name == 'nested.amount' diff --git a/tests/test_fields.py b/tests/test_fields.py index a92fafbc..13525632 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,1042 +1,1028 @@ -# """ -# General serializer field tests. -# """ -# from __future__ import unicode_literals - -# import datetime -# import re -# from decimal import Decimal -# from uuid import uuid4 -# from django.core import validators -# from django.db import models -# from django.test import TestCase -# from django.utils.datastructures import SortedDict -# from rest_framework import serializers -# from tests.models import RESTFrameworkModel - - -# class TimestampedModel(models.Model): -# added = models.DateTimeField(auto_now_add=True) -# updated = models.DateTimeField(auto_now=True) - - -# class CharPrimaryKeyModel(models.Model): -# id = models.CharField(max_length=20, primary_key=True) - - -# class TimestampedModelSerializer(serializers.ModelSerializer): -# class Meta: -# model = TimestampedModel - - -# class CharPrimaryKeyModelSerializer(serializers.ModelSerializer): -# class Meta: -# model = CharPrimaryKeyModel - - -# class TimeFieldModel(models.Model): -# clock = models.TimeField() - - -# class TimeFieldModelSerializer(serializers.ModelSerializer): -# class Meta: -# model = TimeFieldModel - - -# SAMPLE_CHOICES = [ -# ('red', 'Red'), -# ('green', 'Green'), -# ('blue', 'Blue'), -# ] - - -# class ChoiceFieldModel(models.Model): -# choice = models.CharField(choices=SAMPLE_CHOICES, blank=True, max_length=255) - - -# class ChoiceFieldModelSerializer(serializers.ModelSerializer): -# class Meta: -# model = ChoiceFieldModel - - -# class ChoiceFieldModelWithNull(models.Model): -# choice = models.CharField(choices=SAMPLE_CHOICES, blank=True, null=True, max_length=255) - - -# class ChoiceFieldModelWithNullSerializer(serializers.ModelSerializer): -# class Meta: -# model = ChoiceFieldModelWithNull - - -# class BasicFieldTests(TestCase): -# def test_auto_now_fields_read_only(self): -# """ -# auto_now and auto_now_add fields should be read_only by default. -# """ -# serializer = TimestampedModelSerializer() -# self.assertEqual(serializer.fields['added'].read_only, True) - -# def test_auto_pk_fields_read_only(self): -# """ -# AutoField fields should be read_only by default. -# """ -# serializer = TimestampedModelSerializer() -# self.assertEqual(serializer.fields['id'].read_only, True) - -# def test_non_auto_pk_fields_not_read_only(self): -# """ -# PK fields other than AutoField fields should not be read_only by default. -# """ -# serializer = CharPrimaryKeyModelSerializer() -# self.assertEqual(serializer.fields['id'].read_only, False) - -# def test_dict_field_ordering(self): -# """ -# Field should preserve dictionary ordering, if it exists. -# See: https://github.com/tomchristie/django-rest-framework/issues/832 -# """ -# ret = SortedDict() -# ret['c'] = 1 -# ret['b'] = 1 -# ret['a'] = 1 -# ret['z'] = 1 -# field = serializers.Field() -# keys = list(field.to_native(ret).keys()) -# self.assertEqual(keys, ['c', 'b', 'a', 'z']) - -# def test_widget_html_attributes(self): -# """ -# Make sure widget_html() renders the correct attributes -# """ -# r = re.compile('(\S+)=["\']?((?:.(?!["\']?\s+(?:\S+)=|[>"\']))+.)["\']?') -# form = TimeFieldModelSerializer().data -# attributes = r.findall(form.fields['clock'].widget_html()) -# self.assertIn(('name', 'clock'), attributes) -# self.assertIn(('id', 'clock'), attributes) - - -# class DateFieldTest(TestCase): -# """ -# Tests for the DateFieldTest from_native() and to_native() behavior -# """ - -# def test_from_native_string(self): -# """ -# Make sure from_native() accepts default iso input formats. -# """ -# f = serializers.DateField() -# result_1 = f.from_native('1984-07-31') - -# self.assertEqual(datetime.date(1984, 7, 31), result_1) - -# def test_from_native_datetime_date(self): -# """ -# Make sure from_native() accepts a datetime.date instance. -# """ -# f = serializers.DateField() -# result_1 = f.from_native(datetime.date(1984, 7, 31)) - -# self.assertEqual(result_1, datetime.date(1984, 7, 31)) - -# def test_from_native_custom_format(self): -# """ -# Make sure from_native() accepts custom input formats. -# """ -# f = serializers.DateField(input_formats=['%Y -- %d']) -# result = f.from_native('1984 -- 31') - -# self.assertEqual(datetime.date(1984, 1, 31), result) - -# def test_from_native_invalid_default_on_custom_format(self): -# """ -# Make sure from_native() don't accept default formats if custom format is preset -# """ -# f = serializers.DateField(input_formats=['%Y -- %d']) - -# try: -# f.from_native('1984-07-31') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Date has wrong format. Use one of these formats instead: YYYY -- DD"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_from_native_empty(self): -# """ -# Make sure from_native() returns None on empty param. -# """ -# f = serializers.DateField() -# result = f.from_native('') - -# self.assertEqual(result, None) - -# def test_from_native_none(self): -# """ -# Make sure from_native() returns None on None param. -# """ -# f = serializers.DateField() -# result = f.from_native(None) - -# self.assertEqual(result, None) - -# def test_from_native_invalid_date(self): -# """ -# Make sure from_native() raises a ValidationError on passing an invalid date. -# """ -# f = serializers.DateField() - -# try: -# f.from_native('1984-13-31') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_from_native_invalid_format(self): -# """ -# Make sure from_native() raises a ValidationError on passing an invalid format. -# """ -# f = serializers.DateField() - -# try: -# f.from_native('1984 -- 31') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_to_native(self): -# """ -# Make sure to_native() returns datetime as default. -# """ -# f = serializers.DateField() - -# result_1 = f.to_native(datetime.date(1984, 7, 31)) - -# self.assertEqual(datetime.date(1984, 7, 31), result_1) - -# def test_to_native_iso(self): -# """ -# Make sure to_native() with 'iso-8601' returns iso formated date. -# """ -# f = serializers.DateField(format='iso-8601') - -# result_1 = f.to_native(datetime.date(1984, 7, 31)) - -# self.assertEqual('1984-07-31', result_1) - -# def test_to_native_custom_format(self): -# """ -# Make sure to_native() returns correct custom format. -# """ -# f = serializers.DateField(format="%Y - %m.%d") - -# result_1 = f.to_native(datetime.date(1984, 7, 31)) - -# self.assertEqual('1984 - 07.31', result_1) - -# def test_to_native_none(self): -# """ -# Make sure from_native() returns None on None param. -# """ -# f = serializers.DateField(required=False) -# self.assertEqual(None, f.to_native(None)) - - -# class DateTimeFieldTest(TestCase): -# """ -# Tests for the DateTimeField from_native() and to_native() behavior -# """ - -# def test_from_native_string(self): -# """ -# Make sure from_native() accepts default iso input formats. -# """ -# f = serializers.DateTimeField() -# result_1 = f.from_native('1984-07-31 04:31') -# result_2 = f.from_native('1984-07-31 04:31:59') -# result_3 = f.from_native('1984-07-31 04:31:59.000200') - -# self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31), result_1) -# self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31, 59), result_2) -# self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31, 59, 200), result_3) - -# def test_from_native_datetime_datetime(self): -# """ -# Make sure from_native() accepts a datetime.datetime instance. -# """ -# f = serializers.DateTimeField() -# result_1 = f.from_native(datetime.datetime(1984, 7, 31, 4, 31)) -# result_2 = f.from_native(datetime.datetime(1984, 7, 31, 4, 31, 59)) -# result_3 = f.from_native(datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) - -# self.assertEqual(result_1, datetime.datetime(1984, 7, 31, 4, 31)) -# self.assertEqual(result_2, datetime.datetime(1984, 7, 31, 4, 31, 59)) -# self.assertEqual(result_3, datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) - -# def test_from_native_custom_format(self): -# """ -# Make sure from_native() accepts custom input formats. -# """ -# f = serializers.DateTimeField(input_formats=['%Y -- %H:%M']) -# result = f.from_native('1984 -- 04:59') - -# self.assertEqual(datetime.datetime(1984, 1, 1, 4, 59), result) - -# def test_from_native_invalid_default_on_custom_format(self): -# """ -# Make sure from_native() don't accept default formats if custom format is preset -# """ -# f = serializers.DateTimeField(input_formats=['%Y -- %H:%M']) - -# try: -# f.from_native('1984-07-31 04:31:59') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: YYYY -- hh:mm"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_from_native_empty(self): -# """ -# Make sure from_native() returns None on empty param. -# """ -# f = serializers.DateTimeField() -# result = f.from_native('') - -# self.assertEqual(result, None) - -# def test_from_native_none(self): -# """ -# Make sure from_native() returns None on None param. -# """ -# f = serializers.DateTimeField() -# result = f.from_native(None) - -# self.assertEqual(result, None) - -# def test_from_native_invalid_datetime(self): -# """ -# Make sure from_native() raises a ValidationError on passing an invalid datetime. -# """ -# f = serializers.DateTimeField() - -# try: -# f.from_native('04:61:59') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: " -# "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_from_native_invalid_format(self): -# """ -# Make sure from_native() raises a ValidationError on passing an invalid format. -# """ -# f = serializers.DateTimeField() - -# try: -# f.from_native('04 -- 31') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: " -# "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_to_native(self): -# """ -# Make sure to_native() returns isoformat as default. -# """ -# f = serializers.DateTimeField() - -# result_1 = f.to_native(datetime.datetime(1984, 7, 31)) -# result_2 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31)) -# result_3 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59)) -# result_4 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) - -# self.assertEqual(datetime.datetime(1984, 7, 31), result_1) -# self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31), result_2) -# self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31, 59), result_3) -# self.assertEqual(datetime.datetime(1984, 7, 31, 4, 31, 59, 200), result_4) - -# def test_to_native_iso(self): -# """ -# Make sure to_native() with format=iso-8601 returns iso formatted datetime. -# """ -# f = serializers.DateTimeField(format='iso-8601') - -# result_1 = f.to_native(datetime.datetime(1984, 7, 31)) -# result_2 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31)) -# result_3 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59)) -# result_4 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) - -# self.assertEqual('1984-07-31T00:00:00', result_1) -# self.assertEqual('1984-07-31T04:31:00', result_2) -# self.assertEqual('1984-07-31T04:31:59', result_3) -# self.assertEqual('1984-07-31T04:31:59.000200', result_4) - -# def test_to_native_custom_format(self): -# """ -# Make sure to_native() returns correct custom format. -# """ -# f = serializers.DateTimeField(format="%Y - %H:%M") - -# result_1 = f.to_native(datetime.datetime(1984, 7, 31)) -# result_2 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31)) -# result_3 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59)) -# result_4 = f.to_native(datetime.datetime(1984, 7, 31, 4, 31, 59, 200)) - -# self.assertEqual('1984 - 00:00', result_1) -# self.assertEqual('1984 - 04:31', result_2) -# self.assertEqual('1984 - 04:31', result_3) -# self.assertEqual('1984 - 04:31', result_4) - -# def test_to_native_none(self): -# """ -# Make sure from_native() returns None on None param. -# """ -# f = serializers.DateTimeField(required=False) -# self.assertEqual(None, f.to_native(None)) - - -# class TimeFieldTest(TestCase): -# """ -# Tests for the TimeField from_native() and to_native() behavior -# """ - -# def test_from_native_string(self): -# """ -# Make sure from_native() accepts default iso input formats. -# """ -# f = serializers.TimeField() -# result_1 = f.from_native('04:31') -# result_2 = f.from_native('04:31:59') -# result_3 = f.from_native('04:31:59.000200') - -# self.assertEqual(datetime.time(4, 31), result_1) -# self.assertEqual(datetime.time(4, 31, 59), result_2) -# self.assertEqual(datetime.time(4, 31, 59, 200), result_3) - -# def test_from_native_datetime_time(self): -# """ -# Make sure from_native() accepts a datetime.time instance. -# """ -# f = serializers.TimeField() -# result_1 = f.from_native(datetime.time(4, 31)) -# result_2 = f.from_native(datetime.time(4, 31, 59)) -# result_3 = f.from_native(datetime.time(4, 31, 59, 200)) - -# self.assertEqual(result_1, datetime.time(4, 31)) -# self.assertEqual(result_2, datetime.time(4, 31, 59)) -# self.assertEqual(result_3, datetime.time(4, 31, 59, 200)) - -# def test_from_native_custom_format(self): -# """ -# Make sure from_native() accepts custom input formats. -# """ -# f = serializers.TimeField(input_formats=['%H -- %M']) -# result = f.from_native('04 -- 31') - -# self.assertEqual(datetime.time(4, 31), result) - -# def test_from_native_invalid_default_on_custom_format(self): -# """ -# Make sure from_native() don't accept default formats if custom format is preset -# """ -# f = serializers.TimeField(input_formats=['%H -- %M']) - -# try: -# f.from_native('04:31:59') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Time has wrong format. Use one of these formats instead: hh -- mm"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_from_native_empty(self): -# """ -# Make sure from_native() returns None on empty param. -# """ -# f = serializers.TimeField() -# result = f.from_native('') - -# self.assertEqual(result, None) - -# def test_from_native_none(self): -# """ -# Make sure from_native() returns None on None param. -# """ -# f = serializers.TimeField() -# result = f.from_native(None) - -# self.assertEqual(result, None) - -# def test_from_native_invalid_time(self): -# """ -# Make sure from_native() raises a ValidationError on passing an invalid time. -# """ -# f = serializers.TimeField() - -# try: -# f.from_native('04:61:59') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Time has wrong format. Use one of these formats instead: " -# "hh:mm[:ss[.uuuuuu]]"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_from_native_invalid_format(self): -# """ -# Make sure from_native() raises a ValidationError on passing an invalid format. -# """ -# f = serializers.TimeField() - -# try: -# f.from_native('04 -- 31') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Time has wrong format. Use one of these formats instead: " -# "hh:mm[:ss[.uuuuuu]]"]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_to_native(self): -# """ -# Make sure to_native() returns time object as default. -# """ -# f = serializers.TimeField() -# result_1 = f.to_native(datetime.time(4, 31)) -# result_2 = f.to_native(datetime.time(4, 31, 59)) -# result_3 = f.to_native(datetime.time(4, 31, 59, 200)) - -# self.assertEqual(datetime.time(4, 31), result_1) -# self.assertEqual(datetime.time(4, 31, 59), result_2) -# self.assertEqual(datetime.time(4, 31, 59, 200), result_3) - -# def test_to_native_iso(self): -# """ -# Make sure to_native() with format='iso-8601' returns iso formatted time. -# """ -# f = serializers.TimeField(format='iso-8601') -# result_1 = f.to_native(datetime.time(4, 31)) -# result_2 = f.to_native(datetime.time(4, 31, 59)) -# result_3 = f.to_native(datetime.time(4, 31, 59, 200)) - -# self.assertEqual('04:31:00', result_1) -# self.assertEqual('04:31:59', result_2) -# self.assertEqual('04:31:59.000200', result_3) - -# def test_to_native_custom_format(self): -# """ -# Make sure to_native() returns correct custom format. -# """ -# f = serializers.TimeField(format="%H - %S [%f]") -# result_1 = f.to_native(datetime.time(4, 31)) -# result_2 = f.to_native(datetime.time(4, 31, 59)) -# result_3 = f.to_native(datetime.time(4, 31, 59, 200)) - -# self.assertEqual('04 - 00 [000000]', result_1) -# self.assertEqual('04 - 59 [000000]', result_2) -# self.assertEqual('04 - 59 [000200]', result_3) - - -# class DecimalFieldTest(TestCase): -# """ -# Tests for the DecimalField from_native() and to_native() behavior -# """ - -# def test_from_native_string(self): -# """ -# Make sure from_native() accepts string values -# """ -# f = serializers.DecimalField() -# result_1 = f.from_native('9000') -# result_2 = f.from_native('1.00000001') - -# self.assertEqual(Decimal('9000'), result_1) -# self.assertEqual(Decimal('1.00000001'), result_2) - -# def test_from_native_invalid_string(self): -# """ -# Make sure from_native() raises ValidationError on passing invalid string -# """ -# f = serializers.DecimalField() - -# try: -# f.from_native('123.45.6') -# except validators.ValidationError as e: -# self.assertEqual(e.messages, ["Enter a number."]) -# else: -# self.fail("ValidationError was not properly raised") - -# def test_from_native_integer(self): -# """ -# Make sure from_native() accepts integer values -# """ -# f = serializers.DecimalField() -# result = f.from_native(9000) - -# self.assertEqual(Decimal('9000'), result) - -# def test_from_native_float(self): -# """ -# Make sure from_native() accepts float values -# """ -# f = serializers.DecimalField() -# result = f.from_native(1.00000001) - -# self.assertEqual(Decimal('1.00000001'), result) - -# def test_from_native_empty(self): -# """ -# Make sure from_native() returns None on empty param. -# """ -# f = serializers.DecimalField() -# result = f.from_native('') - -# self.assertEqual(result, None) - -# def test_from_native_none(self): -# """ -# Make sure from_native() returns None on None param. -# """ -# f = serializers.DecimalField() -# result = f.from_native(None) - -# self.assertEqual(result, None) - -# def test_to_native(self): -# """ -# Make sure to_native() returns Decimal as string. -# """ -# f = serializers.DecimalField() - -# result_1 = f.to_native(Decimal('9000')) -# result_2 = f.to_native(Decimal('1.00000001')) - -# self.assertEqual(Decimal('9000'), result_1) -# self.assertEqual(Decimal('1.00000001'), result_2) - -# def test_to_native_none(self): -# """ -# Make sure from_native() returns None on None param. -# """ -# f = serializers.DecimalField(required=False) -# self.assertEqual(None, f.to_native(None)) - -# def test_valid_serialization(self): -# """ -# Make sure the serializer works correctly -# """ -# class DecimalSerializer(serializers.Serializer): -# decimal_field = serializers.DecimalField(max_value=9010, -# min_value=9000, -# max_digits=6, -# decimal_places=2) - -# self.assertTrue(DecimalSerializer(data={'decimal_field': '9001'}).is_valid()) -# self.assertTrue(DecimalSerializer(data={'decimal_field': '9001.2'}).is_valid()) -# self.assertTrue(DecimalSerializer(data={'decimal_field': '9001.23'}).is_valid()) - -# self.assertFalse(DecimalSerializer(data={'decimal_field': '8000'}).is_valid()) -# self.assertFalse(DecimalSerializer(data={'decimal_field': '9900'}).is_valid()) -# self.assertFalse(DecimalSerializer(data={'decimal_field': '9001.234'}).is_valid()) - -# def test_raise_max_value(self): -# """ -# Make sure max_value violations raises ValidationError -# """ -# class DecimalSerializer(serializers.Serializer): -# decimal_field = serializers.DecimalField(max_value=100) - -# s = DecimalSerializer(data={'decimal_field': '123'}) - -# self.assertFalse(s.is_valid()) -# self.assertEqual(s.errors, {'decimal_field': ['Ensure this value is less than or equal to 100.']}) - -# def test_raise_min_value(self): -# """ -# Make sure min_value violations raises ValidationError -# """ -# class DecimalSerializer(serializers.Serializer): -# decimal_field = serializers.DecimalField(min_value=100) - -# s = DecimalSerializer(data={'decimal_field': '99'}) - -# self.assertFalse(s.is_valid()) -# self.assertEqual(s.errors, {'decimal_field': ['Ensure this value is greater than or equal to 100.']}) - -# def test_raise_max_digits(self): -# """ -# Make sure max_digits violations raises ValidationError -# """ -# class DecimalSerializer(serializers.Serializer): -# decimal_field = serializers.DecimalField(max_digits=5) - -# s = DecimalSerializer(data={'decimal_field': '123.456'}) - -# self.assertFalse(s.is_valid()) -# self.assertEqual(s.errors, {'decimal_field': ['Ensure that there are no more than 5 digits in total.']}) - -# def test_raise_max_decimal_places(self): -# """ -# Make sure max_decimal_places violations raises ValidationError -# """ -# class DecimalSerializer(serializers.Serializer): -# decimal_field = serializers.DecimalField(decimal_places=3) - -# s = DecimalSerializer(data={'decimal_field': '123.4567'}) - -# self.assertFalse(s.is_valid()) -# self.assertEqual(s.errors, {'decimal_field': ['Ensure that there are no more than 3 decimal places.']}) - -# def test_raise_max_whole_digits(self): -# """ -# Make sure max_whole_digits violations raises ValidationError -# """ -# class DecimalSerializer(serializers.Serializer): -# decimal_field = serializers.DecimalField(max_digits=4, decimal_places=3) - -# s = DecimalSerializer(data={'decimal_field': '12345.6'}) - -# self.assertFalse(s.is_valid()) -# self.assertEqual(s.errors, {'decimal_field': ['Ensure that there are no more than 4 digits in total.']}) - - -# class ChoiceFieldTests(TestCase): -# """ -# Tests for the ChoiceField options generator -# """ -# def test_choices_required(self): -# """ -# Make sure proper choices are rendered if field is required -# """ -# f = serializers.ChoiceField(required=True, choices=SAMPLE_CHOICES) -# self.assertEqual(f.choices, SAMPLE_CHOICES) - -# def test_choices_not_required(self): -# """ -# Make sure proper choices (plus blank) are rendered if the field isn't required -# """ -# f = serializers.ChoiceField(required=False, choices=SAMPLE_CHOICES) -# self.assertEqual(f.choices, models.fields.BLANK_CHOICE_DASH + SAMPLE_CHOICES) - -# def test_blank_choice_display(self): -# blank = 'No Preference' -# f = serializers.ChoiceField( -# required=False, -# choices=SAMPLE_CHOICES, -# blank_display_value=blank, -# ) -# self.assertEqual(f.choices, [('', blank)] + SAMPLE_CHOICES) - -# def test_invalid_choice_model(self): -# s = ChoiceFieldModelSerializer(data={'choice': 'wrong_value'}) -# self.assertFalse(s.is_valid()) -# self.assertEqual(s.errors, {'choice': ['Select a valid choice. wrong_value is not one of the available choices.']}) -# self.assertEqual(s.data['choice'], '') - -# def test_empty_choice_model(self): -# """ -# Test that the 'empty' value is correctly passed and used depending on -# the 'null' property on the model field. -# """ -# s = ChoiceFieldModelSerializer(data={'choice': ''}) -# self.assertTrue(s.is_valid()) -# self.assertEqual(s.data['choice'], '') - -# s = ChoiceFieldModelWithNullSerializer(data={'choice': ''}) -# self.assertTrue(s.is_valid()) -# self.assertEqual(s.data['choice'], None) - -# def test_from_native_empty(self): -# """ -# Make sure from_native() returns an empty string on empty param by default. -# """ -# f = serializers.ChoiceField(choices=SAMPLE_CHOICES) -# self.assertEqual(f.from_native(''), '') -# self.assertEqual(f.from_native(None), '') - -# def test_from_native_empty_override(self): -# """ -# Make sure you can override from_native() behavior regarding empty values. -# """ -# f = serializers.ChoiceField(choices=SAMPLE_CHOICES, empty=None) -# self.assertEqual(f.from_native(''), None) -# self.assertEqual(f.from_native(None), None) - -# def test_metadata_choices(self): -# """ -# Make sure proper choices are included in the field's metadata. -# """ -# choices = [{'value': v, 'display_name': n} for v, n in SAMPLE_CHOICES] -# f = serializers.ChoiceField(choices=SAMPLE_CHOICES) -# self.assertEqual(f.metadata()['choices'], choices) - -# def test_metadata_choices_not_required(self): -# """ -# Make sure proper choices are included in the field's metadata. -# """ -# choices = [{'value': v, 'display_name': n} -# for v, n in models.fields.BLANK_CHOICE_DASH + SAMPLE_CHOICES] -# f = serializers.ChoiceField(required=False, choices=SAMPLE_CHOICES) -# self.assertEqual(f.metadata()['choices'], choices) - - -# class EmailFieldTests(TestCase): -# """ -# Tests for EmailField attribute values -# """ - -# class EmailFieldModel(RESTFrameworkModel): -# email_field = models.EmailField(blank=True) - -# class EmailFieldWithGivenMaxLengthModel(RESTFrameworkModel): -# email_field = models.EmailField(max_length=150, blank=True) - -# def test_default_model_value(self): -# class EmailFieldSerializer(serializers.ModelSerializer): -# class Meta: -# model = self.EmailFieldModel - -# serializer = EmailFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['email_field'], 'max_length'), 75) - -# def test_given_model_value(self): -# class EmailFieldSerializer(serializers.ModelSerializer): -# class Meta: -# model = self.EmailFieldWithGivenMaxLengthModel - -# serializer = EmailFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['email_field'], 'max_length'), 150) - -# def test_given_serializer_value(self): -# class EmailFieldSerializer(serializers.ModelSerializer): -# email_field = serializers.EmailField(source='email_field', max_length=20, required=False) - -# class Meta: -# model = self.EmailFieldModel - -# serializer = EmailFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['email_field'], 'max_length'), 20) - - -# class SlugFieldTests(TestCase): -# """ -# Tests for SlugField attribute values -# """ - -# class SlugFieldModel(RESTFrameworkModel): -# slug_field = models.SlugField(blank=True) - -# class SlugFieldWithGivenMaxLengthModel(RESTFrameworkModel): -# slug_field = models.SlugField(max_length=84, blank=True) - -# def test_default_model_value(self): -# class SlugFieldSerializer(serializers.ModelSerializer): -# class Meta: -# model = self.SlugFieldModel - -# serializer = SlugFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['slug_field'], 'max_length'), 50) - -# def test_given_model_value(self): -# class SlugFieldSerializer(serializers.ModelSerializer): -# class Meta: -# model = self.SlugFieldWithGivenMaxLengthModel - -# serializer = SlugFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['slug_field'], 'max_length'), 84) - -# def test_given_serializer_value(self): -# class SlugFieldSerializer(serializers.ModelSerializer): -# slug_field = serializers.SlugField(source='slug_field', -# max_length=20, required=False) - -# class Meta: -# model = self.SlugFieldModel - -# serializer = SlugFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['slug_field'], -# 'max_length'), 20) - -# def test_invalid_slug(self): -# """ -# Make sure an invalid slug raises ValidationError -# """ -# class SlugFieldSerializer(serializers.ModelSerializer): -# slug_field = serializers.SlugField(source='slug_field', max_length=20, required=True) - -# class Meta: -# model = self.SlugFieldModel - -# s = SlugFieldSerializer(data={'slug_field': 'a b'}) - -# self.assertEqual(s.is_valid(), False) -# self.assertEqual(s.errors, {'slug_field': ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."]}) - - -# class URLFieldTests(TestCase): -# """ -# Tests for URLField attribute values. - -# (Includes test for #1210, checking that validators can be overridden.) -# """ - -# class URLFieldModel(RESTFrameworkModel): -# url_field = models.URLField(blank=True) - -# class URLFieldWithGivenMaxLengthModel(RESTFrameworkModel): -# url_field = models.URLField(max_length=128, blank=True) - -# def test_default_model_value(self): -# class URLFieldSerializer(serializers.ModelSerializer): -# class Meta: -# model = self.URLFieldModel - -# serializer = URLFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['url_field'], -# 'max_length'), 200) - -# def test_given_model_value(self): -# class URLFieldSerializer(serializers.ModelSerializer): -# class Meta: -# model = self.URLFieldWithGivenMaxLengthModel - -# serializer = URLFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['url_field'], -# 'max_length'), 128) - -# def test_given_serializer_value(self): -# class URLFieldSerializer(serializers.ModelSerializer): -# url_field = serializers.URLField(source='url_field', -# max_length=20, required=False) - -# class Meta: -# model = self.URLFieldWithGivenMaxLengthModel - -# serializer = URLFieldSerializer(data={}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(getattr(serializer.fields['url_field'], -# 'max_length'), 20) - -# def test_validators_can_be_overridden(self): -# url_field = serializers.URLField(validators=[]) -# validators = url_field.validators -# self.assertEqual([], validators, 'Passing `validators` kwarg should have overridden default validators') - - -# class FieldMetadata(TestCase): -# def setUp(self): -# self.required_field = serializers.Field() -# self.required_field.label = uuid4().hex -# self.required_field.required = True - -# self.optional_field = serializers.Field() -# self.optional_field.label = uuid4().hex -# self.optional_field.required = False - -# def test_required(self): -# self.assertEqual(self.required_field.metadata()['required'], True) - -# def test_optional(self): -# self.assertEqual(self.optional_field.metadata()['required'], False) - -# def test_label(self): -# for field in (self.required_field, self.optional_field): -# self.assertEqual(field.metadata()['label'], field.label) - - -# class FieldCallableDefault(TestCase): -# def setUp(self): -# self.simple_callable = lambda: 'foo bar' - -# def test_default_can_be_simple_callable(self): -# """ -# Ensure that the 'default' argument can also be a simple callable. -# """ -# field = serializers.WritableField(default=self.simple_callable) -# into = {} -# field.field_from_native({}, {}, 'field', into) -# self.assertEqual(into, {'field': 'foo bar'}) - - -# class CustomIntegerField(TestCase): -# """ -# Test that custom fields apply min_value and max_value constraints -# """ -# def test_custom_fields_can_be_validated_for_value(self): - -# class MoneyField(models.PositiveIntegerField): -# pass - -# class EntryModel(models.Model): -# bank = MoneyField(validators=[validators.MaxValueValidator(100)]) - -# class EntrySerializer(serializers.ModelSerializer): -# class Meta: -# model = EntryModel - -# entry = EntryModel(bank=1) - -# serializer = EntrySerializer(entry, data={"bank": 11}) -# self.assertTrue(serializer.is_valid()) - -# serializer = EntrySerializer(entry, data={"bank": -1}) -# self.assertFalse(serializer.is_valid()) - -# serializer = EntrySerializer(entry, data={"bank": 101}) -# self.assertFalse(serializer.is_valid()) - - -# class BooleanField(TestCase): -# """ -# Tests for BooleanField -# """ -# def test_boolean_required(self): -# class BooleanRequiredSerializer(serializers.Serializer): -# bool_field = serializers.BooleanField(required=True) - -# self.assertFalse(BooleanRequiredSerializer(data={}).is_valid()) - - -# class SerializerMethodFieldTest(TestCase): -# """ -# Tests for the SerializerMethodField field_to_native() behavior -# """ -# class SerializerTest(serializers.Serializer): -# def get_my_test(self, obj): -# return obj.my_test[0:5] - -# class ModelCharField(TestCase): -# """ -# Tests for CharField -# """ -# def test_none_serializing(self): -# class CharFieldSerializer(serializers.Serializer): -# char = serializers.CharField(allow_none=True, required=False) -# serializer = CharFieldSerializer(data={'char': None}) -# self.assertTrue(serializer.is_valid()) -# self.assertIsNone(serializer.object['char']) - - -# class SerializerMethodFieldTest(TestCase): -# """ -# Tests for the SerializerMethodField field_to_native() behavior -# """ -# class SerializerTest(serializers.Serializer): -# def get_my_test(self, obj): -# return obj.my_test[0:5] - -# class Example(): -# my_test = 'Hey, this is a test !' - -# def test_field_to_native(self): -# s = serializers.SerializerMethodField('get_my_test') -# s.initialize(self.SerializerTest(), 'name') -# result = s.field_to_native(self.Example(), None) -# self.assertEqual(result, 'Hey, ') +from decimal import Decimal +from django.utils import timezone +from rest_framework import serializers +import datetime +import django +import pytest + + +# Tests for field keyword arguments and core functionality. +# --------------------------------------------------------- + +class TestEmpty: + """ + Tests for `required`, `allow_null`, `allow_blank`, `default`. + """ + def test_required(self): + """ + By default a field must be included in the input. + """ + field = serializers.IntegerField() + with pytest.raises(serializers.ValidationError) as exc_info: + field.run_validation() + assert exc_info.value.detail == ['This field is required.'] + + def test_not_required(self): + """ + If `required=False` then a field may be omitted from the input. + """ + field = serializers.IntegerField(required=False) + with pytest.raises(serializers.SkipField): + field.run_validation() + + def test_disallow_null(self): + """ + By default `None` is not a valid input. + """ + field = serializers.IntegerField() + with pytest.raises(serializers.ValidationError) as exc_info: + field.run_validation(None) + assert exc_info.value.detail == ['This field may not be null.'] + + def test_allow_null(self): + """ + If `allow_null=True` then `None` is a valid input. + """ + field = serializers.IntegerField(allow_null=True) + output = field.run_validation(None) + assert output is None + + def test_disallow_blank(self): + """ + By default '' is not a valid input. + """ + field = serializers.CharField() + with pytest.raises(serializers.ValidationError) as exc_info: + field.run_validation('') + assert exc_info.value.detail == ['This field may not be blank.'] + + def test_allow_blank(self): + """ + If `allow_blank=True` then '' is a valid input. + """ + field = serializers.CharField(allow_blank=True) + output = field.run_validation('') + assert output is '' + + def test_default(self): + """ + If `default` is set, then omitted values get the default input. + """ + field = serializers.IntegerField(default=123) + output = field.run_validation() + assert output is 123 + + +class TestSource: + def test_source(self): + class ExampleSerializer(serializers.Serializer): + example_field = serializers.CharField(source='other') + serializer = ExampleSerializer(data={'example_field': 'abc'}) + assert serializer.is_valid() + assert serializer.validated_data == {'other': 'abc'} + + def test_redundant_source(self): + class ExampleSerializer(serializers.Serializer): + example_field = serializers.CharField(source='example_field') + with pytest.raises(AssertionError) as exc_info: + ExampleSerializer().fields + assert str(exc_info.value) == ( + "It is redundant to specify `source='example_field'` on field " + "'CharField' in serializer 'ExampleSerializer', because it is the " + "same as the field name. Remove the `source` keyword argument." + ) + + +class TestReadOnly: + def setup(self): + class TestSerializer(serializers.Serializer): + read_only = serializers.ReadOnlyField() + writable = serializers.IntegerField() + self.Serializer = TestSerializer + + def test_validate_read_only(self): + """ + Read-only serializers.should not be included in validation. + """ + data = {'read_only': 123, 'writable': 456} + serializer = self.Serializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == {'writable': 456} + + def test_serialize_read_only(self): + """ + Read-only serializers.should be serialized. + """ + instance = {'read_only': 123, 'writable': 456} + serializer = self.Serializer(instance) + assert serializer.data == {'read_only': 123, 'writable': 456} + + +class TestWriteOnly: + def setup(self): + class TestSerializer(serializers.Serializer): + write_only = serializers.IntegerField(write_only=True) + readable = serializers.IntegerField() + self.Serializer = TestSerializer + + def test_validate_write_only(self): + """ + Write-only serializers.should be included in validation. + """ + data = {'write_only': 123, 'readable': 456} + serializer = self.Serializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == {'write_only': 123, 'readable': 456} + + def test_serialize_write_only(self): + """ + Write-only serializers.should not be serialized. + """ + instance = {'write_only': 123, 'readable': 456} + serializer = self.Serializer(instance) + assert serializer.data == {'readable': 456} + + +class TestInitial: + def setup(self): + class TestSerializer(serializers.Serializer): + initial_field = serializers.IntegerField(initial=123) + blank_field = serializers.IntegerField() + self.serializer = TestSerializer() + + def test_initial(self): + """ + Initial values should be included when serializing a new representation. + """ + assert self.serializer.data == { + 'initial_field': 123, + 'blank_field': None + } + + +class TestLabel: + def setup(self): + class TestSerializer(serializers.Serializer): + labeled = serializers.IntegerField(label='My label') + self.serializer = TestSerializer() + + def test_label(self): + """ + A field's label may be set with the `label` argument. + """ + fields = self.serializer.fields + assert fields['labeled'].label == 'My label' + + +class TestInvalidErrorKey: + def setup(self): + class ExampleField(serializers.Field): + def to_native(self, data): + self.fail('incorrect') + self.field = ExampleField() + + def test_invalid_error_key(self): + """ + If a field raises a validation error, but does not have a corresponding + error message, then raise an appropriate assertion error. + """ + with pytest.raises(AssertionError) as exc_info: + self.field.to_native(123) + expected = ( + 'ValidationError raised by `ExampleField`, but error key ' + '`incorrect` does not exist in the `error_messages` dictionary.' + ) + assert str(exc_info.value) == expected + + +class TestBooleanHTMLInput: + def setup(self): + class TestSerializer(serializers.Serializer): + archived = serializers.BooleanField() + self.Serializer = TestSerializer + + def test_empty_html_checkbox(self): + """ + HTML checkboxes do not send any value, but should be treated + as `False` by BooleanField. + """ + # This class mocks up a dictionary like object, that behaves + # as if it was returned for multipart or urlencoded data. + class MockHTMLDict(dict): + getlist = None + serializer = self.Serializer(data=MockHTMLDict()) + assert serializer.is_valid() + assert serializer.validated_data == {'archived': False} + + +class TestCreateOnlyDefault: + def setup(self): + default = serializers.CreateOnlyDefault('2001-01-01') + + class TestSerializer(serializers.Serializer): + published = serializers.HiddenField(default=default) + text = serializers.CharField() + self.Serializer = TestSerializer + + def test_create_only_default_is_provided(self): + serializer = self.Serializer(data={'text': 'example'}) + assert serializer.is_valid() + assert serializer.validated_data == { + 'text': 'example', 'published': '2001-01-01' + } + + def test_create_only_default_is_not_provided_on_update(self): + instance = { + 'text': 'example', 'published': '2001-01-01' + } + serializer = self.Serializer(instance, data={'text': 'example'}) + assert serializer.is_valid() + assert serializer.validated_data == { + 'text': 'example', + } + + +# Tests for field input and output values. +# ---------------------------------------- + +def get_items(mapping_or_list_of_two_tuples): + # Tests accept either lists of two tuples, or dictionaries. + if isinstance(mapping_or_list_of_two_tuples, dict): + # {value: expected} + return mapping_or_list_of_two_tuples.items() + # [(value, expected), ...] + return mapping_or_list_of_two_tuples + + +class FieldValues: + """ + Base class for testing valid and invalid input values. + """ + def test_valid_inputs(self): + """ + Ensure that valid values return the expected validated data. + """ + for input_value, expected_output in get_items(self.valid_inputs): + assert self.field.run_validation(input_value) == expected_output + + def test_invalid_inputs(self): + """ + Ensure that invalid values raise the expected validation error. + """ + for input_value, expected_failure in get_items(self.invalid_inputs): + with pytest.raises(serializers.ValidationError) as exc_info: + self.field.run_validation(input_value) + assert exc_info.value.detail == expected_failure + + def test_outputs(self): + for output_value, expected_output in get_items(self.outputs): + assert self.field.to_representation(output_value) == expected_output + + +# Boolean types... + +class TestBooleanField(FieldValues): + """ + Valid and invalid values for `BooleanField`. + """ + valid_inputs = { + 'true': True, + 'false': False, + '1': True, + '0': False, + 1: True, + 0: False, + True: True, + False: False, + } + invalid_inputs = { + 'foo': ['`foo` is not a valid boolean.'], + None: ['This field may not be null.'] + } + outputs = { + 'true': True, + 'false': False, + '1': True, + '0': False, + 1: True, + 0: False, + True: True, + False: False, + 'other': True + } + field = serializers.BooleanField() + + +class TestNullBooleanField(FieldValues): + """ + Valid and invalid values for `BooleanField`. + """ + valid_inputs = { + 'true': True, + 'false': False, + 'null': None, + True: True, + False: False, + None: None + } + invalid_inputs = { + 'foo': ['`foo` is not a valid boolean.'], + } + outputs = { + 'true': True, + 'false': False, + 'null': None, + True: True, + False: False, + None: None, + 'other': True + } + field = serializers.NullBooleanField() + + +# String types... + +class TestCharField(FieldValues): + """ + Valid and invalid values for `CharField`. + """ + valid_inputs = { + 1: '1', + 'abc': 'abc' + } + invalid_inputs = { + '': ['This field may not be blank.'] + } + outputs = { + 1: '1', + 'abc': 'abc' + } + field = serializers.CharField() + + +class TestEmailField(FieldValues): + """ + Valid and invalid values for `EmailField`. + """ + valid_inputs = { + 'example@example.com': 'example@example.com', + ' example@example.com ': 'example@example.com', + } + invalid_inputs = { + 'examplecom': ['Enter a valid email address.'] + } + outputs = {} + field = serializers.EmailField() + + +class TestRegexField(FieldValues): + """ + Valid and invalid values for `RegexField`. + """ + valid_inputs = { + 'a9': 'a9', + } + invalid_inputs = { + 'A9': ["This value does not match the required pattern."] + } + outputs = {} + field = serializers.RegexField(regex='[a-z][0-9]') + + +class TestSlugField(FieldValues): + """ + Valid and invalid values for `SlugField`. + """ + valid_inputs = { + 'slug-99': 'slug-99', + } + invalid_inputs = { + 'slug 99': ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."] + } + outputs = {} + field = serializers.SlugField() + + +class TestURLField(FieldValues): + """ + Valid and invalid values for `URLField`. + """ + valid_inputs = { + 'http://example.com': 'http://example.com', + } + invalid_inputs = { + 'example.com': ['Enter a valid URL.'] + } + outputs = {} + field = serializers.URLField() + + +# Number types... + +class TestIntegerField(FieldValues): + """ + Valid and invalid values for `IntegerField`. + """ + valid_inputs = { + '1': 1, + '0': 0, + 1: 1, + 0: 0, + 1.0: 1, + 0.0: 0 + } + invalid_inputs = { + 'abc': ['A valid integer is required.'] + } + outputs = { + '1': 1, + '0': 0, + 1: 1, + 0: 0, + 1.0: 1, + 0.0: 0 + } + field = serializers.IntegerField() + + +class TestMinMaxIntegerField(FieldValues): + """ + Valid and invalid values for `IntegerField` with min and max limits. + """ + valid_inputs = { + '1': 1, + '3': 3, + 1: 1, + 3: 3, + } + invalid_inputs = { + 0: ['Ensure this value is greater than or equal to 1.'], + 4: ['Ensure this value is less than or equal to 3.'], + '0': ['Ensure this value is greater than or equal to 1.'], + '4': ['Ensure this value is less than or equal to 3.'], + } + outputs = {} + field = serializers.IntegerField(min_value=1, max_value=3) + + +class TestFloatField(FieldValues): + """ + Valid and invalid values for `FloatField`. + """ + valid_inputs = { + '1': 1.0, + '0': 0.0, + 1: 1.0, + 0: 0.0, + 1.0: 1.0, + 0.0: 0.0, + } + invalid_inputs = { + 'abc': ["A valid number is required."] + } + outputs = { + '1': 1.0, + '0': 0.0, + 1: 1.0, + 0: 0.0, + 1.0: 1.0, + 0.0: 0.0, + } + field = serializers.FloatField() + + +class TestMinMaxFloatField(FieldValues): + """ + Valid and invalid values for `FloatField` with min and max limits. + """ + valid_inputs = { + '1': 1, + '3': 3, + 1: 1, + 3: 3, + 1.0: 1.0, + 3.0: 3.0, + } + invalid_inputs = { + 0.9: ['Ensure this value is greater than or equal to 1.'], + 3.1: ['Ensure this value is less than or equal to 3.'], + '0.0': ['Ensure this value is greater than or equal to 1.'], + '3.1': ['Ensure this value is less than or equal to 3.'], + } + outputs = {} + field = serializers.FloatField(min_value=1, max_value=3) + + +class TestDecimalField(FieldValues): + """ + Valid and invalid values for `DecimalField`. + """ + valid_inputs = { + '12.3': Decimal('12.3'), + '0.1': Decimal('0.1'), + 10: Decimal('10'), + 0: Decimal('0'), + 12.3: Decimal('12.3'), + 0.1: Decimal('0.1'), + } + invalid_inputs = ( + ('abc', ["A valid number is required."]), + (Decimal('Nan'), ["A valid number is required."]), + (Decimal('Inf'), ["A valid number is required."]), + ('12.345', ["Ensure that there are no more than 3 digits in total."]), + ('0.01', ["Ensure that there are no more than 1 decimal places."]), + (123, ["Ensure that there are no more than 2 digits before the decimal point."]) + ) + outputs = { + '1': '1.0', + '0': '0.0', + '1.09': '1.1', + '0.04': '0.0', + 1: '1.0', + 0: '0.0', + Decimal('1.0'): '1.0', + Decimal('0.0'): '0.0', + Decimal('1.09'): '1.1', + Decimal('0.04'): '0.0' + } + field = serializers.DecimalField(max_digits=3, decimal_places=1) + + +class TestMinMaxDecimalField(FieldValues): + """ + Valid and invalid values for `DecimalField` with min and max limits. + """ + valid_inputs = { + '10.0': Decimal('10.0'), + '20.0': Decimal('20.0'), + } + invalid_inputs = { + '9.9': ['Ensure this value is greater than or equal to 10.'], + '20.1': ['Ensure this value is less than or equal to 20.'], + } + outputs = {} + field = serializers.DecimalField( + max_digits=3, decimal_places=1, + min_value=10, max_value=20 + ) + + +class TestNoStringCoercionDecimalField(FieldValues): + """ + Output values for `DecimalField` with `coerce_to_string=False`. + """ + valid_inputs = {} + invalid_inputs = {} + outputs = { + 1.09: Decimal('1.1'), + 0.04: Decimal('0.0'), + '1.09': Decimal('1.1'), + '0.04': Decimal('0.0'), + Decimal('1.09'): Decimal('1.1'), + Decimal('0.04'): Decimal('0.0'), + } + field = serializers.DecimalField( + max_digits=3, decimal_places=1, + coerce_to_string=False + ) + + +# Date & time serializers... + +class TestDateField(FieldValues): + """ + Valid and invalid values for `DateField`. + """ + valid_inputs = { + '2001-01-01': datetime.date(2001, 1, 1), + datetime.date(2001, 1, 1): datetime.date(2001, 1, 1), + } + invalid_inputs = { + 'abc': ['Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]'], + '2001-99-99': ['Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]'], + datetime.datetime(2001, 1, 1, 12, 00): ['Expected a date but got a datetime.'], + } + outputs = { + datetime.date(2001, 1, 1): '2001-01-01' + } + field = serializers.DateField() + + +class TestCustomInputFormatDateField(FieldValues): + """ + Valid and invalid values for `DateField` with a cutom input format. + """ + valid_inputs = { + '1 Jan 2001': datetime.date(2001, 1, 1), + } + invalid_inputs = { + '2001-01-01': ['Date has wrong format. Use one of these formats instead: DD [Jan-Dec] YYYY'] + } + outputs = {} + field = serializers.DateField(input_formats=['%d %b %Y']) + + +class TestCustomOutputFormatDateField(FieldValues): + """ + Values for `DateField` with a custom output format. + """ + valid_inputs = {} + invalid_inputs = {} + outputs = { + datetime.date(2001, 1, 1): '01 Jan 2001' + } + field = serializers.DateField(format='%d %b %Y') + + +class TestNoOutputFormatDateField(FieldValues): + """ + Values for `DateField` with no output format. + """ + valid_inputs = {} + invalid_inputs = {} + outputs = { + datetime.date(2001, 1, 1): datetime.date(2001, 1, 1) + } + field = serializers.DateField(format=None) + + +class TestDateTimeField(FieldValues): + """ + Valid and invalid values for `DateTimeField`. + """ + valid_inputs = { + '2001-01-01 13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()), + '2001-01-01T13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()), + '2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()), + datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()), + datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()), + # Django 1.4 does not support timezone string parsing. + '2001-01-01T14:00+01:00' if (django.VERSION > (1, 4)) else '2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()) + } + invalid_inputs = { + 'abc': ['Datetime has wrong format. Use one of these formats instead: YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]'], + '2001-99-99T99:00': ['Datetime has wrong format. Use one of these formats instead: YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]'], + datetime.date(2001, 1, 1): ['Expected a datetime but got a date.'], + } + outputs = { + datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00', + datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): '2001-01-01T13:00:00Z' + } + field = serializers.DateTimeField(default_timezone=timezone.UTC()) + + +class TestCustomInputFormatDateTimeField(FieldValues): + """ + Valid and invalid values for `DateTimeField` with a cutom input format. + """ + valid_inputs = { + '1:35pm, 1 Jan 2001': datetime.datetime(2001, 1, 1, 13, 35, tzinfo=timezone.UTC()), + } + invalid_inputs = { + '2001-01-01T20:50': ['Datetime has wrong format. Use one of these formats instead: hh:mm[AM|PM], DD [Jan-Dec] YYYY'] + } + outputs = {} + field = serializers.DateTimeField(default_timezone=timezone.UTC(), input_formats=['%I:%M%p, %d %b %Y']) + + +class TestCustomOutputFormatDateTimeField(FieldValues): + """ + Values for `DateTimeField` with a custom output format. + """ + valid_inputs = {} + invalid_inputs = {} + outputs = { + datetime.datetime(2001, 1, 1, 13, 00): '01:00PM, 01 Jan 2001', + } + field = serializers.DateTimeField(format='%I:%M%p, %d %b %Y') + + +class TestNoOutputFormatDateTimeField(FieldValues): + """ + Values for `DateTimeField` with no output format. + """ + valid_inputs = {} + invalid_inputs = {} + outputs = { + datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00), + } + field = serializers.DateTimeField(format=None) + + +class TestNaiveDateTimeField(FieldValues): + """ + Valid and invalid values for `DateTimeField` with naive datetimes. + """ + valid_inputs = { + datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00), + '2001-01-01 13:00': datetime.datetime(2001, 1, 1, 13, 00), + } + invalid_inputs = {} + outputs = {} + field = serializers.DateTimeField(default_timezone=None) + + +class TestTimeField(FieldValues): + """ + Valid and invalid values for `TimeField`. + """ + valid_inputs = { + '13:00': datetime.time(13, 00), + datetime.time(13, 00): datetime.time(13, 00), + } + invalid_inputs = { + 'abc': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]]'], + '99:99': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]]'], + } + outputs = { + datetime.time(13, 00): '13:00:00' + } + field = serializers.TimeField() + + +class TestCustomInputFormatTimeField(FieldValues): + """ + Valid and invalid values for `TimeField` with a custom input format. + """ + valid_inputs = { + '1:00pm': datetime.time(13, 00), + } + invalid_inputs = { + '13:00': ['Time has wrong format. Use one of these formats instead: hh:mm[AM|PM]'], + } + outputs = {} + field = serializers.TimeField(input_formats=['%I:%M%p']) + + +class TestCustomOutputFormatTimeField(FieldValues): + """ + Values for `TimeField` with a custom output format. + """ + valid_inputs = {} + invalid_inputs = {} + outputs = { + datetime.time(13, 00): '01:00PM' + } + field = serializers.TimeField(format='%I:%M%p') + + +class TestNoOutputFormatTimeField(FieldValues): + """ + Values for `TimeField` with a no output format. + """ + valid_inputs = {} + invalid_inputs = {} + outputs = { + datetime.time(13, 00): datetime.time(13, 00) + } + field = serializers.TimeField(format=None) + + +# Choice types... + +class TestChoiceField(FieldValues): + """ + Valid and invalid values for `ChoiceField`. + """ + valid_inputs = { + 'poor': 'poor', + 'medium': 'medium', + 'good': 'good', + } + invalid_inputs = { + 'amazing': ['`amazing` is not a valid choice.'] + } + outputs = { + 'good': 'good', + '': '' + } + field = serializers.ChoiceField( + choices=[ + ('poor', 'Poor quality'), + ('medium', 'Medium quality'), + ('good', 'Good quality'), + ] + ) + + +class TestChoiceFieldWithType(FieldValues): + """ + Valid and invalid values for a `Choice` field that uses an integer type, + instead of a char type. + """ + valid_inputs = { + '1': 1, + 3: 3, + } + invalid_inputs = { + 5: ['`5` is not a valid choice.'], + 'abc': ['`abc` is not a valid choice.'] + } + outputs = { + '1': 1, + 1: 1 + } + field = serializers.ChoiceField( + choices=[ + (1, 'Poor quality'), + (2, 'Medium quality'), + (3, 'Good quality'), + ] + ) + + +class TestChoiceFieldWithListChoices(FieldValues): + """ + Valid and invalid values for a `Choice` field that uses a flat list for the + choices, rather than a list of pairs of (`value`, `description`). + """ + valid_inputs = { + 'poor': 'poor', + 'medium': 'medium', + 'good': 'good', + } + invalid_inputs = { + 'awful': ['`awful` is not a valid choice.'] + } + outputs = { + 'good': 'good' + } + field = serializers.ChoiceField(choices=('poor', 'medium', 'good')) + + +class TestMultipleChoiceField(FieldValues): + """ + Valid and invalid values for `MultipleChoiceField`. + """ + valid_inputs = { + (): set(), + ('aircon',): set(['aircon']), + ('aircon', 'manual'): set(['aircon', 'manual']), + } + invalid_inputs = { + 'abc': ['Expected a list of items but got type `str`.'], + ('aircon', 'incorrect'): ['`incorrect` is not a valid choice.'] + } + outputs = [ + (['aircon', 'manual'], set(['aircon', 'manual'])) + ] + field = serializers.MultipleChoiceField( + choices=[ + ('aircon', 'AirCon'), + ('manual', 'Manual drive'), + ('diesel', 'Diesel'), + ] + ) + + +# File serializers... + +class MockFile: + def __init__(self, name='', size=0, url=''): + self.name = name + self.size = size + self.url = url + + def __eq__(self, other): + return ( + isinstance(other, MockFile) and + self.name == other.name and + self.size == other.size and + self.url == other.url + ) + + +class TestFileField(FieldValues): + """ + Values for `FileField`. + """ + valid_inputs = [ + (MockFile(name='example', size=10), MockFile(name='example', size=10)) + ] + invalid_inputs = [ + ('invalid', ['The submitted data was not a file. Check the encoding type on the form.']), + (MockFile(name='example.txt', size=0), ['The submitted file is empty.']), + (MockFile(name='', size=10), ['No filename could be determined.']), + (MockFile(name='x' * 100, size=10), ['Ensure this filename has at most 10 characters (it has 100).']) + ] + outputs = [ + (MockFile(name='example.txt', url='/example.txt'), '/example.txt'), + ('', None) + ] + field = serializers.FileField(max_length=10) + + +class TestFieldFieldWithName(FieldValues): + """ + Values for `FileField` with a filename output instead of URLs. + """ + valid_inputs = {} + invalid_inputs = {} + outputs = [ + (MockFile(name='example.txt', url='/example.txt'), 'example.txt') + ] + field = serializers.FileField(use_url=False) + + +# Stub out mock Django `forms.ImageField` class so we don't *actually* +# call into it's regular validation, or require PIL for testing. +class FailImageValidation(object): + def to_python(self, value): + raise serializers.ValidationError(self.error_messages['invalid_image']) + + +class PassImageValidation(object): + def to_python(self, value): + return value + + +class TestInvalidImageField(FieldValues): + """ + Values for an invalid `ImageField`. + """ + valid_inputs = {} + invalid_inputs = [ + (MockFile(name='example.txt', size=10), ['Upload a valid image. The file you uploaded was either not an image or a corrupted image.']) + ] + outputs = {} + field = serializers.ImageField(_DjangoImageField=FailImageValidation) + + +class TestValidImageField(FieldValues): + """ + Values for an valid `ImageField`. + """ + valid_inputs = [ + (MockFile(name='example.txt', size=10), MockFile(name='example.txt', size=10)) + ] + invalid_inputs = {} + outputs = {} + field = serializers.ImageField(_DjangoImageField=PassImageValidation) + + +# Composite serializers... + +class TestListField(FieldValues): + """ + Values for `ListField`. + """ + valid_inputs = [ + ([1, 2, 3], [1, 2, 3]), + (['1', '2', '3'], [1, 2, 3]) + ] + invalid_inputs = [ + ('not a list', ['Expected a list of items but got type `str`']), + ([1, 2, 'error'], ['A valid integer is required.']) + ] + outputs = [ + ([1, 2, 3], [1, 2, 3]), + (['1', '2', '3'], [1, 2, 3]) + ] + field = serializers.ListField(child=serializers.IntegerField()) + + +# Tests for FieldField. +# --------------------- + +class MockRequest: + def build_absolute_uri(self, value): + return 'http://example.com' + value + + +class TestFileFieldContext: + def test_fully_qualified_when_request_in_context(self): + field = serializers.FileField(max_length=10) + field._context = {'request': MockRequest()} + obj = MockFile(name='example.txt', url='/example.txt') + value = field.to_representation(obj) + assert value == 'http://example.com/example.txt' + + +# Tests for SerializerMethodField. +# -------------------------------- + +class TestSerializerMethodField: + def test_serializer_method_field(self): + class ExampleSerializer(serializers.Serializer): + example_field = serializers.SerializerMethodField() + + def get_example_field(self, obj): + return 'ran get_example_field(%d)' % obj['example_field'] + + serializer = ExampleSerializer({'example_field': 123}) + assert serializer.data == { + 'example_field': 'ran get_example_field(123)' + } + + def test_redundant_method_name(self): + class ExampleSerializer(serializers.Serializer): + example_field = serializers.SerializerMethodField('get_example_field') + + with pytest.raises(AssertionError) as exc_info: + ExampleSerializer().fields + assert str(exc_info.value) == ( + "It is redundant to specify `get_example_field` on " + "SerializerMethodField 'example_field' in serializer " + "'ExampleSerializer', because it is the same as the default " + "method name. Remove the `method_name` argument." + ) diff --git a/tests/test_files.py b/tests/test_files.py deleted file mode 100644 index a5613fcb..00000000 --- a/tests/test_files.py +++ /dev/null @@ -1,92 +0,0 @@ -# from __future__ import unicode_literals -# from django.test import TestCase -# from django.utils import six -# from rest_framework import serializers -# from rest_framework.compat import BytesIO -# import datetime - - -# class UploadedFile(object): -# def __init__(self, file=None, created=None): -# self.file = file -# self.created = created or datetime.datetime.now() - - -# class UploadedFileSerializer(serializers.Serializer): -# file = serializers.FileField(required=False) -# created = serializers.DateTimeField() - -# def restore_object(self, attrs, instance=None): -# if instance: -# instance.file = attrs['file'] -# instance.created = attrs['created'] -# return instance -# return UploadedFile(**attrs) - - -# class FileSerializerTests(TestCase): -# def test_create(self): -# now = datetime.datetime.now() -# file = BytesIO(six.b('stuff')) -# file.name = 'stuff.txt' -# file.size = len(file.getvalue()) -# serializer = UploadedFileSerializer(data={'created': now}, files={'file': file}) -# uploaded_file = UploadedFile(file=file, created=now) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.object.created, uploaded_file.created) -# self.assertEqual(serializer.object.file, uploaded_file.file) -# self.assertFalse(serializer.object is uploaded_file) - -# def test_creation_failure(self): -# """ -# Passing files=None should result in an ValidationError - -# Regression test for: -# https://github.com/tomchristie/django-rest-framework/issues/542 -# """ -# now = datetime.datetime.now() - -# serializer = UploadedFileSerializer(data={'created': now}) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.object.created, now) -# self.assertIsNone(serializer.object.file) - -# def test_remove_with_empty_string(self): -# """ -# Passing empty string as data should cause file to be removed - -# Test for: -# https://github.com/tomchristie/django-rest-framework/issues/937 -# """ -# now = datetime.datetime.now() -# file = BytesIO(six.b('stuff')) -# file.name = 'stuff.txt' -# file.size = len(file.getvalue()) - -# uploaded_file = UploadedFile(file=file, created=now) - -# serializer = UploadedFileSerializer(instance=uploaded_file, data={'created': now, 'file': ''}) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.object.created, uploaded_file.created) -# self.assertIsNone(serializer.object.file) - -# def test_validation_error_with_non_file(self): -# """ -# Passing non-files should raise a validation error. -# """ -# now = datetime.datetime.now() -# errmsg = 'No file was submitted. Check the encoding type on the form.' - -# serializer = UploadedFileSerializer(data={'created': now, 'file': 'abc'}) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'file': [errmsg]}) - -# def test_validation_with_no_data(self): -# """ -# Validation should still function when no data dictionary is provided. -# """ -# uploaded_file = BytesIO(six.b('stuff')) -# uploaded_file.name = 'stuff.txt' -# uploaded_file.size = len(uploaded_file.getvalue()) -# serializer = UploadedFileSerializer(files={'file': uploaded_file}) -# self.assertFalse(serializer.is_valid()) diff --git a/tests/test_genericrelations.py b/tests/test_genericrelations.py deleted file mode 100644 index a87ea3fd..00000000 --- a/tests/test_genericrelations.py +++ /dev/null @@ -1,151 +0,0 @@ -# from __future__ import unicode_literals -# from django.contrib.contenttypes.models import ContentType -# from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey -# from django.db import models -# from django.test import TestCase -# from rest_framework import serializers -# from rest_framework.compat import python_2_unicode_compatible - - -# @python_2_unicode_compatible -# class Tag(models.Model): -# """ -# Tags have a descriptive slug, and are attached to an arbitrary object. -# """ -# tag = models.SlugField() -# content_type = models.ForeignKey(ContentType) -# object_id = models.PositiveIntegerField() -# tagged_item = GenericForeignKey('content_type', 'object_id') - -# def __str__(self): -# return self.tag - - -# @python_2_unicode_compatible -# class Bookmark(models.Model): -# """ -# A URL bookmark that may have multiple tags attached. -# """ -# url = models.URLField() -# tags = GenericRelation(Tag) - -# def __str__(self): -# return 'Bookmark: %s' % self.url - - -# @python_2_unicode_compatible -# class Note(models.Model): -# """ -# A textual note that may have multiple tags attached. -# """ -# text = models.TextField() -# tags = GenericRelation(Tag) - -# def __str__(self): -# return 'Note: %s' % self.text - - -# class TestGenericRelations(TestCase): -# def setUp(self): -# self.bookmark = Bookmark.objects.create(url='https://www.djangoproject.com/') -# Tag.objects.create(tagged_item=self.bookmark, tag='django') -# Tag.objects.create(tagged_item=self.bookmark, tag='python') -# self.note = Note.objects.create(text='Remember the milk') -# Tag.objects.create(tagged_item=self.note, tag='reminder') - -# def test_generic_relation(self): -# """ -# Test a relationship that spans a GenericRelation field. -# IE. A reverse generic relationship. -# """ - -# class BookmarkSerializer(serializers.ModelSerializer): -# tags = serializers.RelatedField(many=True) - -# class Meta: -# model = Bookmark -# exclude = ('id',) - -# serializer = BookmarkSerializer(self.bookmark) -# expected = { -# 'tags': ['django', 'python'], -# 'url': 'https://www.djangoproject.com/' -# } -# self.assertEqual(serializer.data, expected) - -# def test_generic_nested_relation(self): -# """ -# Test saving a GenericRelation field via a nested serializer. -# """ - -# class TagSerializer(serializers.ModelSerializer): -# class Meta: -# model = Tag -# exclude = ('content_type', 'object_id') - -# class BookmarkSerializer(serializers.ModelSerializer): -# tags = TagSerializer(many=True) - -# class Meta: -# model = Bookmark -# exclude = ('id',) - -# data = { -# 'url': 'https://docs.djangoproject.com/', -# 'tags': [ -# {'tag': 'contenttypes'}, -# {'tag': 'genericrelations'}, -# ] -# } -# serializer = BookmarkSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# serializer.save() -# self.assertEqual(serializer.object.tags.count(), 2) - -# def test_generic_fk(self): -# """ -# Test a relationship that spans a GenericForeignKey field. -# IE. A forward generic relationship. -# """ - -# class TagSerializer(serializers.ModelSerializer): -# tagged_item = serializers.RelatedField() - -# class Meta: -# model = Tag -# exclude = ('id', 'content_type', 'object_id') - -# serializer = TagSerializer(Tag.objects.all(), many=True) -# expected = [ -# { -# 'tag': 'django', -# 'tagged_item': 'Bookmark: https://www.djangoproject.com/' -# }, -# { -# 'tag': 'python', -# 'tagged_item': 'Bookmark: https://www.djangoproject.com/' -# }, -# { -# 'tag': 'reminder', -# 'tagged_item': 'Note: Remember the milk' -# } -# ] -# self.assertEqual(serializer.data, expected) - -# def test_restore_object_generic_fk(self): -# """ -# Ensure an object with a generic foreign key can be restored. -# """ - -# class TagSerializer(serializers.ModelSerializer): -# class Meta: -# model = Tag -# exclude = ('content_type', 'object_id') - -# serializer = TagSerializer() - -# bookmark = Bookmark(url='http://example.com') -# attrs = {'tagged_item': bookmark, 'tag': 'example'} - -# tag = serializer.restore_object(attrs) -# self.assertEqual(tag.tagged_item, bookmark) diff --git a/tests/test_generics.py b/tests/test_generics.py index 51004edf..2690fb47 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -23,31 +23,22 @@ class ForeignKeySerializer(serializers.ModelSerializer): class RootView(generics.ListCreateAPIView): - """ - Example description for OPTIONS. - """ queryset = BasicModel.objects.all() serializer_class = BasicSerializer class InstanceView(generics.RetrieveUpdateDestroyAPIView): - """ - Example description for OPTIONS. - """ queryset = BasicModel.objects.exclude(text='filtered out') serializer_class = BasicSerializer class FKInstanceView(generics.RetrieveUpdateDestroyAPIView): - """ - FK: example description for OPTIONS. - """ queryset = ForeignKeySource.objects.all() serializer_class = ForeignKeySerializer class SlugSerializer(serializers.ModelSerializer): - slug = serializers.Field(read_only=True) + slug = serializers.ReadOnlyField() class Meta: model = SlugBasedModel @@ -122,47 +113,6 @@ class TestRootView(TestCase): self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) self.assertEqual(response.data, {"detail": "Method 'DELETE' not allowed."}) - # def test_options_root_view(self): - # """ - # OPTIONS requests to ListCreateAPIView should return metadata - # """ - # request = factory.options('/') - # with self.assertNumQueries(0): - # response = self.view(request).render() - # expected = { - # 'parses': [ - # 'application/json', - # 'application/x-www-form-urlencoded', - # 'multipart/form-data' - # ], - # 'renders': [ - # 'application/json', - # 'text/html' - # ], - # 'name': 'Root', - # 'description': 'Example description for OPTIONS.', - # 'actions': { - # 'POST': { - # 'text': { - # 'max_length': 100, - # 'read_only': False, - # 'required': True, - # 'type': 'string', - # "label": "Text comes here", - # "help_text": "Text description." - # }, - # 'id': { - # 'read_only': True, - # 'required': False, - # 'type': 'integer', - # 'label': 'ID', - # }, - # } - # } - # } - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertEqual(response.data, expected) - def test_post_cannot_set_id(self): """ POST requests to create a new object should not be able to set the id. @@ -256,89 +206,6 @@ class TestInstanceView(TestCase): ids = [obj.id for obj in self.objects.all()] self.assertEqual(ids, [2, 3]) - # def test_options_instance_view(self): - # """ - # OPTIONS requests to RetrieveUpdateDestroyAPIView should return metadata - # """ - # request = factory.options('/1') - # with self.assertNumQueries(1): - # response = self.view(request, pk=1).render() - # expected = { - # 'parses': [ - # 'application/json', - # 'application/x-www-form-urlencoded', - # 'multipart/form-data' - # ], - # 'renders': [ - # 'application/json', - # 'text/html' - # ], - # 'name': 'Instance', - # 'description': 'Example description for OPTIONS.', - # 'actions': { - # 'PUT': { - # 'text': { - # 'max_length': 100, - # 'read_only': False, - # 'required': True, - # 'type': 'string', - # 'label': 'Text comes here', - # 'help_text': 'Text description.' - # }, - # 'id': { - # 'read_only': True, - # 'required': False, - # 'type': 'integer', - # 'label': 'ID', - # }, - # } - # } - # } - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertEqual(response.data, expected) - - # def test_options_before_instance_create(self): - # """ - # OPTIONS requests to RetrieveUpdateDestroyAPIView should return metadata - # before the instance has been created - # """ - # request = factory.options('/999') - # with self.assertNumQueries(1): - # response = self.view(request, pk=999).render() - # expected = { - # 'parses': [ - # 'application/json', - # 'application/x-www-form-urlencoded', - # 'multipart/form-data' - # ], - # 'renders': [ - # 'application/json', - # 'text/html' - # ], - # 'name': 'Instance', - # 'description': 'Example description for OPTIONS.', - # 'actions': { - # 'PUT': { - # 'text': { - # 'max_length': 100, - # 'read_only': False, - # 'required': True, - # 'type': 'string', - # 'label': 'Text comes here', - # 'help_text': 'Text description.' - # }, - # 'id': { - # 'read_only': True, - # 'required': False, - # 'type': 'integer', - # 'label': 'ID', - # }, - # } - # } - # } - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertEqual(response.data, expected) - def test_get_instance_view_incorrect_arg(self): """ GET requests with an incorrect pk type, should raise 404, not 500. @@ -415,53 +282,6 @@ class TestFKInstanceView(TestCase): ] self.view = FKInstanceView.as_view() - # def test_options_root_view(self): - # """ - # OPTIONS requests to ListCreateAPIView should return metadata - # """ - # request = factory.options('/999') - # with self.assertNumQueries(1): - # response = self.view(request, pk=999).render() - # expected = { - # 'name': 'Fk Instance', - # 'description': 'FK: example description for OPTIONS.', - # 'renders': [ - # 'application/json', - # 'text/html' - # ], - # 'parses': [ - # 'application/json', - # 'application/x-www-form-urlencoded', - # 'multipart/form-data' - # ], - # 'actions': { - # 'PUT': { - # 'id': { - # 'type': 'integer', - # 'required': False, - # 'read_only': True, - # 'label': 'ID' - # }, - # 'name': { - # 'type': 'string', - # 'required': True, - # 'read_only': False, - # 'label': 'name', - # 'max_length': 100 - # }, - # 'target': { - # 'type': 'field', - # 'required': True, - # 'read_only': False, - # 'label': 'Target', - # 'help_text': 'Target' - # } - # } - # } - # } - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertEqual(response.data, expected) - class TestOverriddenGetObject(TestCase): """ diff --git a/tests/test_hyperlinkedserializers.py b/tests/test_hyperlinkedserializers.py deleted file mode 100644 index ff3663dd..00000000 --- a/tests/test_hyperlinkedserializers.py +++ /dev/null @@ -1,406 +0,0 @@ -# from __future__ import unicode_literals -# import json -# from django.test import TestCase -# from rest_framework import generics, status, serializers -# from django.conf.urls import patterns, url -# from rest_framework.settings import api_settings -# from rest_framework.test import APIRequestFactory -# from tests.models import ( -# Anchor, BasicModel, ManyToManyModel, BlogPost, BlogPostComment, -# Album, Photo, OptionalRelationModel -# ) - -# factory = APIRequestFactory() - - -# class BlogPostCommentSerializer(serializers.ModelSerializer): -# url = serializers.HyperlinkedIdentityField(view_name='blogpostcomment-detail') -# text = serializers.CharField() -# blog_post_url = serializers.HyperlinkedRelatedField(source='blog_post', view_name='blogpost-detail') - -# class Meta: -# model = BlogPostComment -# fields = ('text', 'blog_post_url', 'url') - - -# class PhotoSerializer(serializers.Serializer): -# description = serializers.CharField() -# album_url = serializers.HyperlinkedRelatedField(source='album', view_name='album-detail', queryset=Album.objects.all(), lookup_field='title') - -# def restore_object(self, attrs, instance=None): -# return Photo(**attrs) - - -# class AlbumSerializer(serializers.ModelSerializer): -# url = serializers.HyperlinkedIdentityField(view_name='album-detail', lookup_field='title') - -# class Meta: -# model = Album -# fields = ('title', 'url') - - -# class BasicSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = BasicModel - - -# class AnchorSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = Anchor - - -# class ManyToManySerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = ManyToManyModel - - -# class BlogPostSerializer(serializers.ModelSerializer): -# class Meta: -# model = BlogPost - - -# class OptionalRelationSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = OptionalRelationModel - - -# class BasicList(generics.ListCreateAPIView): -# queryset = BasicModel.objects.all() -# serializer_class = BasicSerializer - - -# class BasicDetail(generics.RetrieveUpdateDestroyAPIView): -# queryset = BasicModel.objects.all() -# serializer_class = BasicSerializer - - -# class AnchorDetail(generics.RetrieveAPIView): -# queryset = Anchor.objects.all() -# serializer_class = AnchorSerializer - - -# class ManyToManyList(generics.ListAPIView): -# queryset = ManyToManyModel.objects.all() -# serializer_class = ManyToManySerializer - - -# class ManyToManyDetail(generics.RetrieveAPIView): -# queryset = ManyToManyModel.objects.all() -# serializer_class = ManyToManySerializer - - -# class BlogPostCommentListCreate(generics.ListCreateAPIView): -# queryset = BlogPostComment.objects.all() -# serializer_class = BlogPostCommentSerializer - - -# class BlogPostCommentDetail(generics.RetrieveAPIView): -# queryset = BlogPostComment.objects.all() -# serializer_class = BlogPostCommentSerializer - - -# class BlogPostDetail(generics.RetrieveAPIView): -# queryset = BlogPost.objects.all() -# serializer_class = BlogPostSerializer - - -# class PhotoListCreate(generics.ListCreateAPIView): -# queryset = Photo.objects.all() -# serializer_class = PhotoSerializer - - -# class AlbumDetail(generics.RetrieveAPIView): -# queryset = Album.objects.all() -# serializer_class = AlbumSerializer -# lookup_field = 'title' - - -# class OptionalRelationDetail(generics.RetrieveUpdateDestroyAPIView): -# queryset = OptionalRelationModel.objects.all() -# serializer_class = OptionalRelationSerializer - - -# urlpatterns = patterns( -# '', -# url(r'^basic/$', BasicList.as_view(), name='basicmodel-list'), -# url(r'^basic/(?P<pk>\d+)/$', BasicDetail.as_view(), name='basicmodel-detail'), -# url(r'^anchor/(?P<pk>\d+)/$', AnchorDetail.as_view(), name='anchor-detail'), -# url(r'^manytomany/$', ManyToManyList.as_view(), name='manytomanymodel-list'), -# url(r'^manytomany/(?P<pk>\d+)/$', ManyToManyDetail.as_view(), name='manytomanymodel-detail'), -# url(r'^posts/(?P<pk>\d+)/$', BlogPostDetail.as_view(), name='blogpost-detail'), -# url(r'^comments/$', BlogPostCommentListCreate.as_view(), name='blogpostcomment-list'), -# url(r'^comments/(?P<pk>\d+)/$', BlogPostCommentDetail.as_view(), name='blogpostcomment-detail'), -# url(r'^albums/(?P<title>\w[\w-]*)/$', AlbumDetail.as_view(), name='album-detail'), -# url(r'^photos/$', PhotoListCreate.as_view(), name='photo-list'), -# url(r'^optionalrelation/(?P<pk>\d+)/$', OptionalRelationDetail.as_view(), name='optionalrelationmodel-detail'), -# ) - - -# class TestBasicHyperlinkedView(TestCase): -# urls = 'tests.test_hyperlinkedserializers' - -# def setUp(self): -# """ -# Create 3 BasicModel instances. -# """ -# items = ['foo', 'bar', 'baz'] -# for item in items: -# BasicModel(text=item).save() -# self.objects = BasicModel.objects -# self.data = [ -# {'url': 'http://testserver/basic/%d/' % obj.id, 'text': obj.text} -# for obj in self.objects.all() -# ] -# self.list_view = BasicList.as_view() -# self.detail_view = BasicDetail.as_view() - -# def test_get_list_view(self): -# """ -# GET requests to ListCreateAPIView should return list of objects. -# """ -# request = factory.get('/basic/') -# response = self.list_view(request).render() -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response.data, self.data) - -# def test_get_detail_view(self): -# """ -# GET requests to ListCreateAPIView should return list of objects. -# """ -# request = factory.get('/basic/1') -# response = self.detail_view(request, pk=1).render() -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response.data, self.data[0]) - - -# class TestManyToManyHyperlinkedView(TestCase): -# urls = 'tests.test_hyperlinkedserializers' - -# def setUp(self): -# """ -# Create 3 BasicModel instances. -# """ -# items = ['foo', 'bar', 'baz'] -# anchors = [] -# for item in items: -# anchor = Anchor(text=item) -# anchor.save() -# anchors.append(anchor) - -# manytomany = ManyToManyModel() -# manytomany.save() -# manytomany.rel.add(*anchors) - -# self.data = [{ -# 'url': 'http://testserver/manytomany/1/', -# 'rel': [ -# 'http://testserver/anchor/1/', -# 'http://testserver/anchor/2/', -# 'http://testserver/anchor/3/', -# ] -# }] -# self.list_view = ManyToManyList.as_view() -# self.detail_view = ManyToManyDetail.as_view() - -# def test_get_list_view(self): -# """ -# GET requests to ListCreateAPIView should return list of objects. -# """ -# request = factory.get('/manytomany/') -# response = self.list_view(request) -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response.data, self.data) - -# def test_get_detail_view(self): -# """ -# GET requests to ListCreateAPIView should return list of objects. -# """ -# request = factory.get('/manytomany/1/') -# response = self.detail_view(request, pk=1) -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response.data, self.data[0]) - - -# class TestHyperlinkedIdentityFieldLookup(TestCase): -# urls = 'tests.test_hyperlinkedserializers' - -# def setUp(self): -# """ -# Create 3 Album instances. -# """ -# titles = ['foo', 'bar', 'baz'] -# for title in titles: -# album = Album(title=title) -# album.save() -# self.detail_view = AlbumDetail.as_view() -# self.data = { -# 'foo': {'title': 'foo', 'url': 'http://testserver/albums/foo/'}, -# 'bar': {'title': 'bar', 'url': 'http://testserver/albums/bar/'}, -# 'baz': {'title': 'baz', 'url': 'http://testserver/albums/baz/'} -# } - -# def test_lookup_field(self): -# """ -# GET requests to AlbumDetail view should return serialized Albums -# with a url field keyed by `title`. -# """ -# for album in Album.objects.all(): -# request = factory.get('/albums/{0}/'.format(album.title)) -# response = self.detail_view(request, title=album.title) -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response.data, self.data[album.title]) - - -# class TestCreateWithForeignKeys(TestCase): -# urls = 'tests.test_hyperlinkedserializers' - -# def setUp(self): -# """ -# Create a blog post -# """ -# self.post = BlogPost.objects.create(title="Test post") -# self.create_view = BlogPostCommentListCreate.as_view() - -# def test_create_comment(self): - -# data = { -# 'text': 'A test comment', -# 'blog_post_url': 'http://testserver/posts/1/' -# } - -# request = factory.post('/comments/', data=data) -# response = self.create_view(request) -# self.assertEqual(response.status_code, status.HTTP_201_CREATED) -# self.assertEqual(response['Location'], 'http://testserver/comments/1/') -# self.assertEqual(self.post.blogpostcomment_set.count(), 1) -# self.assertEqual(self.post.blogpostcomment_set.all()[0].text, 'A test comment') - - -# class TestCreateWithForeignKeysAndCustomSlug(TestCase): -# urls = 'tests.test_hyperlinkedserializers' - -# def setUp(self): -# """ -# Create an Album -# """ -# self.post = Album.objects.create(title='test-album') -# self.list_create_view = PhotoListCreate.as_view() - -# def test_create_photo(self): - -# data = { -# 'description': 'A test photo', -# 'album_url': 'http://testserver/albums/test-album/' -# } - -# request = factory.post('/photos/', data=data) -# response = self.list_create_view(request) -# self.assertEqual(response.status_code, status.HTTP_201_CREATED) -# self.assertNotIn('Location', response, msg='Location should only be included if there is a "url" field on the serializer') -# self.assertEqual(self.post.photo_set.count(), 1) -# self.assertEqual(self.post.photo_set.all()[0].description, 'A test photo') - - -# class TestOptionalRelationHyperlinkedView(TestCase): -# urls = 'tests.test_hyperlinkedserializers' - -# def setUp(self): -# """ -# Create 1 OptionalRelationModel instances. -# """ -# OptionalRelationModel().save() -# self.objects = OptionalRelationModel.objects -# self.detail_view = OptionalRelationDetail.as_view() -# self.data = {"url": "http://testserver/optionalrelation/1/", "other": None} - -# def test_get_detail_view(self): -# """ -# GET requests to RetrieveAPIView with optional relations should return None -# for non existing relations. -# """ -# request = factory.get('/optionalrelationmodel-detail/1') -# response = self.detail_view(request, pk=1) -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response.data, self.data) - -# def test_put_detail_view(self): -# """ -# PUT requests to RetrieveUpdateDestroyAPIView with optional relations -# should accept None for non existing relations. -# """ -# response = self.client.put('/optionalrelation/1/', -# data=json.dumps(self.data), -# content_type='application/json') -# self.assertEqual(response.status_code, status.HTTP_200_OK) - - -# class TestOverriddenURLField(TestCase): -# def setUp(self): -# class OverriddenURLSerializer(serializers.HyperlinkedModelSerializer): -# url = serializers.SerializerMethodField('get_url') - -# class Meta: -# model = BlogPost -# fields = ('title', 'url') - -# def get_url(self, obj): -# return 'foo bar' - -# self.Serializer = OverriddenURLSerializer -# self.obj = BlogPost.objects.create(title='New blog post') - -# def test_overridden_url_field(self): -# """ -# The 'url' field should respect overriding. -# Regression test for #936. -# """ -# serializer = self.Serializer(self.obj) -# self.assertEqual( -# serializer.data, -# {'title': 'New blog post', 'url': 'foo bar'} -# ) - - -# class TestURLFieldNameBySettings(TestCase): -# urls = 'tests.test_hyperlinkedserializers' - -# def setUp(self): -# self.saved_url_field_name = api_settings.URL_FIELD_NAME -# api_settings.URL_FIELD_NAME = 'global_url_field' - -# class Serializer(serializers.HyperlinkedModelSerializer): - -# class Meta: -# model = BlogPost -# fields = ('title', api_settings.URL_FIELD_NAME) - -# self.Serializer = Serializer -# self.obj = BlogPost.objects.create(title="New blog post") - -# def tearDown(self): -# api_settings.URL_FIELD_NAME = self.saved_url_field_name - -# def test_overridden_url_field_name(self): -# request = factory.get('/posts/') -# serializer = self.Serializer(self.obj, context={'request': request}) -# self.assertIn(api_settings.URL_FIELD_NAME, serializer.data) - - -# class TestURLFieldNameByOptions(TestCase): -# urls = 'tests.test_hyperlinkedserializers' - -# def setUp(self): -# class Serializer(serializers.HyperlinkedModelSerializer): - -# class Meta: -# model = BlogPost -# fields = ('title', 'serializer_url_field') -# url_field_name = 'serializer_url_field' - -# self.Serializer = Serializer -# self.obj = BlogPost.objects.create(title="New blog post") - -# def test_overridden_url_field_name(self): -# request = factory.get('/posts/') -# serializer = self.Serializer(self.obj, context={'request': request}) -# self.assertIn(self.Serializer.Meta.url_field_name, serializer.data) diff --git a/tests/test_metadata.py b/tests/test_metadata.py new file mode 100644 index 00000000..5ff59c72 --- /dev/null +++ b/tests/test_metadata.py @@ -0,0 +1,166 @@ +from __future__ import unicode_literals + +from rest_framework import exceptions, serializers, views +from rest_framework.request import Request +from rest_framework.test import APIRequestFactory +import pytest + +request = Request(APIRequestFactory().options('/')) + + +class TestMetadata: + def test_metadata(self): + """ + OPTIONS requests to views should return a valid 200 response. + """ + class ExampleView(views.APIView): + """Example view.""" + pass + + response = ExampleView().options(request=request) + expected = { + 'name': 'Example', + 'description': 'Example view.', + 'renders': [ + 'application/json', + 'text/html' + ], + 'parses': [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ] + } + assert response.status_code == 200 + assert response.data == expected + + def test_none_metadata(self): + """ + OPTIONS requests to views where `metadata_class = None` should raise + a MethodNotAllowed exception, which will result in an HTTP 405 response. + """ + class ExampleView(views.APIView): + metadata_class = None + + with pytest.raises(exceptions.MethodNotAllowed): + ExampleView().options(request=request) + + def test_actions(self): + """ + On generic views OPTIONS should return an 'actions' key with metadata + on the fields that may be supplied to PUT and POST requests. + """ + class ExampleSerializer(serializers.Serializer): + choice_field = serializers.ChoiceField(['red', 'green', 'blue']) + integer_field = serializers.IntegerField(max_value=10) + char_field = serializers.CharField(required=False) + + class ExampleView(views.APIView): + """Example view.""" + def post(self, request): + pass + + def get_serializer(self): + return ExampleSerializer() + + response = ExampleView().options(request=request) + expected = { + 'name': 'Example', + 'description': 'Example view.', + 'renders': [ + 'application/json', + 'text/html' + ], + 'parses': [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ], + 'actions': { + 'POST': { + 'choice_field': { + 'type': 'choice', + 'required': True, + 'read_only': False, + 'label': 'Choice field', + 'choices': [ + {'display_name': 'red', 'value': 'red'}, + {'display_name': 'green', 'value': 'green'}, + {'display_name': 'blue', 'value': 'blue'} + ] + }, + 'integer_field': { + 'type': 'integer', + 'required': True, + 'read_only': False, + 'label': 'Integer field' + }, + 'char_field': { + 'type': 'string', + 'required': False, + 'read_only': False, + 'label': 'Char field' + } + } + } + } + assert response.status_code == 200 + assert response.data == expected + + def test_global_permissions(self): + """ + If a user does not have global permissions on an action, then any + metadata associated with it should not be included in OPTION responses. + """ + class ExampleSerializer(serializers.Serializer): + choice_field = serializers.ChoiceField(['red', 'green', 'blue']) + integer_field = serializers.IntegerField(max_value=10) + char_field = serializers.CharField(required=False) + + class ExampleView(views.APIView): + """Example view.""" + def post(self, request): + pass + + def put(self, request): + pass + + def get_serializer(self): + return ExampleSerializer() + + def check_permissions(self, request): + if request.method == 'POST': + raise exceptions.PermissionDenied() + + response = ExampleView().options(request=request) + assert response.status_code == 200 + assert list(response.data['actions'].keys()) == ['PUT'] + + def test_object_permissions(self): + """ + If a user does not have object permissions on an action, then any + metadata associated with it should not be included in OPTION responses. + """ + class ExampleSerializer(serializers.Serializer): + choice_field = serializers.ChoiceField(['red', 'green', 'blue']) + integer_field = serializers.IntegerField(max_value=10) + char_field = serializers.CharField(required=False) + + class ExampleView(views.APIView): + """Example view.""" + def post(self, request): + pass + + def put(self, request): + pass + + def get_serializer(self): + return ExampleSerializer() + + def get_object(self): + if self.request.method == 'PUT': + raise exceptions.PermissionDenied() + + response = ExampleView().options(request=request) + assert response.status_code == 200 + assert list(response.data['actions'].keys()) == ['POST'] diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index ec922b6d..3aec0da0 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -6,6 +6,7 @@ These tests deal with ensuring that we correctly map the model fields onto an appropriate set of serializer fields for each case. """ from django.core.exceptions import ImproperlyConfigured +from django.core.validators import MaxValueValidator, MinValueValidator, MinLengthValidator from django.db import models from django.test import TestCase from rest_framework import serializers @@ -15,7 +16,8 @@ def dedent(blocktext): return '\n'.join([line[12:] for line in blocktext.splitlines()[1:-1]]) -# Testing regular field mappings +# Tests for regular field mappings. +# --------------------------------- class CustomField(models.Field): """ @@ -32,7 +34,7 @@ class RegularFieldsModel(models.Model): big_integer_field = models.BigIntegerField() boolean_field = models.BooleanField(default=False) char_field = models.CharField(max_length=100) - comma_seperated_integer_field = models.CommaSeparatedIntegerField(max_length=100) + comma_separated_integer_field = models.CommaSeparatedIntegerField(max_length=100) date_field = models.DateField() datetime_field = models.DateTimeField() decimal_field = models.DecimalField(max_digits=3, decimal_places=1) @@ -53,6 +55,19 @@ class RegularFieldsModel(models.Model): return 'method' +COLOR_CHOICES = (('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')) + + +class FieldOptionsModel(models.Model): + value_limit_field = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(10)]) + length_limit_field = models.CharField(validators=[MinLengthValidator(3)], max_length=12) + blank_field = models.CharField(blank=True, max_length=10) + null_field = models.IntegerField(null=True) + default_field = models.IntegerField(default=0) + descriptive_field = models.IntegerField(help_text='Some help text', verbose_name='A label') + choices_field = models.CharField(max_length=100, choices=COLOR_CHOICES) + + class TestRegularFieldMappings(TestCase): def test_regular_fields(self): """ @@ -66,27 +81,45 @@ class TestRegularFieldMappings(TestCase): TestSerializer(): auto_field = IntegerField(read_only=True) big_integer_field = IntegerField() - boolean_field = BooleanField(default=False) + boolean_field = BooleanField(required=False) char_field = CharField(max_length=100) - comma_seperated_integer_field = CharField(max_length=100, validators=[<django.core.validators.RegexValidator object>]) + comma_separated_integer_field = CharField(max_length=100, validators=[<django.core.validators.RegexValidator object>]) date_field = DateField() datetime_field = DateTimeField() decimal_field = DecimalField(decimal_places=1, max_digits=3) email_field = EmailField(max_length=100) float_field = FloatField() integer_field = IntegerField() - null_boolean_field = BooleanField(required=False) + null_boolean_field = NullBooleanField(required=False) positive_integer_field = IntegerField() positive_small_integer_field = IntegerField() slug_field = SlugField(max_length=100) small_integer_field = IntegerField() - text_field = CharField() + text_field = CharField(style={'type': 'textarea'}) time_field = TimeField() url_field = URLField(max_length=100) custom_field = ModelField(model_field=<tests.test_model_serializer.CustomField: custom_field>) """) self.assertEqual(repr(TestSerializer()), expected) + def test_field_options(self): + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = FieldOptionsModel + + expected = dedent(""" + TestSerializer(): + id = IntegerField(label='ID', read_only=True) + value_limit_field = IntegerField(max_value=10, min_value=1) + length_limit_field = CharField(max_length=12, min_length=3) + blank_field = CharField(allow_blank=True, max_length=10, required=False) + null_field = IntegerField(allow_null=True, required=False) + default_field = IntegerField(required=False) + descriptive_field = IntegerField(help_text='Some help text', label='A label') + choices_field = ChoiceField(choices=[('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')]) + """) + self.assertEqual(repr(TestSerializer()), expected) + def test_method_field(self): """ Properties and methods on the model should be allowed as `Meta.fields` @@ -148,7 +181,7 @@ class TestRegularFieldMappings(TestCase): fields = ('auto_field', 'invalid') with self.assertRaises(ImproperlyConfigured) as excinfo: - TestSerializer() + TestSerializer().fields expected = 'Field name `invalid` is not valid for model `ModelBase`.' assert str(excinfo.exception) == expected @@ -165,7 +198,7 @@ class TestRegularFieldMappings(TestCase): fields = ('auto_field',) with self.assertRaises(ImproperlyConfigured) as excinfo: - TestSerializer() + TestSerializer().fields expected = ( 'Field `missing` has been declared on serializer ' '`TestSerializer`, but is missing from `Meta.fields`.' @@ -173,7 +206,8 @@ class TestRegularFieldMappings(TestCase): assert str(excinfo.exception) == expected -# Testing relational field mappings +# Tests for relational field mappings. +# ------------------------------------ class ForeignKeyTargetModel(models.Model): name = models.CharField(max_length=100) @@ -214,7 +248,7 @@ class TestRelationalFieldMappings(TestCase): TestSerializer(): id = IntegerField(label='ID', read_only=True) foreign_key = PrimaryKeyRelatedField(queryset=ForeignKeyTargetModel.objects.all()) - one_to_one = PrimaryKeyRelatedField(queryset=OneToOneTargetModel.objects.all()) + one_to_one = PrimaryKeyRelatedField(queryset=OneToOneTargetModel.objects.all(), validators=[<UniqueValidator(queryset=RelationalModel.objects.all())>]) many_to_many = PrimaryKeyRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all()) through = PrimaryKeyRelatedField(many=True, read_only=True) """) @@ -253,7 +287,7 @@ class TestRelationalFieldMappings(TestCase): TestSerializer(): url = HyperlinkedIdentityField(view_name='relationalmodel-detail') foreign_key = HyperlinkedRelatedField(queryset=ForeignKeyTargetModel.objects.all(), view_name='foreignkeytargetmodel-detail') - one_to_one = HyperlinkedRelatedField(queryset=OneToOneTargetModel.objects.all(), view_name='onetoonetargetmodel-detail') + one_to_one = HyperlinkedRelatedField(queryset=OneToOneTargetModel.objects.all(), validators=[<UniqueValidator(queryset=RelationalModel.objects.all())>], view_name='onetoonetargetmodel-detail') many_to_many = HyperlinkedRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail') through = HyperlinkedRelatedField(many=True, read_only=True, view_name='throughtargetmodel-detail') """) @@ -468,3 +502,36 @@ class TestIntegration(TestCase): 'through': [] } self.assertEqual(serializer.data, expected) + + +# Tests for bulk create using `ListSerializer`. + +class BulkCreateModel(models.Model): + name = models.CharField(max_length=10) + + +class TestBulkCreate(TestCase): + def test_bulk_create(self): + class BasicModelSerializer(serializers.ModelSerializer): + class Meta: + model = BulkCreateModel + fields = ('name',) + + class BulkCreateSerializer(serializers.ListSerializer): + child = BasicModelSerializer() + + data = [{'name': 'a'}, {'name': 'b'}, {'name': 'c'}] + serializer = BulkCreateSerializer(data=data) + assert serializer.is_valid() + + # Objects are returned by save(). + instances = serializer.save() + assert len(instances) == 3 + assert [item.name for item in instances] == ['a', 'b', 'c'] + + # Objects have been created in the database. + assert BulkCreateModel.objects.count() == 3 + assert list(BulkCreateModel.objects.values_list('name', flat=True)) == ['a', 'b', 'c'] + + # Serializer returns correct data. + assert serializer.data == data diff --git a/tests/test_modelinfo.py b/tests/test_modelinfo.py deleted file mode 100644 index 04b67f04..00000000 --- a/tests/test_modelinfo.py +++ /dev/null @@ -1,31 +0,0 @@ -from django.test import TestCase -from django.utils import six -from rest_framework.utils.model_meta import _resolve_model -from tests.models import BasicModel - - -class ResolveModelTests(TestCase): - """ - `_resolve_model` should return a Django model class given the - provided argument is a Django model class itself, or a properly - formatted string representation of one. - """ - def test_resolve_django_model(self): - resolved_model = _resolve_model(BasicModel) - self.assertEqual(resolved_model, BasicModel) - - def test_resolve_string_representation(self): - resolved_model = _resolve_model('tests.BasicModel') - self.assertEqual(resolved_model, BasicModel) - - def test_resolve_unicode_representation(self): - resolved_model = _resolve_model(six.text_type('tests.BasicModel')) - self.assertEqual(resolved_model, BasicModel) - - def test_resolve_non_django_model(self): - with self.assertRaises(ValueError): - _resolve_model(TestCase) - - def test_resolve_improper_string_representation(self): - with self.assertRaises(ValueError): - _resolve_model('BasicModel') diff --git a/tests/test_nullable_fields.py b/tests/test_nullable_fields.py deleted file mode 100644 index 9843182a..00000000 --- a/tests/test_nullable_fields.py +++ /dev/null @@ -1,39 +0,0 @@ -# from django.core.urlresolvers import reverse - -# from django.conf.urls import patterns, url -# from rest_framework import serializers, generics -# from rest_framework.test import APITestCase -# from tests.models import NullableForeignKeySource - - -# class NullableFKSourceSerializer(serializers.ModelSerializer): -# class Meta: -# model = NullableForeignKeySource - - -# class NullableFKSourceDetail(generics.RetrieveUpdateDestroyAPIView): -# queryset = NullableForeignKeySource.objects.all() -# serializer_class = NullableFKSourceSerializer - - -# urlpatterns = patterns( -# '', -# url(r'^objects/(?P<pk>\d+)/$', NullableFKSourceDetail.as_view(), name='object-detail'), -# ) - - -# class NullableForeignKeyTests(APITestCase): -# """ -# DRF should be able to handle nullable foreign keys when a test -# Client POST/PUT request is made with its own serialized object. -# """ -# urls = 'tests.test_nullable_fields' - -# def test_updating_object_with_null_fk(self): -# obj = NullableForeignKeySource(name='example', target=None) -# obj.save() -# serialized_data = NullableFKSourceSerializer(obj).data - -# response = self.client.put(reverse('object-detail', args=[obj.pk]), serialized_data) - -# self.assertEqual(response.data, serialized_data) diff --git a/tests/test_permissions.py b/tests/test_permissions.py index ac398f80..97bac33d 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -95,59 +95,59 @@ class ModelPermissionsIntegrationTests(TestCase): response = instance_view(request, pk=1) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - # def test_options_permitted(self): - # request = factory.options( - # '/', - # HTTP_AUTHORIZATION=self.permitted_credentials - # ) - # response = root_view(request, pk='1') - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertIn('actions', response.data) - # self.assertEqual(list(response.data['actions'].keys()), ['POST']) - - # request = factory.options( - # '/1', - # HTTP_AUTHORIZATION=self.permitted_credentials - # ) - # response = instance_view(request, pk='1') - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertIn('actions', response.data) - # self.assertEqual(list(response.data['actions'].keys()), ['PUT']) - - # def test_options_disallowed(self): - # request = factory.options( - # '/', - # HTTP_AUTHORIZATION=self.disallowed_credentials - # ) - # response = root_view(request, pk='1') - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertNotIn('actions', response.data) - - # request = factory.options( - # '/1', - # HTTP_AUTHORIZATION=self.disallowed_credentials - # ) - # response = instance_view(request, pk='1') - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertNotIn('actions', response.data) - - # def test_options_updateonly(self): - # request = factory.options( - # '/', - # HTTP_AUTHORIZATION=self.updateonly_credentials - # ) - # response = root_view(request, pk='1') - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertNotIn('actions', response.data) - - # request = factory.options( - # '/1', - # HTTP_AUTHORIZATION=self.updateonly_credentials - # ) - # response = instance_view(request, pk='1') - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertIn('actions', response.data) - # self.assertEqual(list(response.data['actions'].keys()), ['PUT']) + def test_options_permitted(self): + request = factory.options( + '/', + HTTP_AUTHORIZATION=self.permitted_credentials + ) + response = root_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('actions', response.data) + self.assertEqual(list(response.data['actions'].keys()), ['POST']) + + request = factory.options( + '/1', + HTTP_AUTHORIZATION=self.permitted_credentials + ) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('actions', response.data) + self.assertEqual(list(response.data['actions'].keys()), ['PUT']) + + def test_options_disallowed(self): + request = factory.options( + '/', + HTTP_AUTHORIZATION=self.disallowed_credentials + ) + response = root_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotIn('actions', response.data) + + request = factory.options( + '/1', + HTTP_AUTHORIZATION=self.disallowed_credentials + ) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotIn('actions', response.data) + + def test_options_updateonly(self): + request = factory.options( + '/', + HTTP_AUTHORIZATION=self.updateonly_credentials + ) + response = root_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotIn('actions', response.data) + + request = factory.options( + '/1', + HTTP_AUTHORIZATION=self.updateonly_credentials + ) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('actions', response.data) + self.assertEqual(list(response.data['actions'].keys()), ['PUT']) class BasicPermModel(models.Model): diff --git a/tests/test_relations.py b/tests/test_relations.py index c29618ce..62353dc2 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -1,5 +1,5 @@ from .utils import mock_reverse, fail_reverse, BadType, MockObject, MockQueryset -from django.core.exceptions import ImproperlyConfigured, ValidationError +from django.core.exceptions import ImproperlyConfigured from rest_framework import serializers from rest_framework.test import APISimpleTestCase import pytest @@ -30,15 +30,15 @@ class TestPrimaryKeyRelatedField(APISimpleTestCase): assert instance is self.instance def test_pk_related_lookup_does_not_exist(self): - with pytest.raises(ValidationError) as excinfo: + with pytest.raises(serializers.ValidationError) as excinfo: self.field.to_internal_value(4) - msg = excinfo.value.messages[0] + msg = excinfo.value.detail[0] assert msg == "Invalid pk '4' - object does not exist." def test_pk_related_lookup_invalid_type(self): - with pytest.raises(ValidationError) as excinfo: + with pytest.raises(serializers.ValidationError) as excinfo: self.field.to_internal_value(BadType()) - msg = excinfo.value.messages[0] + msg = excinfo.value.detail[0] assert msg == 'Incorrect type. Expected pk value, received BadType.' def test_pk_representation(self): @@ -51,7 +51,7 @@ class TestHyperlinkedIdentityField(APISimpleTestCase): self.instance = MockObject(pk=1, name='foo') self.field = serializers.HyperlinkedIdentityField(view_name='example') self.field.reverse = mock_reverse - self.field.context = {'request': True} + self.field._context = {'request': True} def test_representation(self): representation = self.field.to_representation(self.instance) @@ -62,7 +62,7 @@ class TestHyperlinkedIdentityField(APISimpleTestCase): assert representation is None def test_representation_with_format(self): - self.field.context['format'] = 'xml' + self.field._context['format'] = 'xml' representation = self.field.to_representation(self.instance) assert representation == 'http://example.org/example/1.xml/' @@ -91,14 +91,14 @@ class TestHyperlinkedIdentityFieldWithFormat(APISimpleTestCase): self.instance = MockObject(pk=1, name='foo') self.field = serializers.HyperlinkedIdentityField(view_name='example', format='json') self.field.reverse = mock_reverse - self.field.context = {'request': True} + self.field._context = {'request': True} def test_representation(self): representation = self.field.to_representation(self.instance) assert representation == 'http://example.org/example/1/' def test_representation_with_format(self): - self.field.context['format'] = 'xml' + self.field._context['format'] = 'xml' representation = self.field.to_representation(self.instance) assert representation == 'http://example.org/example/1.json/' @@ -120,169 +120,17 @@ class TestSlugRelatedField(APISimpleTestCase): assert instance is self.instance def test_slug_related_lookup_does_not_exist(self): - with pytest.raises(ValidationError) as excinfo: + with pytest.raises(serializers.ValidationError) as excinfo: self.field.to_internal_value('doesnotexist') - msg = excinfo.value.messages[0] + msg = excinfo.value.detail[0] assert msg == 'Object with name=doesnotexist does not exist.' def test_slug_related_lookup_invalid_type(self): - with pytest.raises(ValidationError) as excinfo: + with pytest.raises(serializers.ValidationError) as excinfo: self.field.to_internal_value(BadType()) - msg = excinfo.value.messages[0] + msg = excinfo.value.detail[0] assert msg == 'Invalid value.' def test_representation(self): representation = self.field.to_representation(self.instance) assert representation == self.instance.name - -# Older tests, for review... - -# """ -# General tests for relational fields. -# """ -# from __future__ import unicode_literals -# from django import get_version -# from django.db import models -# from django.test import TestCase -# from django.utils import unittest -# from rest_framework import serializers -# from tests.models import BlogPost - - -# class NullModel(models.Model): -# pass - - -# class FieldTests(TestCase): -# def test_pk_related_field_with_empty_string(self): -# """ -# Regression test for #446 - -# https://github.com/tomchristie/django-rest-framework/issues/446 -# """ -# field = serializers.PrimaryKeyRelatedField(queryset=NullModel.objects.all()) -# self.assertRaises(serializers.ValidationError, field.to_primative, '') -# self.assertRaises(serializers.ValidationError, field.to_primative, []) - -# def test_hyperlinked_related_field_with_empty_string(self): -# field = serializers.HyperlinkedRelatedField(queryset=NullModel.objects.all(), view_name='') -# self.assertRaises(serializers.ValidationError, field.to_primative, '') -# self.assertRaises(serializers.ValidationError, field.to_primative, []) - -# def test_slug_related_field_with_empty_string(self): -# field = serializers.SlugRelatedField(queryset=NullModel.objects.all(), slug_field='pk') -# self.assertRaises(serializers.ValidationError, field.to_primative, '') -# self.assertRaises(serializers.ValidationError, field.to_primative, []) - - -# class TestManyRelatedMixin(TestCase): -# def test_missing_many_to_many_related_field(self): -# ''' -# Regression test for #632 - -# https://github.com/tomchristie/django-rest-framework/pull/632 -# ''' -# field = serializers.RelatedField(many=True, read_only=False) - -# into = {} -# field.field_from_native({}, None, 'field_name', into) -# self.assertEqual(into['field_name'], []) - - -# # Regression tests for #694 (`source` attribute on related fields) - -# class RelatedFieldSourceTests(TestCase): -# def test_related_manager_source(self): -# """ -# Relational fields should be able to use manager-returning methods as their source. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.RelatedField(many=True, source='get_blogposts_manager') - -# class ClassWithManagerMethod(object): -# def get_blogposts_manager(self): -# return BlogPost.objects - -# obj = ClassWithManagerMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, ['BlogPost object']) - -# def test_related_queryset_source(self): -# """ -# Relational fields should be able to use queryset-returning methods as their source. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.RelatedField(many=True, source='get_blogposts_queryset') - -# class ClassWithQuerysetMethod(object): -# def get_blogposts_queryset(self): -# return BlogPost.objects.all() - -# obj = ClassWithQuerysetMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, ['BlogPost object']) - -# def test_dotted_source(self): -# """ -# Source argument should support dotted.source notation. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.RelatedField(many=True, source='a.b.c') - -# class ClassWithQuerysetMethod(object): -# a = { -# 'b': { -# 'c': BlogPost.objects.all() -# } -# } - -# obj = ClassWithQuerysetMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, ['BlogPost object']) - -# # Regression for #1129 -# def test_exception_for_incorect_fk(self): -# """ -# Check that the exception message are correct if the source field -# doesn't exist. -# """ -# from tests.models import ManyToManySource - -# class Meta: -# model = ManyToManySource - -# attrs = { -# 'name': serializers.SlugRelatedField( -# slug_field='name', source='banzai'), -# 'Meta': Meta, -# } - -# TestSerializer = type( -# str('TestSerializer'), -# (serializers.ModelSerializer,), -# attrs -# ) -# with self.assertRaises(AttributeError): -# TestSerializer(data={'name': 'foo'}) - - -# @unittest.skipIf(get_version() < '1.6.0', 'Upstream behaviour changed in v1.6') -# class RelatedFieldChoicesTests(TestCase): -# """ -# Tests for #1408 "Web browseable API doesn't have blank option on drop down list box" -# https://github.com/tomchristie/django-rest-framework/issues/1408 -# """ -# def test_blank_option_is_added_to_choice_if_required_equals_false(self): -# """ - -# """ -# post = BlogPost(title="Checking blank option is added") -# post.save() - -# queryset = BlogPost.objects.all() -# field = serializers.RelatedField(required=False, queryset=queryset) - -# choice_count = BlogPost.objects.count() -# widget_count = len(field.widget.choices) - -# self.assertEqual(widget_count, choice_count + 1, 'BLANK_CHOICE_DASH option should have been added') diff --git a/tests/test_relations_generic.py b/tests/test_relations_generic.py new file mode 100644 index 00000000..380ad91d --- /dev/null +++ b/tests/test_relations_generic.py @@ -0,0 +1,104 @@ +from __future__ import unicode_literals +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey +from django.db import models +from django.test import TestCase +from rest_framework import serializers +from rest_framework.compat import python_2_unicode_compatible + + +@python_2_unicode_compatible +class Tag(models.Model): + """ + Tags have a descriptive slug, and are attached to an arbitrary object. + """ + tag = models.SlugField() + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + tagged_item = GenericForeignKey('content_type', 'object_id') + + def __str__(self): + return self.tag + + +@python_2_unicode_compatible +class Bookmark(models.Model): + """ + A URL bookmark that may have multiple tags attached. + """ + url = models.URLField() + tags = GenericRelation(Tag) + + def __str__(self): + return 'Bookmark: %s' % self.url + + +@python_2_unicode_compatible +class Note(models.Model): + """ + A textual note that may have multiple tags attached. + """ + text = models.TextField() + tags = GenericRelation(Tag) + + def __str__(self): + return 'Note: %s' % self.text + + +class TestGenericRelations(TestCase): + def setUp(self): + self.bookmark = Bookmark.objects.create(url='https://www.djangoproject.com/') + Tag.objects.create(tagged_item=self.bookmark, tag='django') + Tag.objects.create(tagged_item=self.bookmark, tag='python') + self.note = Note.objects.create(text='Remember the milk') + Tag.objects.create(tagged_item=self.note, tag='reminder') + + def test_generic_relation(self): + """ + Test a relationship that spans a GenericRelation field. + IE. A reverse generic relationship. + """ + + class BookmarkSerializer(serializers.ModelSerializer): + tags = serializers.StringRelatedField(many=True) + + class Meta: + model = Bookmark + fields = ('tags', 'url') + + serializer = BookmarkSerializer(self.bookmark) + expected = { + 'tags': ['django', 'python'], + 'url': 'https://www.djangoproject.com/' + } + self.assertEqual(serializer.data, expected) + + def test_generic_fk(self): + """ + Test a relationship that spans a GenericForeignKey field. + IE. A forward generic relationship. + """ + + class TagSerializer(serializers.ModelSerializer): + tagged_item = serializers.StringRelatedField() + + class Meta: + model = Tag + fields = ('tag', 'tagged_item') + + serializer = TagSerializer(Tag.objects.all(), many=True) + expected = [ + { + 'tag': 'django', + 'tagged_item': 'Bookmark: https://www.djangoproject.com/' + }, + { + 'tag': 'python', + 'tagged_item': 'Bookmark: https://www.djangoproject.com/' + }, + { + 'tag': 'reminder', + 'tagged_item': 'Note: Remember the milk' + } + ] + self.assertEqual(serializer.data, expected) diff --git a/tests/test_relations_hyperlink.py b/tests/test_relations_hyperlink.py index 315d1abf..b938e385 100644 --- a/tests/test_relations_hyperlink.py +++ b/tests/test_relations_hyperlink.py @@ -1,525 +1,433 @@ -# from __future__ import unicode_literals -# from django.conf.urls import patterns, url -# from django.test import TestCase -# from rest_framework import serializers -# from rest_framework.test import APIRequestFactory -# from tests.models import ( -# BlogPost, -# ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource, -# NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource -# ) - -# factory = APIRequestFactory() -# request = factory.get('/') # Just to ensure we have a request in the serializer context - - -# def dummy_view(request, pk): -# pass - -# urlpatterns = patterns( -# '', -# url(r'^dummyurl/(?P<pk>[0-9]+)/$', dummy_view, name='dummy-url'), -# url(r'^manytomanysource/(?P<pk>[0-9]+)/$', dummy_view, name='manytomanysource-detail'), -# url(r'^manytomanytarget/(?P<pk>[0-9]+)/$', dummy_view, name='manytomanytarget-detail'), -# url(r'^foreignkeysource/(?P<pk>[0-9]+)/$', dummy_view, name='foreignkeysource-detail'), -# url(r'^foreignkeytarget/(?P<pk>[0-9]+)/$', dummy_view, name='foreignkeytarget-detail'), -# url(r'^nullableforeignkeysource/(?P<pk>[0-9]+)/$', dummy_view, name='nullableforeignkeysource-detail'), -# url(r'^onetoonetarget/(?P<pk>[0-9]+)/$', dummy_view, name='onetoonetarget-detail'), -# url(r'^nullableonetoonesource/(?P<pk>[0-9]+)/$', dummy_view, name='nullableonetoonesource-detail'), -# ) - - -# # ManyToMany -# class ManyToManyTargetSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = ManyToManyTarget -# fields = ('url', 'name', 'sources') - - -# class ManyToManySourceSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = ManyToManySource -# fields = ('url', 'name', 'targets') - - -# # ForeignKey -# class ForeignKeyTargetSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = ForeignKeyTarget -# fields = ('url', 'name', 'sources') - - -# class ForeignKeySourceSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = ForeignKeySource -# fields = ('url', 'name', 'target') - - -# # Nullable ForeignKey -# class NullableForeignKeySourceSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = NullableForeignKeySource -# fields = ('url', 'name', 'target') - - -# # Nullable OneToOne -# class NullableOneToOneTargetSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = OneToOneTarget -# fields = ('url', 'name', 'nullable_source') - - -# # TODO: Add test that .data cannot be accessed prior to .is_valid - -# class HyperlinkedManyToManyTests(TestCase): -# urls = 'tests.test_relations_hyperlink' - -# def setUp(self): -# for idx in range(1, 4): -# target = ManyToManyTarget(name='target-%d' % idx) -# target.save() -# source = ManyToManySource(name='source-%d' % idx) -# source.save() -# for target in ManyToManyTarget.objects.all(): -# source.targets.add(target) - -# def test_many_to_many_retrieve(self): -# queryset = ManyToManySource.objects.all() -# serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/']}, -# {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']}, -# {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_many_to_many_retrieve(self): -# queryset = ManyToManyTarget.objects.all() -# serializer = ManyToManyTargetSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, -# {'url': 'http://testserver/manytomanytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, -# {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_many_to_many_update(self): -# data = {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} -# instance = ManyToManySource.objects.get(pk=1) -# serializer = ManyToManySourceSerializer(instance, data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# serializer.save() -# self.assertEqual(serializer.data, data) - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = ManyToManySource.objects.all() -# serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}, -# {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']}, -# {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_many_to_many_update(self): -# data = {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/']} -# instance = ManyToManyTarget.objects.get(pk=1) -# serializer = ManyToManyTargetSerializer(instance, data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# serializer.save() -# self.assertEqual(serializer.data, data) - -# # Ensure target 1 is updated, and everything else is as expected -# queryset = ManyToManyTarget.objects.all() -# serializer = ManyToManyTargetSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/']}, -# {'url': 'http://testserver/manytomanytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, -# {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']} - -# ] -# self.assertEqual(serializer.data, expected) - -# def test_many_to_many_create(self): -# data = {'url': 'http://testserver/manytomanysource/4/', 'name': 'source-4', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/3/']} -# serializer = ManyToManySourceSerializer(data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is added, and everything else is as expected -# queryset = ManyToManySource.objects.all() -# serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/']}, -# {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']}, -# {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}, -# {'url': 'http://testserver/manytomanysource/4/', 'name': 'source-4', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/3/']} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_many_to_many_create(self): -# data = {'url': 'http://testserver/manytomanytarget/4/', 'name': 'target-4', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/3/']} -# serializer = ManyToManyTargetSerializer(data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-4') - -# # Ensure target 4 is added, and everything else is as expected -# queryset = ManyToManyTarget.objects.all() -# serializer = ManyToManyTargetSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, -# {'url': 'http://testserver/manytomanytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, -# {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']}, -# {'url': 'http://testserver/manytomanytarget/4/', 'name': 'target-4', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/3/']} -# ] -# self.assertEqual(serializer.data, expected) - - -# class HyperlinkedForeignKeyTests(TestCase): -# urls = 'tests.test_relations_hyperlink' - -# def setUp(self): -# target = ForeignKeyTarget(name='target-1') -# target.save() -# new_target = ForeignKeyTarget(name='target-2') -# new_target.save() -# for idx in range(1, 4): -# source = ForeignKeySource(name='source-%d' % idx, target=target) -# source.save() - -# def test_foreign_key_retrieve(self): -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_foreign_key_retrieve(self): -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/2/', 'http://testserver/foreignkeysource/3/']}, -# {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update(self): -# data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/2/'} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/2/'}, -# {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_incorrect_type(self): -# data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 2} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'target': ['Incorrect type. Expected url string, received int.']}) - -# def test_reverse_foreign_key_update(self): -# data = {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']} -# instance = ForeignKeyTarget.objects.get(pk=2) -# serializer = ForeignKeyTargetSerializer(instance, data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# # We shouldn't have saved anything to the db yet since save -# # hasn't been called. -# queryset = ForeignKeyTarget.objects.all() -# new_serializer = ForeignKeyTargetSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/2/', 'http://testserver/foreignkeysource/3/']}, -# {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, -# ] -# self.assertEqual(new_serializer.data, expected) - -# serializer.save() -# self.assertEqual(serializer.data, data) - -# # Ensure target 2 is update, and everything else is as expected -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/2/']}, -# {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create(self): -# data = {'url': 'http://testserver/foreignkeysource/4/', 'name': 'source-4', 'target': 'http://testserver/foreignkeytarget/2/'} -# serializer = ForeignKeySourceSerializer(data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/foreignkeysource/4/', 'name': 'source-4', 'target': 'http://testserver/foreignkeytarget/2/'}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_foreign_key_create(self): -# data = {'url': 'http://testserver/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']} -# serializer = ForeignKeyTargetSerializer(data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-3') - -# # Ensure target 4 is added, and everything else is as expected -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/2/']}, -# {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, -# {'url': 'http://testserver/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_invalid_null(self): -# data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': None} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'target': ['This field is required.']}) - - -# class HyperlinkedNullableForeignKeyTests(TestCase): -# urls = 'tests.test_relations_hyperlink' - -# def setUp(self): -# target = ForeignKeyTarget(name='target-1') -# target.save() -# for idx in range(1, 4): -# if idx == 3: -# target = None -# source = NullableForeignKeySource(name='source-%d' % idx, target=target) -# source.save() - -# def test_foreign_key_retrieve_with_null(self): -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create_with_valid_null(self): -# data = {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} -# serializer = NullableForeignKeySourceSerializer(data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is created, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, -# {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create_with_valid_emptystring(self): -# """ -# The emptystring should be interpreted as null in the context -# of relationships. -# """ -# data = {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': ''} -# expected_data = {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} -# serializer = NullableForeignKeySourceSerializer(data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, expected_data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is created, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, -# {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_valid_null(self): -# data = {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None} -# instance = NullableForeignKeySource.objects.get(pk=1) -# serializer = NullableForeignKeySourceSerializer(instance, data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}, -# {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_valid_emptystring(self): -# """ -# The emptystring should be interpreted as null in the context -# of relationships. -# """ -# data = {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': ''} -# expected_data = {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None} -# instance = NullableForeignKeySource.objects.get(pk=1) -# serializer = NullableForeignKeySourceSerializer(instance, data=data, context={'request': request}) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, expected_data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}, -# {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, -# {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, -# ] -# self.assertEqual(serializer.data, expected) - -# # reverse foreign keys MUST be read_only -# # In the general case they do not provide .remove() or .clear() -# # and cannot be arbitrarily set. - -# # def test_reverse_foreign_key_update(self): -# # data = {'id': 1, 'name': 'target-1', 'sources': [1]} -# # instance = ForeignKeyTarget.objects.get(pk=1) -# # serializer = ForeignKeyTargetSerializer(instance, data=data) -# # self.assertTrue(serializer.is_valid()) -# # self.assertEqual(serializer.data, data) -# # serializer.save() - -# # # Ensure target 1 is updated, and everything else is as expected -# # queryset = ForeignKeyTarget.objects.all() -# # serializer = ForeignKeyTargetSerializer(queryset, many=True) -# # expected = [ -# # {'id': 1, 'name': 'target-1', 'sources': [1]}, -# # {'id': 2, 'name': 'target-2', 'sources': []}, -# # ] -# # self.assertEqual(serializer.data, expected) - - -# class HyperlinkedNullableOneToOneTests(TestCase): -# urls = 'tests.test_relations_hyperlink' - -# def setUp(self): -# target = OneToOneTarget(name='target-1') -# target.save() -# new_target = OneToOneTarget(name='target-2') -# new_target.save() -# source = NullableOneToOneSource(name='source-1', target=target) -# source.save() - -# def test_reverse_foreign_key_retrieve_with_null(self): -# queryset = OneToOneTarget.objects.all() -# serializer = NullableOneToOneTargetSerializer(queryset, many=True, context={'request': request}) -# expected = [ -# {'url': 'http://testserver/onetoonetarget/1/', 'name': 'target-1', 'nullable_source': 'http://testserver/nullableonetoonesource/1/'}, -# {'url': 'http://testserver/onetoonetarget/2/', 'name': 'target-2', 'nullable_source': None}, -# ] -# self.assertEqual(serializer.data, expected) - - -# # Regression tests for #694 (`source` attribute on related fields) - -# class HyperlinkedRelatedFieldSourceTests(TestCase): -# urls = 'tests.test_relations_hyperlink' - -# def test_related_manager_source(self): -# """ -# Relational fields should be able to use manager-returning methods as their source. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.HyperlinkedRelatedField( -# many=True, -# source='get_blogposts_manager', -# view_name='dummy-url', -# ) -# field.context = {'request': request} - -# class ClassWithManagerMethod(object): -# def get_blogposts_manager(self): -# return BlogPost.objects - -# obj = ClassWithManagerMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, ['http://testserver/dummyurl/1/']) - -# def test_related_queryset_source(self): -# """ -# Relational fields should be able to use queryset-returning methods as their source. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.HyperlinkedRelatedField( -# many=True, -# source='get_blogposts_queryset', -# view_name='dummy-url', -# ) -# field.context = {'request': request} - -# class ClassWithQuerysetMethod(object): -# def get_blogposts_queryset(self): -# return BlogPost.objects.all() - -# obj = ClassWithQuerysetMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, ['http://testserver/dummyurl/1/']) - -# def test_dotted_source(self): -# """ -# Source argument should support dotted.source notation. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.HyperlinkedRelatedField( -# many=True, -# source='a.b.c', -# view_name='dummy-url', -# ) -# field.context = {'request': request} - -# class ClassWithQuerysetMethod(object): -# a = { -# 'b': { -# 'c': BlogPost.objects.all() -# } -# } - -# obj = ClassWithQuerysetMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, ['http://testserver/dummyurl/1/']) +from __future__ import unicode_literals +from django.conf.urls import patterns, url +from django.test import TestCase +from rest_framework import serializers +from rest_framework.test import APIRequestFactory +from tests.models import ( + ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource, + NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource +) + +factory = APIRequestFactory() +request = factory.get('/') # Just to ensure we have a request in the serializer context + + +dummy_view = lambda request, pk: None + +urlpatterns = patterns( + '', + url(r'^dummyurl/(?P<pk>[0-9]+)/$', dummy_view, name='dummy-url'), + url(r'^manytomanysource/(?P<pk>[0-9]+)/$', dummy_view, name='manytomanysource-detail'), + url(r'^manytomanytarget/(?P<pk>[0-9]+)/$', dummy_view, name='manytomanytarget-detail'), + url(r'^foreignkeysource/(?P<pk>[0-9]+)/$', dummy_view, name='foreignkeysource-detail'), + url(r'^foreignkeytarget/(?P<pk>[0-9]+)/$', dummy_view, name='foreignkeytarget-detail'), + url(r'^nullableforeignkeysource/(?P<pk>[0-9]+)/$', dummy_view, name='nullableforeignkeysource-detail'), + url(r'^onetoonetarget/(?P<pk>[0-9]+)/$', dummy_view, name='onetoonetarget-detail'), + url(r'^nullableonetoonesource/(?P<pk>[0-9]+)/$', dummy_view, name='nullableonetoonesource-detail'), +) + + +# ManyToMany +class ManyToManyTargetSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = ManyToManyTarget + fields = ('url', 'name', 'sources') + + +class ManyToManySourceSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = ManyToManySource + fields = ('url', 'name', 'targets') + + +# ForeignKey +class ForeignKeyTargetSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = ForeignKeyTarget + fields = ('url', 'name', 'sources') + + +class ForeignKeySourceSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = ForeignKeySource + fields = ('url', 'name', 'target') + + +# Nullable ForeignKey +class NullableForeignKeySourceSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = NullableForeignKeySource + fields = ('url', 'name', 'target') + + +# Nullable OneToOne +class NullableOneToOneTargetSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = OneToOneTarget + fields = ('url', 'name', 'nullable_source') + + +# TODO: Add test that .data cannot be accessed prior to .is_valid + +class HyperlinkedManyToManyTests(TestCase): + urls = 'tests.test_relations_hyperlink' + + def setUp(self): + for idx in range(1, 4): + target = ManyToManyTarget(name='target-%d' % idx) + target.save() + source = ManyToManySource(name='source-%d' % idx) + source.save() + for target in ManyToManyTarget.objects.all(): + source.targets.add(target) + + def test_many_to_many_retrieve(self): + queryset = ManyToManySource.objects.all() + serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/']}, + {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']}, + {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_many_to_many_retrieve(self): + queryset = ManyToManyTarget.objects.all() + serializer = ManyToManyTargetSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, + {'url': 'http://testserver/manytomanytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, + {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']} + ] + self.assertEqual(serializer.data, expected) + + def test_many_to_many_update(self): + data = {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} + instance = ManyToManySource.objects.get(pk=1) + serializer = ManyToManySourceSerializer(instance, data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = ManyToManySource.objects.all() + serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}, + {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']}, + {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_many_to_many_update(self): + data = {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/']} + instance = ManyToManyTarget.objects.get(pk=1) + serializer = ManyToManyTargetSerializer(instance, data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure target 1 is updated, and everything else is as expected + queryset = ManyToManyTarget.objects.all() + serializer = ManyToManyTargetSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/']}, + {'url': 'http://testserver/manytomanytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, + {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']} + + ] + self.assertEqual(serializer.data, expected) + + def test_many_to_many_create(self): + data = {'url': 'http://testserver/manytomanysource/4/', 'name': 'source-4', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/3/']} + serializer = ManyToManySourceSerializer(data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is added, and everything else is as expected + queryset = ManyToManySource.objects.all() + serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/']}, + {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']}, + {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}, + {'url': 'http://testserver/manytomanysource/4/', 'name': 'source-4', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/3/']} + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_many_to_many_create(self): + data = {'url': 'http://testserver/manytomanytarget/4/', 'name': 'target-4', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/3/']} + serializer = ManyToManyTargetSerializer(data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'target-4') + + # Ensure target 4 is added, and everything else is as expected + queryset = ManyToManyTarget.objects.all() + serializer = ManyToManyTargetSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/manytomanytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, + {'url': 'http://testserver/manytomanytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']}, + {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']}, + {'url': 'http://testserver/manytomanytarget/4/', 'name': 'target-4', 'sources': ['http://testserver/manytomanysource/1/', 'http://testserver/manytomanysource/3/']} + ] + self.assertEqual(serializer.data, expected) + + +class HyperlinkedForeignKeyTests(TestCase): + urls = 'tests.test_relations_hyperlink' + + def setUp(self): + target = ForeignKeyTarget(name='target-1') + target.save() + new_target = ForeignKeyTarget(name='target-2') + new_target.save() + for idx in range(1, 4): + source = ForeignKeySource(name='source-%d' % idx, target=target) + source.save() + + def test_foreign_key_retrieve(self): + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'} + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_foreign_key_retrieve(self): + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/2/', 'http://testserver/foreignkeysource/3/']}, + {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update(self): + data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/2/'} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/2/'}, + {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_incorrect_type(self): + data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 2} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors, {'target': ['Incorrect type. Expected URL string, received int.']}) + + def test_reverse_foreign_key_update(self): + data = {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']} + instance = ForeignKeyTarget.objects.get(pk=2) + serializer = ForeignKeyTargetSerializer(instance, data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + # We shouldn't have saved anything to the db yet since save + # hasn't been called. + queryset = ForeignKeyTarget.objects.all() + new_serializer = ForeignKeyTargetSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/2/', 'http://testserver/foreignkeysource/3/']}, + {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, + ] + self.assertEqual(new_serializer.data, expected) + + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure target 2 is update, and everything else is as expected + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/2/']}, + {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create(self): + data = {'url': 'http://testserver/foreignkeysource/4/', 'name': 'source-4', 'target': 'http://testserver/foreignkeytarget/2/'} + serializer = ForeignKeySourceSerializer(data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 1 is updated, and everything else is as expected + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/foreignkeysource/4/', 'name': 'source-4', 'target': 'http://testserver/foreignkeytarget/2/'}, + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_foreign_key_create(self): + data = {'url': 'http://testserver/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']} + serializer = ForeignKeyTargetSerializer(data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'target-3') + + # Ensure target 4 is added, and everything else is as expected + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/2/']}, + {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []}, + {'url': 'http://testserver/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/3/']}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_invalid_null(self): + data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': None} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors, {'target': ['This field may not be null.']}) + + +class HyperlinkedNullableForeignKeyTests(TestCase): + urls = 'tests.test_relations_hyperlink' + + def setUp(self): + target = ForeignKeyTarget(name='target-1') + target.save() + for idx in range(1, 4): + if idx == 3: + target = None + source = NullableForeignKeySource(name='source-%d' % idx, target=target) + source.save() + + def test_foreign_key_retrieve_with_null(self): + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create_with_valid_null(self): + data = {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} + serializer = NullableForeignKeySourceSerializer(data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is created, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, + {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create_with_valid_emptystring(self): + """ + The emptystring should be interpreted as null in the context + of relationships. + """ + data = {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': ''} + expected_data = {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} + serializer = NullableForeignKeySourceSerializer(data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, expected_data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is created, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, + {'url': 'http://testserver/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_valid_null(self): + data = {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None} + instance = NullableForeignKeySource.objects.get(pk=1) + serializer = NullableForeignKeySourceSerializer(instance, data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}, + {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_valid_emptystring(self): + """ + The emptystring should be interpreted as null in the context + of relationships. + """ + data = {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': ''} + expected_data = {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None} + instance = NullableForeignKeySource.objects.get(pk=1) + serializer = NullableForeignKeySourceSerializer(instance, data=data, context={'request': request}) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, expected_data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}, + {'url': 'http://testserver/nullableforeignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'}, + {'url': 'http://testserver/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None}, + ] + self.assertEqual(serializer.data, expected) + + +class HyperlinkedNullableOneToOneTests(TestCase): + urls = 'tests.test_relations_hyperlink' + + def setUp(self): + target = OneToOneTarget(name='target-1') + target.save() + new_target = OneToOneTarget(name='target-2') + new_target.save() + source = NullableOneToOneSource(name='source-1', target=target) + source.save() + + def test_reverse_foreign_key_retrieve_with_null(self): + queryset = OneToOneTarget.objects.all() + serializer = NullableOneToOneTargetSerializer(queryset, many=True, context={'request': request}) + expected = [ + {'url': 'http://testserver/onetoonetarget/1/', 'name': 'target-1', 'nullable_source': 'http://testserver/nullableonetoonesource/1/'}, + {'url': 'http://testserver/onetoonetarget/2/', 'name': 'target-2', 'nullable_source': None}, + ] + self.assertEqual(serializer.data, expected) diff --git a/tests/test_relations_nested.py b/tests/test_relations_nested.py deleted file mode 100644 index 4a99fee9..00000000 --- a/tests/test_relations_nested.py +++ /dev/null @@ -1,326 +0,0 @@ -# from __future__ import unicode_literals -# from django.db import models -# from django.test import TestCase -# from rest_framework import serializers - -# from .models import OneToOneTarget - - -# class OneToOneSource(models.Model): -# name = models.CharField(max_length=100) -# target = models.OneToOneField(OneToOneTarget, related_name='source', -# null=True, blank=True) - - -# class OneToManyTarget(models.Model): -# name = models.CharField(max_length=100) - - -# class OneToManySource(models.Model): -# name = models.CharField(max_length=100) -# target = models.ForeignKey(OneToManyTarget, related_name='sources') - - -# class ReverseNestedOneToOneTests(TestCase): -# def setUp(self): -# class OneToOneSourceSerializer(serializers.ModelSerializer): -# class Meta: -# model = OneToOneSource -# fields = ('id', 'name') - -# class OneToOneTargetSerializer(serializers.ModelSerializer): -# source = OneToOneSourceSerializer() - -# class Meta: -# model = OneToOneTarget -# fields = ('id', 'name', 'source') - -# self.Serializer = OneToOneTargetSerializer - -# for idx in range(1, 4): -# target = OneToOneTarget(name='target-%d' % idx) -# target.save() -# source = OneToOneSource(name='source-%d' % idx, target=target) -# source.save() - -# def test_one_to_one_retrieve(self): -# queryset = OneToOneTarget.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'source': {'id': 1, 'name': 'source-1'}}, -# {'id': 2, 'name': 'target-2', 'source': {'id': 2, 'name': 'source-2'}}, -# {'id': 3, 'name': 'target-3', 'source': {'id': 3, 'name': 'source-3'}} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_one_to_one_create(self): -# data = {'id': 4, 'name': 'target-4', 'source': {'id': 4, 'name': 'source-4'}} -# serializer = self.Serializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-4') - -# # Ensure (target 4, target_source 4, source 4) are added, and -# # everything else is as expected. -# queryset = OneToOneTarget.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'source': {'id': 1, 'name': 'source-1'}}, -# {'id': 2, 'name': 'target-2', 'source': {'id': 2, 'name': 'source-2'}}, -# {'id': 3, 'name': 'target-3', 'source': {'id': 3, 'name': 'source-3'}}, -# {'id': 4, 'name': 'target-4', 'source': {'id': 4, 'name': 'source-4'}} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_one_to_one_create_with_invalid_data(self): -# data = {'id': 4, 'name': 'target-4', 'source': {'id': 4}} -# serializer = self.Serializer(data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'source': [{'name': ['This field is required.']}]}) - -# def test_one_to_one_update(self): -# data = {'id': 3, 'name': 'target-3-updated', 'source': {'id': 3, 'name': 'source-3-updated'}} -# instance = OneToOneTarget.objects.get(pk=3) -# serializer = self.Serializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-3-updated') - -# # Ensure (target 3, target_source 3, source 3) are updated, -# # and everything else is as expected. -# queryset = OneToOneTarget.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'source': {'id': 1, 'name': 'source-1'}}, -# {'id': 2, 'name': 'target-2', 'source': {'id': 2, 'name': 'source-2'}}, -# {'id': 3, 'name': 'target-3-updated', 'source': {'id': 3, 'name': 'source-3-updated'}} -# ] -# self.assertEqual(serializer.data, expected) - - -# class ForwardNestedOneToOneTests(TestCase): -# def setUp(self): -# class OneToOneTargetSerializer(serializers.ModelSerializer): -# class Meta: -# model = OneToOneTarget -# fields = ('id', 'name') - -# class OneToOneSourceSerializer(serializers.ModelSerializer): -# target = OneToOneTargetSerializer() - -# class Meta: -# model = OneToOneSource -# fields = ('id', 'name', 'target') - -# self.Serializer = OneToOneSourceSerializer - -# for idx in range(1, 4): -# target = OneToOneTarget(name='target-%d' % idx) -# target.save() -# source = OneToOneSource(name='source-%d' % idx, target=target) -# source.save() - -# def test_one_to_one_retrieve(self): -# queryset = OneToOneSource.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': {'id': 1, 'name': 'target-1'}}, -# {'id': 2, 'name': 'source-2', 'target': {'id': 2, 'name': 'target-2'}}, -# {'id': 3, 'name': 'source-3', 'target': {'id': 3, 'name': 'target-3'}} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_one_to_one_create(self): -# data = {'id': 4, 'name': 'source-4', 'target': {'id': 4, 'name': 'target-4'}} -# serializer = self.Serializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure (target 4, target_source 4, source 4) are added, and -# # everything else is as expected. -# queryset = OneToOneSource.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': {'id': 1, 'name': 'target-1'}}, -# {'id': 2, 'name': 'source-2', 'target': {'id': 2, 'name': 'target-2'}}, -# {'id': 3, 'name': 'source-3', 'target': {'id': 3, 'name': 'target-3'}}, -# {'id': 4, 'name': 'source-4', 'target': {'id': 4, 'name': 'target-4'}} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_one_to_one_create_with_invalid_data(self): -# data = {'id': 4, 'name': 'source-4', 'target': {'id': 4}} -# serializer = self.Serializer(data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'target': [{'name': ['This field is required.']}]}) - -# def test_one_to_one_update(self): -# data = {'id': 3, 'name': 'source-3-updated', 'target': {'id': 3, 'name': 'target-3-updated'}} -# instance = OneToOneSource.objects.get(pk=3) -# serializer = self.Serializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-3-updated') - -# # Ensure (target 3, target_source 3, source 3) are updated, -# # and everything else is as expected. -# queryset = OneToOneSource.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': {'id': 1, 'name': 'target-1'}}, -# {'id': 2, 'name': 'source-2', 'target': {'id': 2, 'name': 'target-2'}}, -# {'id': 3, 'name': 'source-3-updated', 'target': {'id': 3, 'name': 'target-3-updated'}} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_one_to_one_update_to_null(self): -# data = {'id': 3, 'name': 'source-3-updated', 'target': None} -# instance = OneToOneSource.objects.get(pk=3) -# serializer = self.Serializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() - -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-3-updated') -# self.assertEqual(obj.target, None) - -# queryset = OneToOneSource.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': {'id': 1, 'name': 'target-1'}}, -# {'id': 2, 'name': 'source-2', 'target': {'id': 2, 'name': 'target-2'}}, -# {'id': 3, 'name': 'source-3-updated', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# # TODO: Nullable 1-1 tests -# # def test_one_to_one_delete(self): -# # data = {'id': 3, 'name': 'target-3', 'target_source': None} -# # instance = OneToOneTarget.objects.get(pk=3) -# # serializer = self.Serializer(instance, data=data) -# # self.assertTrue(serializer.is_valid()) -# # serializer.save() - -# # # Ensure (target_source 3, source 3) are deleted, -# # # and everything else is as expected. -# # queryset = OneToOneTarget.objects.all() -# # serializer = self.Serializer(queryset) -# # expected = [ -# # {'id': 1, 'name': 'target-1', 'source': {'id': 1, 'name': 'source-1'}}, -# # {'id': 2, 'name': 'target-2', 'source': {'id': 2, 'name': 'source-2'}}, -# # {'id': 3, 'name': 'target-3', 'source': None} -# # ] -# # self.assertEqual(serializer.data, expected) - - -# class ReverseNestedOneToManyTests(TestCase): -# def setUp(self): -# class OneToManySourceSerializer(serializers.ModelSerializer): -# class Meta: -# model = OneToManySource -# fields = ('id', 'name') - -# class OneToManyTargetSerializer(serializers.ModelSerializer): -# sources = OneToManySourceSerializer(many=True, allow_add_remove=True) - -# class Meta: -# model = OneToManyTarget -# fields = ('id', 'name', 'sources') - -# self.Serializer = OneToManyTargetSerializer - -# target = OneToManyTarget(name='target-1') -# target.save() -# for idx in range(1, 4): -# source = OneToManySource(name='source-%d' % idx, target=target) -# source.save() - -# def test_one_to_many_retrieve(self): -# queryset = OneToManyTarget.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [{'id': 1, 'name': 'source-1'}, -# {'id': 2, 'name': 'source-2'}, -# {'id': 3, 'name': 'source-3'}]}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_one_to_many_create(self): -# data = {'id': 1, 'name': 'target-1', 'sources': [{'id': 1, 'name': 'source-1'}, -# {'id': 2, 'name': 'source-2'}, -# {'id': 3, 'name': 'source-3'}, -# {'id': 4, 'name': 'source-4'}]} -# instance = OneToManyTarget.objects.get(pk=1) -# serializer = self.Serializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-1') - -# # Ensure source 4 is added, and everything else is as -# # expected. -# queryset = OneToManyTarget.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [{'id': 1, 'name': 'source-1'}, -# {'id': 2, 'name': 'source-2'}, -# {'id': 3, 'name': 'source-3'}, -# {'id': 4, 'name': 'source-4'}]} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_one_to_many_create_with_invalid_data(self): -# data = {'id': 1, 'name': 'target-1', 'sources': [{'id': 1, 'name': 'source-1'}, -# {'id': 2, 'name': 'source-2'}, -# {'id': 3, 'name': 'source-3'}, -# {'id': 4}]} -# serializer = self.Serializer(data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'sources': [{}, {}, {}, {'name': ['This field is required.']}]}) - -# def test_one_to_many_update(self): -# data = {'id': 1, 'name': 'target-1-updated', 'sources': [{'id': 1, 'name': 'source-1-updated'}, -# {'id': 2, 'name': 'source-2'}, -# {'id': 3, 'name': 'source-3'}]} -# instance = OneToManyTarget.objects.get(pk=1) -# serializer = self.Serializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-1-updated') - -# # Ensure (target 1, source 1) are updated, -# # and everything else is as expected. -# queryset = OneToManyTarget.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1-updated', 'sources': [{'id': 1, 'name': 'source-1-updated'}, -# {'id': 2, 'name': 'source-2'}, -# {'id': 3, 'name': 'source-3'}]} - -# ] -# self.assertEqual(serializer.data, expected) - -# def test_one_to_many_delete(self): -# data = {'id': 1, 'name': 'target-1', 'sources': [{'id': 1, 'name': 'source-1'}, -# {'id': 3, 'name': 'source-3'}]} -# instance = OneToManyTarget.objects.get(pk=1) -# serializer = self.Serializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# serializer.save() - -# # Ensure source 2 is deleted, and everything else is as -# # expected. -# queryset = OneToManyTarget.objects.all() -# serializer = self.Serializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [{'id': 1, 'name': 'source-1'}, -# {'id': 3, 'name': 'source-3'}]} - -# ] -# self.assertEqual(serializer.data, expected) diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py index 031a79b3..e95a877e 100644 --- a/tests/test_relations_pk.py +++ b/tests/test_relations_pk.py @@ -1,551 +1,418 @@ -# from __future__ import unicode_literals -# from django.db import models -# from django.test import TestCase -# from django.utils import six -# from rest_framework import serializers -# from tests.models import ( -# BlogPost, ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource, -# NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource, -# ) - - -# # ManyToMany -# class ManyToManyTargetSerializer(serializers.ModelSerializer): -# class Meta: -# model = ManyToManyTarget -# fields = ('id', 'name', 'sources') - - -# class ManyToManySourceSerializer(serializers.ModelSerializer): -# class Meta: -# model = ManyToManySource -# fields = ('id', 'name', 'targets') - - -# # ForeignKey -# class ForeignKeyTargetSerializer(serializers.ModelSerializer): -# class Meta: -# model = ForeignKeyTarget -# fields = ('id', 'name', 'sources') - - -# class ForeignKeySourceSerializer(serializers.ModelSerializer): -# class Meta: -# model = ForeignKeySource -# fields = ('id', 'name', 'target') - - -# # Nullable ForeignKey -# class NullableForeignKeySourceSerializer(serializers.ModelSerializer): -# class Meta: -# model = NullableForeignKeySource -# fields = ('id', 'name', 'target') - - -# # Nullable OneToOne -# class NullableOneToOneTargetSerializer(serializers.ModelSerializer): -# class Meta: -# model = OneToOneTarget -# fields = ('id', 'name', 'nullable_source') - - -# # TODO: Add test that .data cannot be accessed prior to .is_valid - -# class PKManyToManyTests(TestCase): -# def setUp(self): -# for idx in range(1, 4): -# target = ManyToManyTarget(name='target-%d' % idx) -# target.save() -# source = ManyToManySource(name='source-%d' % idx) -# source.save() -# for target in ManyToManyTarget.objects.all(): -# source.targets.add(target) - -# def test_many_to_many_retrieve(self): -# queryset = ManyToManySource.objects.all() -# serializer = ManyToManySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'targets': [1]}, -# {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, -# {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_many_to_many_retrieve(self): -# queryset = ManyToManyTarget.objects.all() -# serializer = ManyToManyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, -# {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, -# {'id': 3, 'name': 'target-3', 'sources': [3]} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_many_to_many_update(self): -# data = {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]} -# instance = ManyToManySource.objects.get(pk=1) -# serializer = ManyToManySourceSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# serializer.save() -# self.assertEqual(serializer.data, data) - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = ManyToManySource.objects.all() -# serializer = ManyToManySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]}, -# {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, -# {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_many_to_many_update(self): -# data = {'id': 1, 'name': 'target-1', 'sources': [1]} -# instance = ManyToManyTarget.objects.get(pk=1) -# serializer = ManyToManyTargetSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# serializer.save() -# self.assertEqual(serializer.data, data) - -# # Ensure target 1 is updated, and everything else is as expected -# queryset = ManyToManyTarget.objects.all() -# serializer = ManyToManyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [1]}, -# {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, -# {'id': 3, 'name': 'target-3', 'sources': [3]} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_many_to_many_create(self): -# data = {'id': 4, 'name': 'source-4', 'targets': [1, 3]} -# serializer = ManyToManySourceSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is added, and everything else is as expected -# queryset = ManyToManySource.objects.all() -# serializer = ManyToManySourceSerializer(queryset, many=True) -# self.assertFalse(serializer.fields['targets'].read_only) -# expected = [ -# {'id': 1, 'name': 'source-1', 'targets': [1]}, -# {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, -# {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}, -# {'id': 4, 'name': 'source-4', 'targets': [1, 3]}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_many_to_many_create(self): -# data = {'id': 4, 'name': 'target-4', 'sources': [1, 3]} -# serializer = ManyToManyTargetSerializer(data=data) -# self.assertFalse(serializer.fields['sources'].read_only) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-4') - -# # Ensure target 4 is added, and everything else is as expected -# queryset = ManyToManyTarget.objects.all() -# serializer = ManyToManyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, -# {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, -# {'id': 3, 'name': 'target-3', 'sources': [3]}, -# {'id': 4, 'name': 'target-4', 'sources': [1, 3]} -# ] -# self.assertEqual(serializer.data, expected) - - -# class PKForeignKeyTests(TestCase): -# def setUp(self): -# target = ForeignKeyTarget(name='target-1') -# target.save() -# new_target = ForeignKeyTarget(name='target-2') -# new_target.save() -# for idx in range(1, 4): -# source = ForeignKeySource(name='source-%d' % idx, target=target) -# source.save() - -# def test_foreign_key_retrieve(self): -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 1}, -# {'id': 2, 'name': 'source-2', 'target': 1}, -# {'id': 3, 'name': 'source-3', 'target': 1} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_foreign_key_retrieve(self): -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, -# {'id': 2, 'name': 'target-2', 'sources': []}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update(self): -# data = {'id': 1, 'name': 'source-1', 'target': 2} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 2}, -# {'id': 2, 'name': 'source-2', 'target': 1}, -# {'id': 3, 'name': 'source-3', 'target': 1} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_incorrect_type(self): -# data = {'id': 1, 'name': 'source-1', 'target': 'foo'} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'target': ['Incorrect type. Expected pk value, received %s.' % six.text_type.__name__]}) - -# def test_reverse_foreign_key_update(self): -# data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]} -# instance = ForeignKeyTarget.objects.get(pk=2) -# serializer = ForeignKeyTargetSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# # We shouldn't have saved anything to the db yet since save -# # hasn't been called. -# queryset = ForeignKeyTarget.objects.all() -# new_serializer = ForeignKeyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, -# {'id': 2, 'name': 'target-2', 'sources': []}, -# ] -# self.assertEqual(new_serializer.data, expected) - -# serializer.save() -# self.assertEqual(serializer.data, data) - -# # Ensure target 2 is update, and everything else is as expected -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [2]}, -# {'id': 2, 'name': 'target-2', 'sources': [1, 3]}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create(self): -# data = {'id': 4, 'name': 'source-4', 'target': 2} -# serializer = ForeignKeySourceSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is added, and everything else is as expected -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 1}, -# {'id': 2, 'name': 'source-2', 'target': 1}, -# {'id': 3, 'name': 'source-3', 'target': 1}, -# {'id': 4, 'name': 'source-4', 'target': 2}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_foreign_key_create(self): -# data = {'id': 3, 'name': 'target-3', 'sources': [1, 3]} -# serializer = ForeignKeyTargetSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-3') - -# # Ensure target 3 is added, and everything else is as expected -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': [2]}, -# {'id': 2, 'name': 'target-2', 'sources': []}, -# {'id': 3, 'name': 'target-3', 'sources': [1, 3]}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_invalid_null(self): -# data = {'id': 1, 'name': 'source-1', 'target': None} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'target': ['This field is required.']}) - -# def test_foreign_key_with_empty(self): -# """ -# Regression test for #1072 - -# https://github.com/tomchristie/django-rest-framework/issues/1072 -# """ -# serializer = NullableForeignKeySourceSerializer() -# self.assertEqual(serializer.data['target'], None) - - -# class PKNullableForeignKeyTests(TestCase): -# def setUp(self): -# target = ForeignKeyTarget(name='target-1') -# target.save() -# for idx in range(1, 4): -# if idx == 3: -# target = None -# source = NullableForeignKeySource(name='source-%d' % idx, target=target) -# source.save() - -# def test_foreign_key_retrieve_with_null(self): -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 1}, -# {'id': 2, 'name': 'source-2', 'target': 1}, -# {'id': 3, 'name': 'source-3', 'target': None}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create_with_valid_null(self): -# data = {'id': 4, 'name': 'source-4', 'target': None} -# serializer = NullableForeignKeySourceSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is created, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 1}, -# {'id': 2, 'name': 'source-2', 'target': 1}, -# {'id': 3, 'name': 'source-3', 'target': None}, -# {'id': 4, 'name': 'source-4', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create_with_valid_emptystring(self): -# """ -# The emptystring should be interpreted as null in the context -# of relationships. -# """ -# data = {'id': 4, 'name': 'source-4', 'target': ''} -# expected_data = {'id': 4, 'name': 'source-4', 'target': None} -# serializer = NullableForeignKeySourceSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, expected_data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is created, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 1}, -# {'id': 2, 'name': 'source-2', 'target': 1}, -# {'id': 3, 'name': 'source-3', 'target': None}, -# {'id': 4, 'name': 'source-4', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_valid_null(self): -# data = {'id': 1, 'name': 'source-1', 'target': None} -# instance = NullableForeignKeySource.objects.get(pk=1) -# serializer = NullableForeignKeySourceSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': None}, -# {'id': 2, 'name': 'source-2', 'target': 1}, -# {'id': 3, 'name': 'source-3', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_valid_emptystring(self): -# """ -# The emptystring should be interpreted as null in the context -# of relationships. -# """ -# data = {'id': 1, 'name': 'source-1', 'target': ''} -# expected_data = {'id': 1, 'name': 'source-1', 'target': None} -# instance = NullableForeignKeySource.objects.get(pk=1) -# serializer = NullableForeignKeySourceSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, expected_data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': None}, -# {'id': 2, 'name': 'source-2', 'target': 1}, -# {'id': 3, 'name': 'source-3', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# # reverse foreign keys MUST be read_only -# # In the general case they do not provide .remove() or .clear() -# # and cannot be arbitrarily set. - -# # def test_reverse_foreign_key_update(self): -# # data = {'id': 1, 'name': 'target-1', 'sources': [1]} -# # instance = ForeignKeyTarget.objects.get(pk=1) -# # serializer = ForeignKeyTargetSerializer(instance, data=data) -# # self.assertTrue(serializer.is_valid()) -# # self.assertEqual(serializer.data, data) -# # serializer.save() - -# # # Ensure target 1 is updated, and everything else is as expected -# # queryset = ForeignKeyTarget.objects.all() -# # serializer = ForeignKeyTargetSerializer(queryset, many=True) -# # expected = [ -# # {'id': 1, 'name': 'target-1', 'sources': [1]}, -# # {'id': 2, 'name': 'target-2', 'sources': []}, -# # ] -# # self.assertEqual(serializer.data, expected) - - -# class PKNullableOneToOneTests(TestCase): -# def setUp(self): -# target = OneToOneTarget(name='target-1') -# target.save() -# new_target = OneToOneTarget(name='target-2') -# new_target.save() -# 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': None}, -# {'id': 2, 'name': 'target-2', 'nullable_source': 1}, -# ] -# self.assertEqual(serializer.data, expected) - - -# # The below models and tests ensure that serializer fields corresponding -# # to a ManyToManyField field with a user-specified ``through`` model are -# # set to read only - - -# class ManyToManyThroughTarget(models.Model): -# name = models.CharField(max_length=100) - - -# class ManyToManyThrough(models.Model): -# source = models.ForeignKey('ManyToManyThroughSource') -# target = models.ForeignKey(ManyToManyThroughTarget) - - -# class ManyToManyThroughSource(models.Model): -# name = models.CharField(max_length=100) -# targets = models.ManyToManyField(ManyToManyThroughTarget, -# related_name='sources', -# through='ManyToManyThrough') - - -# class ManyToManyThroughTargetSerializer(serializers.ModelSerializer): -# class Meta: -# model = ManyToManyThroughTarget -# fields = ('id', 'name', 'sources') - - -# class ManyToManyThroughSourceSerializer(serializers.ModelSerializer): -# class Meta: -# model = ManyToManyThroughSource -# fields = ('id', 'name', 'targets') - - -# class PKManyToManyThroughTests(TestCase): -# def setUp(self): -# self.source = ManyToManyThroughSource.objects.create( -# name='through-source-1') -# self.target = ManyToManyThroughTarget.objects.create( -# name='through-target-1') - -# def test_many_to_many_create(self): -# data = {'id': 2, 'name': 'source-2', 'targets': [self.target.pk]} -# serializer = ManyToManyThroughSourceSerializer(data=data) -# self.assertTrue(serializer.fields['targets'].read_only) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(obj.name, 'source-2') -# self.assertEqual(obj.targets.count(), 0) - -# def test_many_to_many_reverse_create(self): -# data = {'id': 2, 'name': 'target-2', 'sources': [self.source.pk]} -# serializer = ManyToManyThroughTargetSerializer(data=data) -# self.assertTrue(serializer.fields['sources'].read_only) -# self.assertTrue(serializer.is_valid()) -# serializer.save() -# obj = serializer.save() -# self.assertEqual(obj.name, 'target-2') -# self.assertEqual(obj.sources.count(), 0) - - -# # Regression tests for #694 (`source` attribute on related fields) - - -# class PrimaryKeyRelatedFieldSourceTests(TestCase): -# def test_related_manager_source(self): -# """ -# Relational fields should be able to use manager-returning methods as their source. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.PrimaryKeyRelatedField(many=True, source='get_blogposts_manager') - -# class ClassWithManagerMethod(object): -# def get_blogposts_manager(self): -# return BlogPost.objects - -# obj = ClassWithManagerMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, [1]) - -# def test_related_queryset_source(self): -# """ -# Relational fields should be able to use queryset-returning methods as their source. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.PrimaryKeyRelatedField(many=True, source='get_blogposts_queryset') - -# class ClassWithQuerysetMethod(object): -# def get_blogposts_queryset(self): -# return BlogPost.objects.all() - -# obj = ClassWithQuerysetMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, [1]) - -# def test_dotted_source(self): -# """ -# Source argument should support dotted.source notation. -# """ -# BlogPost.objects.create(title='blah') -# field = serializers.PrimaryKeyRelatedField(many=True, source='a.b.c') - -# class ClassWithQuerysetMethod(object): -# a = { -# 'b': { -# 'c': BlogPost.objects.all() -# } -# } - -# obj = ClassWithQuerysetMethod() -# value = field.field_to_native(obj, 'field_name') -# self.assertEqual(value, [1]) +from __future__ import unicode_literals +from django.test import TestCase +from django.utils import six +from rest_framework import serializers +from tests.models import ( + ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource, + NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource, +) + + +# ManyToMany +class ManyToManyTargetSerializer(serializers.ModelSerializer): + class Meta: + model = ManyToManyTarget + fields = ('id', 'name', 'sources') + + +class ManyToManySourceSerializer(serializers.ModelSerializer): + class Meta: + model = ManyToManySource + fields = ('id', 'name', 'targets') + + +# ForeignKey +class ForeignKeyTargetSerializer(serializers.ModelSerializer): + class Meta: + model = ForeignKeyTarget + fields = ('id', 'name', 'sources') + + +class ForeignKeySourceSerializer(serializers.ModelSerializer): + class Meta: + model = ForeignKeySource + fields = ('id', 'name', 'target') + + +# Nullable ForeignKey +class NullableForeignKeySourceSerializer(serializers.ModelSerializer): + class Meta: + model = NullableForeignKeySource + fields = ('id', 'name', 'target') + + +# Nullable OneToOne +class NullableOneToOneTargetSerializer(serializers.ModelSerializer): + class Meta: + model = OneToOneTarget + fields = ('id', 'name', 'nullable_source') + + +# TODO: Add test that .data cannot be accessed prior to .is_valid + +class PKManyToManyTests(TestCase): + def setUp(self): + for idx in range(1, 4): + target = ManyToManyTarget(name='target-%d' % idx) + target.save() + source = ManyToManySource(name='source-%d' % idx) + source.save() + for target in ManyToManyTarget.objects.all(): + source.targets.add(target) + + def test_many_to_many_retrieve(self): + queryset = ManyToManySource.objects.all() + serializer = ManyToManySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'targets': [1]}, + {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, + {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]} + ] + with self.assertNumQueries(4): + self.assertEqual(serializer.data, expected) + + def test_reverse_many_to_many_retrieve(self): + queryset = ManyToManyTarget.objects.all() + serializer = ManyToManyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, + {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, + {'id': 3, 'name': 'target-3', 'sources': [3]} + ] + with self.assertNumQueries(4): + self.assertEqual(serializer.data, expected) + + def test_many_to_many_update(self): + data = {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]} + instance = ManyToManySource.objects.get(pk=1) + serializer = ManyToManySourceSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = ManyToManySource.objects.all() + serializer = ManyToManySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]}, + {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, + {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]} + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_many_to_many_update(self): + data = {'id': 1, 'name': 'target-1', 'sources': [1]} + instance = ManyToManyTarget.objects.get(pk=1) + serializer = ManyToManyTargetSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure target 1 is updated, and everything else is as expected + queryset = ManyToManyTarget.objects.all() + serializer = ManyToManyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': [1]}, + {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, + {'id': 3, 'name': 'target-3', 'sources': [3]} + ] + self.assertEqual(serializer.data, expected) + + def test_many_to_many_create(self): + data = {'id': 4, 'name': 'source-4', 'targets': [1, 3]} + serializer = ManyToManySourceSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is added, and everything else is as expected + queryset = ManyToManySource.objects.all() + serializer = ManyToManySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'targets': [1]}, + {'id': 2, 'name': 'source-2', 'targets': [1, 2]}, + {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}, + {'id': 4, 'name': 'source-4', 'targets': [1, 3]}, + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_many_to_many_create(self): + data = {'id': 4, 'name': 'target-4', 'sources': [1, 3]} + serializer = ManyToManyTargetSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'target-4') + + # Ensure target 4 is added, and everything else is as expected + queryset = ManyToManyTarget.objects.all() + serializer = ManyToManyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, + {'id': 2, 'name': 'target-2', 'sources': [2, 3]}, + {'id': 3, 'name': 'target-3', 'sources': [3]}, + {'id': 4, 'name': 'target-4', 'sources': [1, 3]} + ] + self.assertEqual(serializer.data, expected) + + +class PKForeignKeyTests(TestCase): + def setUp(self): + target = ForeignKeyTarget(name='target-1') + target.save() + new_target = ForeignKeyTarget(name='target-2') + new_target.save() + for idx in range(1, 4): + source = ForeignKeySource(name='source-%d' % idx, target=target) + source.save() + + def test_foreign_key_retrieve(self): + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 1}, + {'id': 2, 'name': 'source-2', 'target': 1}, + {'id': 3, 'name': 'source-3', 'target': 1} + ] + with self.assertNumQueries(1): + self.assertEqual(serializer.data, expected) + + def test_reverse_foreign_key_retrieve(self): + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, + {'id': 2, 'name': 'target-2', 'sources': []}, + ] + with self.assertNumQueries(3): + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update(self): + data = {'id': 1, 'name': 'source-1', 'target': 2} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 2}, + {'id': 2, 'name': 'source-2', 'target': 1}, + {'id': 3, 'name': 'source-3', 'target': 1} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_incorrect_type(self): + data = {'id': 1, 'name': 'source-1', 'target': 'foo'} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data) + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors, {'target': ['Incorrect type. Expected pk value, received %s.' % six.text_type.__name__]}) + + def test_reverse_foreign_key_update(self): + data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]} + instance = ForeignKeyTarget.objects.get(pk=2) + serializer = ForeignKeyTargetSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + # We shouldn't have saved anything to the db yet since save + # hasn't been called. + queryset = ForeignKeyTarget.objects.all() + new_serializer = ForeignKeyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]}, + {'id': 2, 'name': 'target-2', 'sources': []}, + ] + self.assertEqual(new_serializer.data, expected) + + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure target 2 is update, and everything else is as expected + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': [2]}, + {'id': 2, 'name': 'target-2', 'sources': [1, 3]}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create(self): + data = {'id': 4, 'name': 'source-4', 'target': 2} + serializer = ForeignKeySourceSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is added, and everything else is as expected + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 1}, + {'id': 2, 'name': 'source-2', 'target': 1}, + {'id': 3, 'name': 'source-3', 'target': 1}, + {'id': 4, 'name': 'source-4', 'target': 2}, + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_foreign_key_create(self): + data = {'id': 3, 'name': 'target-3', 'sources': [1, 3]} + serializer = ForeignKeyTargetSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'target-3') + + # Ensure target 3 is added, and everything else is as expected + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': [2]}, + {'id': 2, 'name': 'target-2', 'sources': []}, + {'id': 3, 'name': 'target-3', 'sources': [1, 3]}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_invalid_null(self): + data = {'id': 1, 'name': 'source-1', 'target': None} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data) + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors, {'target': ['This field may not be null.']}) + + def test_foreign_key_with_empty(self): + """ + Regression test for #1072 + + https://github.com/tomchristie/django-rest-framework/issues/1072 + """ + serializer = NullableForeignKeySourceSerializer() + self.assertEqual(serializer.data['target'], None) + + +class PKNullableForeignKeyTests(TestCase): + def setUp(self): + target = ForeignKeyTarget(name='target-1') + target.save() + for idx in range(1, 4): + if idx == 3: + target = None + source = NullableForeignKeySource(name='source-%d' % idx, target=target) + source.save() + + def test_foreign_key_retrieve_with_null(self): + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 1}, + {'id': 2, 'name': 'source-2', 'target': 1}, + {'id': 3, 'name': 'source-3', 'target': None}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create_with_valid_null(self): + data = {'id': 4, 'name': 'source-4', 'target': None} + serializer = NullableForeignKeySourceSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is created, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 1}, + {'id': 2, 'name': 'source-2', 'target': 1}, + {'id': 3, 'name': 'source-3', 'target': None}, + {'id': 4, 'name': 'source-4', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create_with_valid_emptystring(self): + """ + The emptystring should be interpreted as null in the context + of relationships. + """ + data = {'id': 4, 'name': 'source-4', 'target': ''} + expected_data = {'id': 4, 'name': 'source-4', 'target': None} + serializer = NullableForeignKeySourceSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, expected_data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is created, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 1}, + {'id': 2, 'name': 'source-2', 'target': 1}, + {'id': 3, 'name': 'source-3', 'target': None}, + {'id': 4, 'name': 'source-4', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_valid_null(self): + data = {'id': 1, 'name': 'source-1', 'target': None} + instance = NullableForeignKeySource.objects.get(pk=1) + serializer = NullableForeignKeySourceSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': None}, + {'id': 2, 'name': 'source-2', 'target': 1}, + {'id': 3, 'name': 'source-3', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_valid_emptystring(self): + """ + The emptystring should be interpreted as null in the context + of relationships. + """ + data = {'id': 1, 'name': 'source-1', 'target': ''} + expected_data = {'id': 1, 'name': 'source-1', 'target': None} + instance = NullableForeignKeySource.objects.get(pk=1) + serializer = NullableForeignKeySourceSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, expected_data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': None}, + {'id': 2, 'name': 'source-2', 'target': 1}, + {'id': 3, 'name': 'source-3', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + +class PKNullableOneToOneTests(TestCase): + def setUp(self): + target = OneToOneTarget(name='target-1') + target.save() + new_target = OneToOneTarget(name='target-2') + new_target.save() + 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': None}, + {'id': 2, 'name': 'target-2', 'nullable_source': 1}, + ] + self.assertEqual(serializer.data, expected) diff --git a/tests/test_relations_slug.py b/tests/test_relations_slug.py index f7a59a95..7bac9046 100644 --- a/tests/test_relations_slug.py +++ b/tests/test_relations_slug.py @@ -1,257 +1,268 @@ -# from django.test import TestCase -# from rest_framework import serializers -# from tests.models import NullableForeignKeySource, ForeignKeySource, ForeignKeyTarget - - -# class ForeignKeyTargetSerializer(serializers.ModelSerializer): -# sources = serializers.SlugRelatedField(many=True, slug_field='name') - -# class Meta: -# model = ForeignKeyTarget - - -# class ForeignKeySourceSerializer(serializers.ModelSerializer): -# target = serializers.SlugRelatedField(slug_field='name') - -# class Meta: -# model = ForeignKeySource - - -# class NullableForeignKeySourceSerializer(serializers.ModelSerializer): -# target = serializers.SlugRelatedField(slug_field='name', required=False) - -# class Meta: -# model = NullableForeignKeySource - - -# # TODO: M2M Tests, FKTests (Non-nullable), One2One -# class SlugForeignKeyTests(TestCase): -# def setUp(self): -# target = ForeignKeyTarget(name='target-1') -# target.save() -# new_target = ForeignKeyTarget(name='target-2') -# new_target.save() -# for idx in range(1, 4): -# source = ForeignKeySource(name='source-%d' % idx, target=target) -# source.save() - -# def test_foreign_key_retrieve(self): -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 'target-1'}, -# {'id': 2, 'name': 'source-2', 'target': 'target-1'}, -# {'id': 3, 'name': 'source-3', 'target': 'target-1'} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_foreign_key_retrieve(self): -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': ['source-1', 'source-2', 'source-3']}, -# {'id': 2, 'name': 'target-2', 'sources': []}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update(self): -# data = {'id': 1, 'name': 'source-1', 'target': 'target-2'} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 'target-2'}, -# {'id': 2, 'name': 'source-2', 'target': 'target-1'}, -# {'id': 3, 'name': 'source-3', 'target': 'target-1'} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_incorrect_type(self): -# data = {'id': 1, 'name': 'source-1', 'target': 123} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'target': ['Object with name=123 does not exist.']}) - -# def test_reverse_foreign_key_update(self): -# data = {'id': 2, 'name': 'target-2', 'sources': ['source-1', 'source-3']} -# instance = ForeignKeyTarget.objects.get(pk=2) -# serializer = ForeignKeyTargetSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# # We shouldn't have saved anything to the db yet since save -# # hasn't been called. -# queryset = ForeignKeyTarget.objects.all() -# new_serializer = ForeignKeyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': ['source-1', 'source-2', 'source-3']}, -# {'id': 2, 'name': 'target-2', 'sources': []}, -# ] -# self.assertEqual(new_serializer.data, expected) - -# serializer.save() -# self.assertEqual(serializer.data, data) - -# # Ensure target 2 is update, and everything else is as expected -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': ['source-2']}, -# {'id': 2, 'name': 'target-2', 'sources': ['source-1', 'source-3']}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create(self): -# data = {'id': 4, 'name': 'source-4', 'target': 'target-2'} -# serializer = ForeignKeySourceSerializer(data=data) -# serializer.is_valid() -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is added, and everything else is as expected -# queryset = ForeignKeySource.objects.all() -# serializer = ForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 'target-1'}, -# {'id': 2, 'name': 'source-2', 'target': 'target-1'}, -# {'id': 3, 'name': 'source-3', 'target': 'target-1'}, -# {'id': 4, 'name': 'source-4', 'target': 'target-2'}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_reverse_foreign_key_create(self): -# data = {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']} -# serializer = ForeignKeyTargetSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'target-3') - -# # Ensure target 3 is added, and everything else is as expected -# queryset = ForeignKeyTarget.objects.all() -# serializer = ForeignKeyTargetSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'target-1', 'sources': ['source-2']}, -# {'id': 2, 'name': 'target-2', 'sources': []}, -# {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_invalid_null(self): -# data = {'id': 1, 'name': 'source-1', 'target': None} -# instance = ForeignKeySource.objects.get(pk=1) -# serializer = ForeignKeySourceSerializer(instance, data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'target': ['This field is required.']}) - - -# class SlugNullableForeignKeyTests(TestCase): -# def setUp(self): -# target = ForeignKeyTarget(name='target-1') -# target.save() -# for idx in range(1, 4): -# if idx == 3: -# target = None -# source = NullableForeignKeySource(name='source-%d' % idx, target=target) -# source.save() - -# def test_foreign_key_retrieve_with_null(self): -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 'target-1'}, -# {'id': 2, 'name': 'source-2', 'target': 'target-1'}, -# {'id': 3, 'name': 'source-3', 'target': None}, -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create_with_valid_null(self): -# data = {'id': 4, 'name': 'source-4', 'target': None} -# serializer = NullableForeignKeySourceSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is created, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 'target-1'}, -# {'id': 2, 'name': 'source-2', 'target': 'target-1'}, -# {'id': 3, 'name': 'source-3', 'target': None}, -# {'id': 4, 'name': 'source-4', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_create_with_valid_emptystring(self): -# """ -# The emptystring should be interpreted as null in the context -# of relationships. -# """ -# data = {'id': 4, 'name': 'source-4', 'target': ''} -# expected_data = {'id': 4, 'name': 'source-4', 'target': None} -# serializer = NullableForeignKeySourceSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# obj = serializer.save() -# self.assertEqual(serializer.data, expected_data) -# self.assertEqual(obj.name, 'source-4') - -# # Ensure source 4 is created, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': 'target-1'}, -# {'id': 2, 'name': 'source-2', 'target': 'target-1'}, -# {'id': 3, 'name': 'source-3', 'target': None}, -# {'id': 4, 'name': 'source-4', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_valid_null(self): -# data = {'id': 1, 'name': 'source-1', 'target': None} -# instance = NullableForeignKeySource.objects.get(pk=1) -# serializer = NullableForeignKeySourceSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': None}, -# {'id': 2, 'name': 'source-2', 'target': 'target-1'}, -# {'id': 3, 'name': 'source-3', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) - -# def test_foreign_key_update_with_valid_emptystring(self): -# """ -# The emptystring should be interpreted as null in the context -# of relationships. -# """ -# data = {'id': 1, 'name': 'source-1', 'target': ''} -# expected_data = {'id': 1, 'name': 'source-1', 'target': None} -# instance = NullableForeignKeySource.objects.get(pk=1) -# serializer = NullableForeignKeySourceSerializer(instance, data=data) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, expected_data) -# serializer.save() - -# # Ensure source 1 is updated, and everything else is as expected -# queryset = NullableForeignKeySource.objects.all() -# serializer = NullableForeignKeySourceSerializer(queryset, many=True) -# expected = [ -# {'id': 1, 'name': 'source-1', 'target': None}, -# {'id': 2, 'name': 'source-2', 'target': 'target-1'}, -# {'id': 3, 'name': 'source-3', 'target': None} -# ] -# self.assertEqual(serializer.data, expected) +from django.test import TestCase +from rest_framework import serializers +from tests.models import NullableForeignKeySource, ForeignKeySource, ForeignKeyTarget + + +class ForeignKeyTargetSerializer(serializers.ModelSerializer): + sources = serializers.SlugRelatedField( + slug_field='name', + queryset=ForeignKeySource.objects.all(), + many=True + ) + + class Meta: + model = ForeignKeyTarget + + +class ForeignKeySourceSerializer(serializers.ModelSerializer): + target = serializers.SlugRelatedField( + slug_field='name', + queryset=ForeignKeyTarget.objects.all() + ) + + class Meta: + model = ForeignKeySource + + +class NullableForeignKeySourceSerializer(serializers.ModelSerializer): + target = serializers.SlugRelatedField( + slug_field='name', + queryset=ForeignKeyTarget.objects.all(), + allow_null=True + ) + + class Meta: + model = NullableForeignKeySource + + +# TODO: M2M Tests, FKTests (Non-nullable), One2One +class SlugForeignKeyTests(TestCase): + def setUp(self): + target = ForeignKeyTarget(name='target-1') + target.save() + new_target = ForeignKeyTarget(name='target-2') + new_target.save() + for idx in range(1, 4): + source = ForeignKeySource(name='source-%d' % idx, target=target) + source.save() + + def test_foreign_key_retrieve(self): + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 'target-1'}, + {'id': 2, 'name': 'source-2', 'target': 'target-1'}, + {'id': 3, 'name': 'source-3', 'target': 'target-1'} + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_foreign_key_retrieve(self): + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': ['source-1', 'source-2', 'source-3']}, + {'id': 2, 'name': 'target-2', 'sources': []}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update(self): + data = {'id': 1, 'name': 'source-1', 'target': 'target-2'} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 'target-2'}, + {'id': 2, 'name': 'source-2', 'target': 'target-1'}, + {'id': 3, 'name': 'source-3', 'target': 'target-1'} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_incorrect_type(self): + data = {'id': 1, 'name': 'source-1', 'target': 123} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data) + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors, {'target': ['Object with name=123 does not exist.']}) + + def test_reverse_foreign_key_update(self): + data = {'id': 2, 'name': 'target-2', 'sources': ['source-1', 'source-3']} + instance = ForeignKeyTarget.objects.get(pk=2) + serializer = ForeignKeyTargetSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + # We shouldn't have saved anything to the db yet since save + # hasn't been called. + queryset = ForeignKeyTarget.objects.all() + new_serializer = ForeignKeyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': ['source-1', 'source-2', 'source-3']}, + {'id': 2, 'name': 'target-2', 'sources': []}, + ] + self.assertEqual(new_serializer.data, expected) + + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure target 2 is update, and everything else is as expected + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': ['source-2']}, + {'id': 2, 'name': 'target-2', 'sources': ['source-1', 'source-3']}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create(self): + data = {'id': 4, 'name': 'source-4', 'target': 'target-2'} + serializer = ForeignKeySourceSerializer(data=data) + serializer.is_valid() + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is added, and everything else is as expected + queryset = ForeignKeySource.objects.all() + serializer = ForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 'target-1'}, + {'id': 2, 'name': 'source-2', 'target': 'target-1'}, + {'id': 3, 'name': 'source-3', 'target': 'target-1'}, + {'id': 4, 'name': 'source-4', 'target': 'target-2'}, + ] + self.assertEqual(serializer.data, expected) + + def test_reverse_foreign_key_create(self): + data = {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']} + serializer = ForeignKeyTargetSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'target-3') + + # Ensure target 3 is added, and everything else is as expected + queryset = ForeignKeyTarget.objects.all() + serializer = ForeignKeyTargetSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'target-1', 'sources': ['source-2']}, + {'id': 2, 'name': 'target-2', 'sources': []}, + {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_invalid_null(self): + data = {'id': 1, 'name': 'source-1', 'target': None} + instance = ForeignKeySource.objects.get(pk=1) + serializer = ForeignKeySourceSerializer(instance, data=data) + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors, {'target': ['This field may not be null.']}) + + +class SlugNullableForeignKeyTests(TestCase): + def setUp(self): + target = ForeignKeyTarget(name='target-1') + target.save() + for idx in range(1, 4): + if idx == 3: + target = None + source = NullableForeignKeySource(name='source-%d' % idx, target=target) + source.save() + + def test_foreign_key_retrieve_with_null(self): + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 'target-1'}, + {'id': 2, 'name': 'source-2', 'target': 'target-1'}, + {'id': 3, 'name': 'source-3', 'target': None}, + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create_with_valid_null(self): + data = {'id': 4, 'name': 'source-4', 'target': None} + serializer = NullableForeignKeySourceSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is created, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 'target-1'}, + {'id': 2, 'name': 'source-2', 'target': 'target-1'}, + {'id': 3, 'name': 'source-3', 'target': None}, + {'id': 4, 'name': 'source-4', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_create_with_valid_emptystring(self): + """ + The emptystring should be interpreted as null in the context + of relationships. + """ + data = {'id': 4, 'name': 'source-4', 'target': ''} + expected_data = {'id': 4, 'name': 'source-4', 'target': None} + serializer = NullableForeignKeySourceSerializer(data=data) + self.assertTrue(serializer.is_valid()) + obj = serializer.save() + self.assertEqual(serializer.data, expected_data) + self.assertEqual(obj.name, 'source-4') + + # Ensure source 4 is created, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': 'target-1'}, + {'id': 2, 'name': 'source-2', 'target': 'target-1'}, + {'id': 3, 'name': 'source-3', 'target': None}, + {'id': 4, 'name': 'source-4', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_valid_null(self): + data = {'id': 1, 'name': 'source-1', 'target': None} + instance = NullableForeignKeySource.objects.get(pk=1) + serializer = NullableForeignKeySourceSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': None}, + {'id': 2, 'name': 'source-2', 'target': 'target-1'}, + {'id': 3, 'name': 'source-3', 'target': None} + ] + self.assertEqual(serializer.data, expected) + + def test_foreign_key_update_with_valid_emptystring(self): + """ + The emptystring should be interpreted as null in the context + of relationships. + """ + data = {'id': 1, 'name': 'source-1', 'target': ''} + expected_data = {'id': 1, 'name': 'source-1', 'target': None} + instance = NullableForeignKeySource.objects.get(pk=1) + serializer = NullableForeignKeySourceSerializer(instance, data=data) + self.assertTrue(serializer.is_valid()) + serializer.save() + self.assertEqual(serializer.data, expected_data) + + # Ensure source 1 is updated, and everything else is as expected + queryset = NullableForeignKeySource.objects.all() + serializer = NullableForeignKeySourceSerializer(queryset, many=True) + expected = [ + {'id': 1, 'name': 'source-1', 'target': None}, + {'id': 2, 'name': 'source-2', 'target': 'target-1'}, + {'id': 3, 'name': 'source-3', 'target': None} + ] + self.assertEqual(serializer.data, expected) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index a8fd5f46..416d7f22 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -9,7 +9,7 @@ from django.test import TestCase from django.utils import six, unittest from django.utils.translation import ugettext_lazy as _ from rest_framework import status, permissions -from rest_framework.compat import yaml, etree, StringIO +from rest_framework.compat import yaml, etree, StringIO, BytesIO from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \ @@ -467,7 +467,7 @@ if yaml: obj = {'foo': ['bar', 'baz']} renderer = YAMLRenderer() content = renderer.render(obj, 'application/yaml') - self.assertEqual(content, _yaml_repr) + self.assertEqual(content.decode('utf-8'), _yaml_repr) def test_render_and_parse(self): """ @@ -480,7 +480,7 @@ if yaml: parser = YAMLParser() content = renderer.render(obj, 'application/yaml') - data = parser.parse(StringIO(content)) + data = parser.parse(BytesIO(content)) self.assertEqual(obj, data) def test_render_decimal(self): @@ -489,7 +489,7 @@ if yaml: """ renderer = YAMLRenderer() content = renderer.render({'field': Decimal('111.2')}, 'application/yaml') - self.assertYAMLContains(content, "field: '111.2'") + self.assertYAMLContains(content.decode('utf-8'), "field: '111.2'") def assertYAMLContains(self, content, string): self.assertTrue(string in content, '%r not in %r' % (string, content)) @@ -646,6 +646,7 @@ class CacheRenderTest(TestCase): """ method = getattr(self.client, http_method) resp = method(url) + resp._closable_objects = [] del resp.client, resp.request try: del resp.wsgi_request diff --git a/tests/test_request.py b/tests/test_request.py index 8ddaf0a7..44afd243 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -179,89 +179,6 @@ class TestContentParsing(TestCase): self.assertEqual(request._data, Empty) self.assertEqual(request._files, Empty) - # def test_accessing_post_after_data_form(self): - # """ - # Ensures request.POST can be accessed after request.DATA in - # form request. - # """ - # data = {'qwerty': 'uiop'} - # request = factory.post('/', data=data) - # self.assertEqual(request.DATA.items(), data.items()) - # self.assertEqual(request.POST.items(), data.items()) - - # def test_accessing_post_after_data_for_json(self): - # """ - # Ensures request.POST can be accessed after request.DATA in - # json request. - # """ - # data = {'qwerty': 'uiop'} - # content = json.dumps(data) - # content_type = 'application/json' - # parsers = (JSONParser, ) - - # request = factory.post('/', content, content_type=content_type, - # parsers=parsers) - # self.assertEqual(request.DATA.items(), data.items()) - # self.assertEqual(request.POST.items(), []) - - # def test_accessing_post_after_data_for_overloaded_json(self): - # """ - # Ensures request.POST can be accessed after request.DATA in overloaded - # json request. - # """ - # data = {'qwerty': 'uiop'} - # content = json.dumps(data) - # content_type = 'application/json' - # parsers = (JSONParser, ) - # form_data = {Request._CONTENT_PARAM: content, - # Request._CONTENTTYPE_PARAM: content_type} - - # request = factory.post('/', form_data, parsers=parsers) - # self.assertEqual(request.DATA.items(), data.items()) - # self.assertEqual(request.POST.items(), form_data.items()) - - # def test_accessing_data_after_post_form(self): - # """ - # Ensures request.DATA can be accessed after request.POST in - # form request. - # """ - # data = {'qwerty': 'uiop'} - # parsers = (FormParser, MultiPartParser) - # request = factory.post('/', data, parsers=parsers) - - # self.assertEqual(request.POST.items(), data.items()) - # self.assertEqual(request.DATA.items(), data.items()) - - # def test_accessing_data_after_post_for_json(self): - # """ - # Ensures request.DATA can be accessed after request.POST in - # json request. - # """ - # data = {'qwerty': 'uiop'} - # content = json.dumps(data) - # content_type = 'application/json' - # parsers = (JSONParser, ) - # request = factory.post('/', content, content_type=content_type, - # parsers=parsers) - # self.assertEqual(request.POST.items(), []) - # self.assertEqual(request.DATA.items(), data.items()) - - # def test_accessing_data_after_post_for_overloaded_json(self): - # """ - # Ensures request.DATA can be accessed after request.POST in overloaded - # json request - # """ - # data = {'qwerty': 'uiop'} - # content = json.dumps(data) - # content_type = 'application/json' - # parsers = (JSONParser, ) - # form_data = {Request._CONTENT_PARAM: content, - # Request._CONTENTTYPE_PARAM: content_type} - - # request = factory.post('/', form_data, parsers=parsers) - # self.assertEqual(request.POST.items(), form_data.items()) - # self.assertEqual(request.DATA.items(), data.items()) - class MockView(APIView): authentication_classes = (SessionAuthentication,) @@ -301,18 +218,6 @@ class TestContentParsingWithAuthentication(TestCase): response = self.csrf_client.post('/', content) self.assertEqual(status.HTTP_200_OK, response.status_code) - # def test_user_logged_in_authentication_has_post_when_logged_in(self): - # """Ensures request.POST exists after UserLoggedInAuthentication when user does log in""" - # self.client.login(username='john', password='password') - # self.csrf_client.login(username='john', password='password') - # content = {'example': 'example'} - - # response = self.client.post('/', content) - # self.assertEqual(status.OK, response.status_code, "POST data is malformed") - - # response = self.csrf_client.post('/', content) - # self.assertEqual(status.OK, response.status_code, "POST data is malformed") - class TestUserSetter(TestCase): diff --git a/tests/test_response.py b/tests/test_response.py index 84c39c1a..f233ae33 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -262,9 +262,9 @@ class Issue807Tests(TestCase): expected = "{0}; charset={1}".format(RendererC.media_type, RendererC.charset) self.assertEqual(expected, resp['Content-Type']) - def test_content_type_set_explictly_on_response(self): + def test_content_type_set_explicitly_on_response(self): """ - The content type may be set explictly on the response. + The content type may be set explicitly on the response. """ headers = {"HTTP_ACCEPT": RendererC.media_type} resp = self.client.get('/setbyview', **headers) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index b0eb4e27..6dabaf42 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -1,2004 +1,177 @@ -# # -*- coding: utf-8 -*- -# from __future__ import unicode_literals -# from django.db import models -# from django.db.models.fields import BLANK_CHOICE_DASH -# from django.test import TestCase -# from django.utils import unittest -# from django.utils.datastructures import MultiValueDict -# from django.utils.translation import ugettext_lazy as _ -# from rest_framework import serializers, fields, relations -# from tests.models import ( -# HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel, -# BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, -# DefaultValueModel, ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, -# RESTFrameworkModel, ForeignKeySource -# ) -# from tests.models import BasicModelSerializer -# import datetime -# import pickle -# try: -# import PIL -# except: -# PIL = None - - -# if PIL is not None: -# class AMOAFModel(RESTFrameworkModel): -# char_field = models.CharField(max_length=1024, blank=True) -# comma_separated_integer_field = models.CommaSeparatedIntegerField(max_length=1024, blank=True) -# decimal_field = models.DecimalField(max_digits=64, decimal_places=32, blank=True) -# email_field = models.EmailField(max_length=1024, blank=True) -# file_field = models.FileField(upload_to='test', max_length=1024, blank=True) -# image_field = models.ImageField(upload_to='test', max_length=1024, blank=True) -# slug_field = models.SlugField(max_length=1024, blank=True) -# url_field = models.URLField(max_length=1024, blank=True) -# nullable_char_field = models.CharField(max_length=1024, blank=True, null=True) - -# class DVOAFModel(RESTFrameworkModel): -# positive_integer_field = models.PositiveIntegerField(blank=True) -# positive_small_integer_field = models.PositiveSmallIntegerField(blank=True) -# email_field = models.EmailField(blank=True) -# file_field = models.FileField(upload_to='test', blank=True) -# image_field = models.ImageField(upload_to='test', blank=True) -# slug_field = models.SlugField(blank=True) -# url_field = models.URLField(blank=True) - - -# class SubComment(object): -# def __init__(self, sub_comment): -# self.sub_comment = sub_comment - - -# class Comment(object): -# def __init__(self, email, content, created): -# self.email = email -# self.content = content -# self.created = created or datetime.datetime.now() - -# def __eq__(self, other): -# return all([getattr(self, attr) == getattr(other, attr) -# for attr in ('email', 'content', 'created')]) - -# def get_sub_comment(self): -# sub_comment = SubComment('And Merry Christmas!') -# return sub_comment - - -# class CommentSerializer(serializers.Serializer): -# email = serializers.EmailField() -# content = serializers.CharField(max_length=1000) -# created = serializers.DateTimeField() -# sub_comment = serializers.Field(source='get_sub_comment.sub_comment') - -# def restore_object(self, data, instance=None): -# if instance is None: -# return Comment(**data) -# for key, val in data.items(): -# setattr(instance, key, val) -# return instance - - -# class NamesSerializer(serializers.Serializer): -# first = serializers.CharField() -# last = serializers.CharField(required=False, default='') -# initials = serializers.CharField(required=False, default='') - - -# class PersonIdentifierSerializer(serializers.Serializer): -# ssn = serializers.CharField() -# names = NamesSerializer(source='names', required=False) - - -# class BookSerializer(serializers.ModelSerializer): -# isbn = serializers.RegexField(regex=r'^[0-9]{13}$', error_messages={'invalid': 'isbn has to be exact 13 numbers'}) - -# class Meta: -# model = Book - - -# class ActionItemSerializer(serializers.ModelSerializer): - -# class Meta: -# model = ActionItem - - -# class ActionItemSerializerOptionalFields(serializers.ModelSerializer): -# """ -# Intended to test that fields with `required=False` are excluded from validation. -# """ -# title = serializers.CharField(required=False) - -# class Meta: -# model = ActionItem -# fields = ('title',) - - -# class ActionItemSerializerCustomRestore(serializers.ModelSerializer): - -# class Meta: -# model = ActionItem - -# def restore_object(self, data, instance=None): -# if instance is None: -# return ActionItem(**data) -# for key, val in data.items(): -# setattr(instance, key, val) -# return instance - - -# class PersonSerializer(serializers.ModelSerializer): -# info = serializers.Field(source='info') - -# class Meta: -# model = Person -# fields = ('name', 'age', 'info') -# read_only_fields = ('age',) - - -# class NestedSerializer(serializers.Serializer): -# info = serializers.Field() - - -# class ModelSerializerWithNestedSerializer(serializers.ModelSerializer): -# nested = NestedSerializer(source='*') - -# class Meta: -# model = Person - - -# class NestedSerializerWithRenamedField(serializers.Serializer): -# renamed_info = serializers.Field(source='info') - - -# class ModelSerializerWithNestedSerializerWithRenamedField(serializers.ModelSerializer): -# nested = NestedSerializerWithRenamedField(source='*') - -# class Meta: -# model = Person - - -# class PersonSerializerInvalidReadOnly(serializers.ModelSerializer): -# """ -# Testing for #652. -# """ -# info = serializers.Field(source='info') - -# class Meta: -# model = Person -# fields = ('name', 'age', 'info') -# read_only_fields = ('age', 'info') - - -# class AlbumsSerializer(serializers.ModelSerializer): - -# class Meta: -# model = Album -# fields = ['title', 'ref'] # lists are also valid options - - -# class PositiveIntegerAsChoiceSerializer(serializers.ModelSerializer): -# class Meta: -# model = HasPositiveIntegerAsChoice -# fields = ['some_integer'] - - -# class ForeignKeySourceSerializer(serializers.ModelSerializer): -# class Meta: -# model = ForeignKeySource - - -# class HyperlinkedForeignKeySourceSerializer(serializers.HyperlinkedModelSerializer): -# class Meta: -# model = ForeignKeySource - - -# class BasicTests(TestCase): -# def setUp(self): -# self.comment = Comment( -# 'tom@example.com', -# 'Happy new year!', -# datetime.datetime(2012, 1, 1) -# ) -# self.actionitem = ActionItem(title='Some to do item',) -# self.data = { -# 'email': 'tom@example.com', -# 'content': 'Happy new year!', -# 'created': datetime.datetime(2012, 1, 1), -# 'sub_comment': 'This wont change' -# } -# self.expected = { -# 'email': 'tom@example.com', -# 'content': 'Happy new year!', -# 'created': datetime.datetime(2012, 1, 1), -# 'sub_comment': 'And Merry Christmas!' -# } -# self.person_data = {'name': 'dwight', 'age': 35} -# self.person = Person(**self.person_data) -# self.person.save() - -# def test_empty(self): -# serializer = CommentSerializer() -# expected = { -# 'email': '', -# 'content': '', -# 'created': None -# } -# self.assertEqual(serializer.data, expected) - -# def test_retrieve(self): -# serializer = CommentSerializer(self.comment) -# self.assertEqual(serializer.data, self.expected) - -# def test_create(self): -# serializer = CommentSerializer(data=self.data) -# expected = self.comment -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, expected) -# self.assertFalse(serializer.object is expected) -# self.assertEqual(serializer.data['sub_comment'], 'And Merry Christmas!') - -# def test_create_nested(self): -# """Test a serializer with nested data.""" -# names = {'first': 'John', 'last': 'Doe', 'initials': 'jd'} -# data = {'ssn': '1234567890', 'names': names} -# serializer = PersonIdentifierSerializer(data=data) - -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, data) -# self.assertFalse(serializer.object is data) -# self.assertEqual(serializer.data['names'], names) - -# def test_create_partial_nested(self): -# """Test a serializer with nested data which has missing fields.""" -# names = {'first': 'John'} -# data = {'ssn': '1234567890', 'names': names} -# serializer = PersonIdentifierSerializer(data=data) - -# expected_names = {'first': 'John', 'last': '', 'initials': ''} -# data['names'] = expected_names - -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, data) -# self.assertFalse(serializer.object is expected_names) -# self.assertEqual(serializer.data['names'], expected_names) - -# def test_null_nested(self): -# """Test a serializer with a nonexistent nested field""" -# data = {'ssn': '1234567890'} -# serializer = PersonIdentifierSerializer(data=data) - -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, data) -# self.assertFalse(serializer.object is data) -# expected = {'ssn': '1234567890', 'names': None} -# self.assertEqual(serializer.data, expected) - -# def test_update(self): -# serializer = CommentSerializer(self.comment, data=self.data) -# expected = self.comment -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, expected) -# self.assertTrue(serializer.object is expected) -# self.assertEqual(serializer.data['sub_comment'], 'And Merry Christmas!') - -# def test_partial_update(self): -# msg = 'Merry New Year!' -# partial_data = {'content': msg} -# serializer = CommentSerializer(self.comment, data=partial_data) -# self.assertEqual(serializer.is_valid(), False) -# serializer = CommentSerializer(self.comment, data=partial_data, partial=True) -# expected = self.comment -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, expected) -# self.assertTrue(serializer.object is expected) -# self.assertEqual(serializer.data['content'], msg) - -# def test_model_fields_as_expected(self): -# """ -# Make sure that the fields returned are the same as defined -# in the Meta data -# """ -# serializer = PersonSerializer(self.person) -# self.assertEqual( -# set(serializer.data.keys()), -# set(['name', 'age', 'info']) -# ) - -# def test_field_with_dictionary(self): -# """ -# Make sure that dictionaries from fields are left intact -# """ -# serializer = PersonSerializer(self.person) -# expected = self.person_data -# self.assertEqual(serializer.data['info'], expected) - -# def test_read_only_fields(self): -# """ -# Attempting to update fields set as read_only should have no effect. -# """ -# serializer = PersonSerializer(self.person, data={'name': 'dwight', 'age': 99}) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(serializer.errors, {}) -# # Assert age is unchanged (35) -# self.assertEqual(instance.age, self.person_data['age']) - -# def test_invalid_read_only_fields(self): -# """ -# Regression test for #652. -# """ -# self.assertRaises(AssertionError, PersonSerializerInvalidReadOnly, []) - -# def test_serializer_data_is_cleared_on_save(self): -# """ -# Check _data attribute is cleared on `save()` - -# Regression test for #1116 -# — id field is not populated if `data` is accessed prior to `save()` -# """ -# serializer = ActionItemSerializer(self.actionitem) -# self.assertIsNone(serializer.data.get('id', None), 'New instance. `id` should not be set.') -# serializer.save() -# self.assertIsNotNone(serializer.data.get('id', None), 'Model is saved. `id` should be set.') - -# def test_fields_marked_as_not_required_are_excluded_from_validation(self): -# """ -# Check that fields with `required=False` are included in list of exclusions. -# """ -# serializer = ActionItemSerializerOptionalFields(self.actionitem) -# exclusions = serializer.get_validation_exclusions() -# self.assertTrue('title' in exclusions, '`title` field was marked `required=False` and should be excluded') - - -# class DictStyleSerializer(serializers.Serializer): -# """ -# Note that we don't have any `restore_object` method, so the default -# case of simply returning a dict will apply. -# """ -# email = serializers.EmailField() - - -# class DictStyleSerializerTests(TestCase): -# def test_dict_style_deserialize(self): -# """ -# Ensure serializers can deserialize into a dict. -# """ -# data = {'email': 'foo@example.com'} -# serializer = DictStyleSerializer(data=data) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, data) - -# def test_dict_style_serialize(self): -# """ -# Ensure serializers can serialize dict objects. -# """ -# data = {'email': 'foo@example.com'} -# serializer = DictStyleSerializer(data) -# self.assertEqual(serializer.data, data) - - -# class ValidationTests(TestCase): -# def setUp(self): -# self.comment = Comment( -# 'tom@example.com', -# 'Happy new year!', -# datetime.datetime(2012, 1, 1) -# ) -# self.data = { -# 'email': 'tom@example.com', -# 'content': 'x' * 1001, -# 'created': datetime.datetime(2012, 1, 1) -# } -# self.actionitem = ActionItem(title='Some to do item',) - -# def test_create(self): -# serializer = CommentSerializer(data=self.data) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']}) - -# def test_update(self): -# serializer = CommentSerializer(self.comment, data=self.data) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']}) - -# def test_update_missing_field(self): -# data = { -# 'content': 'xxx', -# 'created': datetime.datetime(2012, 1, 1) -# } -# serializer = CommentSerializer(self.comment, data=data) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, {'email': ['This field is required.']}) - -# def test_missing_bool_with_default(self): -# """Make sure that a boolean value with a 'False' value is not -# mistaken for not having a default.""" -# data = { -# 'title': 'Some action item', -# # No 'done' value. -# } -# serializer = ActionItemSerializer(self.actionitem, data=data) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.errors, {}) - -# def test_cross_field_validation(self): - -# class CommentSerializerWithCrossFieldValidator(CommentSerializer): - -# def validate(self, attrs): -# if attrs["email"] not in attrs["content"]: -# raise serializers.ValidationError("Email address not in content") -# return attrs - -# data = { -# 'email': 'tom@example.com', -# 'content': 'A comment from tom@example.com', -# 'created': datetime.datetime(2012, 1, 1) -# } - -# serializer = CommentSerializerWithCrossFieldValidator(data=data) -# self.assertTrue(serializer.is_valid()) - -# data['content'] = 'A comment from foo@bar.com' - -# serializer = CommentSerializerWithCrossFieldValidator(data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'non_field_errors': ['Email address not in content']}) - -# def test_null_is_true_fields(self): -# """ -# Omitting a value for null-field should validate. -# """ -# serializer = PersonSerializer(data={'name': 'marko'}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.errors, {}) - -# def test_modelserializer_max_length_exceeded(self): -# data = { -# 'title': 'x' * 201, -# } -# serializer = ActionItemSerializer(data=data) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']}) - -# def test_modelserializer_max_length_exceeded_with_custom_restore(self): -# """ -# When overriding ModelSerializer.restore_object, validation tests should still apply. -# Regression test for #623. - -# https://github.com/tomchristie/django-rest-framework/pull/623 -# """ -# data = { -# 'title': 'x' * 201, -# } -# serializer = ActionItemSerializerCustomRestore(data=data) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']}) - -# def test_default_modelfield_max_length_exceeded(self): -# data = { -# 'title': 'Testing "info" field...', -# 'info': 'x' * 13, -# } -# serializer = ActionItemSerializer(data=data) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, {'info': ['Ensure this value has at most 12 characters (it has 13).']}) - -# def test_datetime_validation_failure(self): -# """ -# Test DateTimeField validation errors on non-str values. -# Regression test for #669. - -# https://github.com/tomchristie/django-rest-framework/issues/669 -# """ -# data = self.data -# data['created'] = 0 - -# serializer = CommentSerializer(data=data) -# self.assertEqual(serializer.is_valid(), False) - -# self.assertIn('created', serializer.errors) - -# def test_missing_model_field_exception_msg(self): -# """ -# Assert that a meaningful exception message is outputted when the model -# field is missing (e.g. when mistyping ``model``). -# """ -# class BrokenModelSerializer(serializers.ModelSerializer): -# class Meta: -# fields = ['some_field'] - -# try: -# BrokenModelSerializer() -# except AssertionError as e: -# self.assertEqual(e.args[0], "Serializer class 'BrokenModelSerializer' is missing 'model' Meta option") -# except: -# self.fail('Wrong exception type thrown.') - -# def test_writable_star_source_on_nested_serializer(self): -# """ -# Assert that a nested serializer instantiated with source='*' correctly -# expands the data into the outer serializer. -# """ -# serializer = ModelSerializerWithNestedSerializer(data={ -# 'name': 'marko', -# 'nested': {'info': 'hi'}}, -# ) -# self.assertEqual(serializer.is_valid(), True) - -# def test_writable_star_source_on_nested_serializer_with_parent_object(self): -# class TitleSerializer(serializers.Serializer): -# title = serializers.WritableField(source='title') - -# class AlbumSerializer(serializers.ModelSerializer): -# nested = TitleSerializer(source='*') - -# class Meta: -# model = Album -# fields = ('nested',) - -# class PhotoSerializer(serializers.ModelSerializer): -# album = AlbumSerializer(source='album') - -# class Meta: -# model = Photo -# fields = ('album', ) - -# photo = Photo(album=Album()) - -# data = {'album': {'nested': {'title': 'test'}}} - -# serializer = PhotoSerializer(photo, data=data) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.data, data) - -# def test_writable_star_source_with_inner_source_fields(self): -# """ -# Tests that a serializer with source="*" correctly expands the -# it's fields into the outer serializer even if they have their -# own 'source' parameters. -# """ - -# serializer = ModelSerializerWithNestedSerializerWithRenamedField(data={ -# 'name': 'marko', -# 'nested': {'renamed_info': 'hi'}}, -# ) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.errors, {}) - - -# class CustomValidationTests(TestCase): -# class CommentSerializerWithFieldValidator(CommentSerializer): - -# def validate_email(self, attrs, source): -# attrs[source] -# return attrs - -# def validate_content(self, attrs, source): -# value = attrs[source] -# if "test" not in value: -# raise serializers.ValidationError("Test not in value") -# return attrs - -# def test_field_validation(self): -# data = { -# 'email': 'tom@example.com', -# 'content': 'A test comment', -# 'created': datetime.datetime(2012, 1, 1) -# } - -# serializer = self.CommentSerializerWithFieldValidator(data=data) -# self.assertTrue(serializer.is_valid()) - -# data['content'] = 'This should not validate' - -# serializer = self.CommentSerializerWithFieldValidator(data=data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'content': ['Test not in value']}) - -# def test_missing_data(self): -# """ -# Make sure that validate_content isn't called if the field is missing -# """ -# incomplete_data = { -# 'email': 'tom@example.com', -# 'created': datetime.datetime(2012, 1, 1) -# } -# serializer = self.CommentSerializerWithFieldValidator(data=incomplete_data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'content': ['This field is required.']}) - -# def test_wrong_data(self): -# """ -# Make sure that validate_content isn't called if the field input is wrong -# """ -# wrong_data = { -# 'email': 'not an email', -# 'content': 'A test comment', -# 'created': datetime.datetime(2012, 1, 1) -# } -# serializer = self.CommentSerializerWithFieldValidator(data=wrong_data) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'email': ['Enter a valid email address.']}) - -# def test_partial_update(self): -# """ -# Make sure that validate_email isn't called when partial=True and email -# isn't found in data. -# """ -# initial_data = { -# 'email': 'tom@example.com', -# 'content': 'A test comment', -# 'created': datetime.datetime(2012, 1, 1) -# } - -# serializer = self.CommentSerializerWithFieldValidator(data=initial_data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.object - -# new_content = 'An *updated* test comment' -# partial_data = { -# 'content': new_content -# } - -# serializer = self.CommentSerializerWithFieldValidator(instance=instance, -# data=partial_data, -# partial=True) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.object -# self.assertEqual(instance.content, new_content) - - -# class PositiveIntegerAsChoiceTests(TestCase): -# def test_positive_integer_in_json_is_correctly_parsed(self): -# data = {'some_integer': 1} -# serializer = PositiveIntegerAsChoiceSerializer(data=data) -# self.assertEqual(serializer.is_valid(), True) - - -# class ModelValidationTests(TestCase): -# def test_validate_unique(self): -# """ -# Just check if serializers.ModelSerializer handles unique checks via .full_clean() -# """ -# serializer = AlbumsSerializer(data={'title': 'a', 'ref': '1'}) -# serializer.is_valid() -# serializer.save() -# second_serializer = AlbumsSerializer(data={'title': 'a'}) -# self.assertFalse(second_serializer.is_valid()) -# self.assertEqual(second_serializer.errors, {'title': ['Album with this Title already exists.']}) -# third_serializer = AlbumsSerializer(data=[{'title': 'b', 'ref': '1'}, {'title': 'c'}], many=True) -# self.assertFalse(third_serializer.is_valid()) -# self.assertEqual(third_serializer.errors, [{'ref': ['Album with this Ref already exists.']}, {}]) - -# def test_foreign_key_is_null_with_partial(self): -# """ -# Test ModelSerializer validation with partial=True - -# Specifically test that a null foreign key does not pass validation -# """ -# album = Album(title='test') -# album.save() - -# class PhotoSerializer(serializers.ModelSerializer): -# class Meta: -# model = Photo - -# photo_serializer = PhotoSerializer(data={'description': 'test', 'album': album.pk}) -# self.assertTrue(photo_serializer.is_valid()) -# photo = photo_serializer.save() - -# # Updating only the album (foreign key) -# photo_serializer = PhotoSerializer(instance=photo, data={'album': ''}, partial=True) -# self.assertFalse(photo_serializer.is_valid()) -# self.assertTrue('album' in photo_serializer.errors) -# self.assertEqual(photo_serializer.errors['album'], [photo_serializer.error_messages['required']]) - -# def test_foreign_key_with_partial(self): -# """ -# Test ModelSerializer validation with partial=True - -# Specifically test foreign key validation. -# """ - -# album = Album(title='test') -# album.save() - -# class PhotoSerializer(serializers.ModelSerializer): -# class Meta: -# model = Photo - -# photo_serializer = PhotoSerializer(data={'description': 'test', 'album': album.pk}) -# self.assertTrue(photo_serializer.is_valid()) -# photo = photo_serializer.save() - -# # Updating only the album (foreign key) -# photo_serializer = PhotoSerializer(instance=photo, data={'album': album.pk}, partial=True) -# self.assertTrue(photo_serializer.is_valid()) -# self.assertTrue(photo_serializer.save()) - -# # Updating only the description -# photo_serializer = PhotoSerializer(instance=photo, -# data={'description': 'new'}, -# partial=True) - -# self.assertTrue(photo_serializer.is_valid()) -# self.assertTrue(photo_serializer.save()) - - -# class RegexValidationTest(TestCase): -# def test_create_failed(self): -# serializer = BookSerializer(data={'isbn': '1234567890'}) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) - -# serializer = BookSerializer(data={'isbn': '12345678901234'}) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) - -# serializer = BookSerializer(data={'isbn': 'abcdefghijklm'}) -# self.assertFalse(serializer.is_valid()) -# self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']}) - -# def test_create_success(self): -# serializer = BookSerializer(data={'isbn': '1234567890123'}) -# self.assertTrue(serializer.is_valid()) - - -# class MetadataTests(TestCase): -# def test_empty(self): -# serializer = CommentSerializer() -# expected = { -# 'email': serializers.CharField, -# 'content': serializers.CharField, -# 'created': serializers.DateTimeField -# } -# for field_name, field in expected.items(): -# self.assertTrue(isinstance(serializer.data.fields[field_name], field)) - - -# class ManyToManyTests(TestCase): -# def setUp(self): -# class ManyToManySerializer(serializers.ModelSerializer): -# class Meta: -# model = ManyToManyModel - -# self.serializer_class = ManyToManySerializer - -# # An anchor instance to use for the relationship -# self.anchor = Anchor() -# self.anchor.save() - -# # A model instance with a many to many relationship to the anchor -# self.instance = ManyToManyModel() -# self.instance.save() -# self.instance.rel.add(self.anchor) - -# # A serialized representation of the model instance -# self.data = {'id': 1, 'rel': [self.anchor.id]} - -# def test_retrieve(self): -# """ -# Serialize an instance of a model with a ManyToMany relationship. -# """ -# serializer = self.serializer_class(instance=self.instance) -# expected = self.data -# self.assertEqual(serializer.data, expected) - -# def test_create(self): -# """ -# Create an instance of a model with a ManyToMany relationship. -# """ -# data = {'rel': [self.anchor.id]} -# serializer = self.serializer_class(data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(ManyToManyModel.objects.all()), 2) -# self.assertEqual(instance.pk, 2) -# self.assertEqual(list(instance.rel.all()), [self.anchor]) - -# def test_update(self): -# """ -# Update an instance of a model with a ManyToMany relationship. -# """ -# new_anchor = Anchor() -# new_anchor.save() -# data = {'rel': [self.anchor.id, new_anchor.id]} -# serializer = self.serializer_class(self.instance, data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(ManyToManyModel.objects.all()), 1) -# self.assertEqual(instance.pk, 1) -# self.assertEqual(list(instance.rel.all()), [self.anchor, new_anchor]) - -# def test_create_empty_relationship(self): -# """ -# Create an instance of a model with a ManyToMany relationship, -# containing no items. -# """ -# data = {'rel': []} -# serializer = self.serializer_class(data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(ManyToManyModel.objects.all()), 2) -# self.assertEqual(instance.pk, 2) -# self.assertEqual(list(instance.rel.all()), []) - -# def test_update_empty_relationship(self): -# """ -# Update an instance of a model with a ManyToMany relationship, -# containing no items. -# """ -# new_anchor = Anchor() -# new_anchor.save() -# data = {'rel': []} -# serializer = self.serializer_class(self.instance, data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(ManyToManyModel.objects.all()), 1) -# self.assertEqual(instance.pk, 1) -# self.assertEqual(list(instance.rel.all()), []) - -# def test_create_empty_relationship_flat_data(self): -# """ -# Create an instance of a model with a ManyToMany relationship, -# containing no items, using a representation that does not support -# lists (eg form data). -# """ -# data = MultiValueDict() -# data.setlist('rel', ['']) -# serializer = self.serializer_class(data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(ManyToManyModel.objects.all()), 2) -# self.assertEqual(instance.pk, 2) -# self.assertEqual(list(instance.rel.all()), []) - - -# class ReadOnlyManyToManyTests(TestCase): -# def setUp(self): -# class ReadOnlyManyToManySerializer(serializers.ModelSerializer): -# rel = serializers.RelatedField(many=True, read_only=True) - -# class Meta: -# model = ReadOnlyManyToManyModel - -# self.serializer_class = ReadOnlyManyToManySerializer - -# # An anchor instance to use for the relationship -# self.anchor = Anchor() -# self.anchor.save() - -# # A model instance with a many to many relationship to the anchor -# self.instance = ReadOnlyManyToManyModel() -# self.instance.save() -# self.instance.rel.add(self.anchor) - -# # A serialized representation of the model instance -# self.data = {'rel': [self.anchor.id], 'id': 1, 'text': 'anchor'} - -# def test_update(self): -# """ -# Attempt to update an instance of a model with a ManyToMany -# relationship. Not updated due to read_only=True -# """ -# new_anchor = Anchor() -# new_anchor.save() -# data = {'rel': [self.anchor.id, new_anchor.id]} -# serializer = self.serializer_class(self.instance, data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(ReadOnlyManyToManyModel.objects.all()), 1) -# self.assertEqual(instance.pk, 1) -# # rel is still as original (1 entry) -# self.assertEqual(list(instance.rel.all()), [self.anchor]) - -# def test_update_without_relationship(self): -# """ -# Attempt to update an instance of a model where many to ManyToMany -# relationship is not supplied. Not updated due to read_only=True -# """ -# new_anchor = Anchor() -# new_anchor.save() -# data = {} -# serializer = self.serializer_class(self.instance, data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(ReadOnlyManyToManyModel.objects.all()), 1) -# self.assertEqual(instance.pk, 1) -# # rel is still as original (1 entry) -# self.assertEqual(list(instance.rel.all()), [self.anchor]) - - -# class DefaultValueTests(TestCase): -# def setUp(self): -# class DefaultValueSerializer(serializers.ModelSerializer): -# class Meta: -# model = DefaultValueModel - -# self.serializer_class = DefaultValueSerializer -# self.objects = DefaultValueModel.objects - -# def test_create_using_default(self): -# data = {} -# serializer = self.serializer_class(data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(self.objects.all()), 1) -# self.assertEqual(instance.pk, 1) -# self.assertEqual(instance.text, 'foobar') - -# def test_create_overriding_default(self): -# data = {'text': 'overridden'} -# serializer = self.serializer_class(data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(self.objects.all()), 1) -# self.assertEqual(instance.pk, 1) -# self.assertEqual(instance.text, 'overridden') - -# def test_partial_update_default(self): -# """ Regression test for issue #532 """ -# data = {'text': 'overridden'} -# serializer = self.serializer_class(data=data, partial=True) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() - -# data = {'extra': 'extra_value'} -# serializer = self.serializer_class(instance=instance, data=data, partial=True) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() - -# self.assertEqual(instance.extra, 'extra_value') -# self.assertEqual(instance.text, 'overridden') - - -# class WritableFieldDefaultValueTests(TestCase): - -# def setUp(self): -# self.expected = {'default': 'value'} -# self.create_field = fields.WritableField - -# def test_get_default_value_with_noncallable(self): -# field = self.create_field(default=self.expected) -# got = field.get_default_value() -# self.assertEqual(got, self.expected) - -# def test_get_default_value_with_callable(self): -# field = self.create_field(default=lambda: self.expected) -# got = field.get_default_value() -# self.assertEqual(got, self.expected) - -# def test_get_default_value_when_not_required(self): -# field = self.create_field(default=self.expected, required=False) -# got = field.get_default_value() -# self.assertEqual(got, self.expected) - -# def test_get_default_value_returns_None(self): -# field = self.create_field() -# got = field.get_default_value() -# self.assertIsNone(got) - -# def test_get_default_value_returns_non_True_values(self): -# values = [None, '', False, 0, [], (), {}] # values that assumed as 'False' in the 'if' clause -# for expected in values: -# field = self.create_field(default=expected) -# got = field.get_default_value() -# self.assertEqual(got, expected) - - -# class RelatedFieldDefaultValueTests(WritableFieldDefaultValueTests): - -# def setUp(self): -# self.expected = {'foo': 'bar'} -# self.create_field = relations.RelatedField - -# def test_get_default_value_returns_empty_list(self): -# field = self.create_field(many=True) -# got = field.get_default_value() -# self.assertListEqual(got, []) - -# def test_get_default_value_returns_expected(self): -# expected = [1, 2, 3] -# field = self.create_field(many=True, default=expected) -# got = field.get_default_value() -# self.assertListEqual(got, expected) - - -# class CallableDefaultValueTests(TestCase): -# def setUp(self): -# class CallableDefaultValueSerializer(serializers.ModelSerializer): -# class Meta: -# model = CallableDefaultValueModel - -# self.serializer_class = CallableDefaultValueSerializer -# self.objects = CallableDefaultValueModel.objects - -# def test_create_using_default(self): -# data = {} -# serializer = self.serializer_class(data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(self.objects.all()), 1) -# self.assertEqual(instance.pk, 1) -# self.assertEqual(instance.text, 'foobar') - -# def test_create_overriding_default(self): -# data = {'text': 'overridden'} -# serializer = self.serializer_class(data=data) -# self.assertEqual(serializer.is_valid(), True) -# instance = serializer.save() -# self.assertEqual(len(self.objects.all()), 1) -# self.assertEqual(instance.pk, 1) -# self.assertEqual(instance.text, 'overridden') - - -# class ManyRelatedTests(TestCase): -# def test_reverse_relations(self): -# post = BlogPost.objects.create(title="Test blog post") -# post.blogpostcomment_set.create(text="I hate this blog post") -# post.blogpostcomment_set.create(text="I love this blog post") - -# class BlogPostCommentSerializer(serializers.Serializer): -# text = serializers.CharField() - -# class BlogPostSerializer(serializers.Serializer): -# title = serializers.CharField() -# comments = BlogPostCommentSerializer(source='blogpostcomment_set') - -# serializer = BlogPostSerializer(instance=post) -# expected = { -# 'title': 'Test blog post', -# 'comments': [ -# {'text': 'I hate this blog post'}, -# {'text': 'I love this blog post'} -# ] -# } - -# self.assertEqual(serializer.data, expected) - -# def test_include_reverse_relations(self): -# post = BlogPost.objects.create(title="Test blog post") -# post.blogpostcomment_set.create(text="I hate this blog post") -# post.blogpostcomment_set.create(text="I love this blog post") - -# class BlogPostSerializer(serializers.ModelSerializer): -# class Meta: -# model = BlogPost -# fields = ('id', 'title', 'blogpostcomment_set') - -# serializer = BlogPostSerializer(instance=post) -# expected = { -# 'id': 1, 'title': 'Test blog post', 'blogpostcomment_set': [1, 2] -# } -# self.assertEqual(serializer.data, expected) - -# def test_depth_include_reverse_relations(self): -# post = BlogPost.objects.create(title="Test blog post") -# post.blogpostcomment_set.create(text="I hate this blog post") -# post.blogpostcomment_set.create(text="I love this blog post") - -# class BlogPostSerializer(serializers.ModelSerializer): -# class Meta: -# model = BlogPost -# fields = ('id', 'title', 'blogpostcomment_set') -# depth = 1 - -# serializer = BlogPostSerializer(instance=post) -# expected = { -# 'id': 1, 'title': 'Test blog post', -# 'blogpostcomment_set': [ -# {'id': 1, 'text': 'I hate this blog post', 'blog_post': 1}, -# {'id': 2, 'text': 'I love this blog post', 'blog_post': 1} -# ] -# } -# self.assertEqual(serializer.data, expected) - -# def test_callable_source(self): -# post = BlogPost.objects.create(title="Test blog post") -# post.blogpostcomment_set.create(text="I love this blog post") - -# class BlogPostCommentSerializer(serializers.Serializer): -# text = serializers.CharField() - -# class BlogPostSerializer(serializers.Serializer): -# title = serializers.CharField() -# first_comment = BlogPostCommentSerializer(source='get_first_comment') - -# serializer = BlogPostSerializer(post) - -# expected = { -# 'title': 'Test blog post', -# 'first_comment': {'text': 'I love this blog post'} -# } -# self.assertEqual(serializer.data, expected) - - -# class RelatedTraversalTest(TestCase): -# def test_nested_traversal(self): -# """ -# Source argument should support dotted.source notation. -# """ -# user = Person.objects.create(name="django") -# post = BlogPost.objects.create(title="Test blog post", writer=user) -# post.blogpostcomment_set.create(text="I love this blog post") - -# class PersonSerializer(serializers.ModelSerializer): -# class Meta: -# model = Person -# fields = ("name", "age") - -# class BlogPostCommentSerializer(serializers.ModelSerializer): -# class Meta: -# model = BlogPostComment -# fields = ("text", "post_owner") - -# text = serializers.CharField() -# post_owner = PersonSerializer(source='blog_post.writer') - -# class BlogPostSerializer(serializers.Serializer): -# title = serializers.CharField() -# comments = BlogPostCommentSerializer(source='blogpostcomment_set') - -# serializer = BlogPostSerializer(instance=post) - -# expected = { -# 'title': 'Test blog post', -# 'comments': [{ -# 'text': 'I love this blog post', -# 'post_owner': { -# "name": "django", -# "age": None -# } -# }] -# } - -# self.assertEqual(serializer.data, expected) - -# def test_nested_traversal_with_none(self): -# """ -# If a component of the dotted.source is None, return None for the field. -# """ -# from tests.models import NullableForeignKeySource -# instance = NullableForeignKeySource.objects.create(name='Source with null FK') - -# class NullableSourceSerializer(serializers.Serializer): -# target_name = serializers.Field(source='target.name') - -# serializer = NullableSourceSerializer(instance=instance) - -# expected = { -# 'target_name': None, -# } - -# self.assertEqual(serializer.data, expected) - - -# class SerializerMethodFieldTests(TestCase): -# def setUp(self): - -# class BoopSerializer(serializers.Serializer): -# beep = serializers.SerializerMethodField('get_beep') -# boop = serializers.Field() -# boop_count = serializers.SerializerMethodField('get_boop_count') - -# def get_beep(self, obj): -# return 'hello!' - -# def get_boop_count(self, obj): -# return len(obj.boop) - -# self.serializer_class = BoopSerializer - -# def test_serializer_method_field(self): - -# class MyModel(object): -# boop = ['a', 'b', 'c'] - -# source_data = MyModel() - -# serializer = self.serializer_class(source_data) - -# expected = { -# 'beep': 'hello!', -# 'boop': ['a', 'b', 'c'], -# 'boop_count': 3, -# } - -# self.assertEqual(serializer.data, expected) - - -# # Test for issue #324 -# class BlankFieldTests(TestCase): -# def setUp(self): - -# class BlankFieldModelSerializer(serializers.ModelSerializer): -# class Meta: -# model = BlankFieldModel - -# class BlankFieldSerializer(serializers.Serializer): -# title = serializers.CharField(required=False) - -# class NotBlankFieldModelSerializer(serializers.ModelSerializer): -# class Meta: -# model = BasicModel - -# class NotBlankFieldSerializer(serializers.Serializer): -# title = serializers.CharField() - -# self.model_serializer_class = BlankFieldModelSerializer -# self.serializer_class = BlankFieldSerializer -# self.not_blank_model_serializer_class = NotBlankFieldModelSerializer -# self.not_blank_serializer_class = NotBlankFieldSerializer -# self.data = {'title': ''} - -# def test_create_blank_field(self): -# serializer = self.serializer_class(data=self.data) -# self.assertEqual(serializer.is_valid(), True) - -# def test_create_model_blank_field(self): -# serializer = self.model_serializer_class(data=self.data) -# self.assertEqual(serializer.is_valid(), True) - -# def test_create_model_null_field(self): -# serializer = self.model_serializer_class(data={'title': None}) -# self.assertEqual(serializer.is_valid(), True) -# serializer.save() -# self.assertIsNot(serializer.object.pk, None) -# self.assertEqual(serializer.object.title, '') - -# def test_create_not_blank_field(self): -# """ -# Test to ensure blank data in a field not marked as blank=True -# is considered invalid in a non-model serializer -# """ -# serializer = self.not_blank_serializer_class(data=self.data) -# self.assertEqual(serializer.is_valid(), False) - -# def test_create_model_not_blank_field(self): -# """ -# Test to ensure blank data in a field not marked as blank=True -# is considered invalid in a model serializer -# """ -# serializer = self.not_blank_model_serializer_class(data=self.data) -# self.assertEqual(serializer.is_valid(), False) - -# def test_create_model_empty_field(self): -# serializer = self.model_serializer_class(data={}) -# self.assertEqual(serializer.is_valid(), True) - -# def test_create_model_null_field_save(self): -# """ -# Regression test for #1330. - -# https://github.com/tomchristie/django-rest-framework/pull/1330 -# """ -# serializer = self.model_serializer_class(data={'title': None}) -# self.assertEqual(serializer.is_valid(), True) - -# try: -# serializer.save() -# except Exception: -# self.fail('Exception raised on save() after validation passes') - - -# # Test for issue #460 -# class SerializerPickleTests(TestCase): -# """ -# Test pickleability of the output of Serializers -# """ -# def test_pickle_simple_model_serializer_data(self): -# """ -# Test simple serializer -# """ -# pickle.dumps(PersonSerializer(Person(name="Methusela", age=969)).data) - -# def test_pickle_inner_serializer(self): -# """ -# Test pickling a serializer whose resulting .data (a SortedDictWithMetadata) will -# have unpickleable meta data--in order to make sure metadata doesn't get pulled into the pickle. -# See DictWithMetadata.__getstate__ -# """ -# class InnerPersonSerializer(serializers.ModelSerializer): -# class Meta: -# model = Person -# fields = ('name', 'age') -# pickle.dumps(InnerPersonSerializer(Person(name="Noah", age=950)).data, 0) - -# def test_getstate_method_should_not_return_none(self): -# """ -# Regression test for #645. -# """ -# data = serializers.DictWithMetadata({1: 1}) -# self.assertEqual(data.__getstate__(), serializers.SortedDict({1: 1})) - -# def test_serializer_data_is_pickleable(self): -# """ -# Another regression test for #645. -# """ -# data = serializers.SortedDictWithMetadata({1: 1}) -# repr(pickle.loads(pickle.dumps(data, 0))) - - -# # test for issue #725 -# class SeveralChoicesModel(models.Model): -# color = models.CharField( -# max_length=10, -# choices=[('red', 'Red'), ('green', 'Green'), ('blue', 'Blue')], -# blank=False -# ) -# drink = models.CharField( -# max_length=10, -# choices=[('beer', 'Beer'), ('wine', 'Wine'), ('cider', 'Cider')], -# blank=False, -# default='beer' -# ) -# os = models.CharField( -# max_length=10, -# choices=[('linux', 'Linux'), ('osx', 'OSX'), ('windows', 'Windows')], -# blank=True -# ) -# music_genre = models.CharField( -# max_length=10, -# choices=[('rock', 'Rock'), ('metal', 'Metal'), ('grunge', 'Grunge')], -# blank=True, -# default='metal' -# ) - - -# class SerializerChoiceFields(TestCase): - -# def setUp(self): -# super(SerializerChoiceFields, self).setUp() - -# class SeveralChoicesSerializer(serializers.ModelSerializer): -# class Meta: -# model = SeveralChoicesModel -# fields = ('color', 'drink', 'os', 'music_genre') - -# self.several_choices_serializer = SeveralChoicesSerializer - -# def test_choices_blank_false_not_default(self): -# serializer = self.several_choices_serializer() -# self.assertEqual( -# serializer.fields['color'].choices, -# [('red', 'Red'), ('green', 'Green'), ('blue', 'Blue')] -# ) - -# def test_choices_blank_false_with_default(self): -# serializer = self.several_choices_serializer() -# self.assertEqual( -# serializer.fields['drink'].choices, -# [('beer', 'Beer'), ('wine', 'Wine'), ('cider', 'Cider')] -# ) - -# def test_choices_blank_true_not_default(self): -# serializer = self.several_choices_serializer() -# self.assertEqual( -# serializer.fields['os'].choices, -# BLANK_CHOICE_DASH + [('linux', 'Linux'), ('osx', 'OSX'), ('windows', 'Windows')] -# ) - -# def test_choices_blank_true_with_default(self): -# serializer = self.several_choices_serializer() -# self.assertEqual( -# serializer.fields['music_genre'].choices, -# BLANK_CHOICE_DASH + [('rock', 'Rock'), ('metal', 'Metal'), ('grunge', 'Grunge')] -# ) - - -# # Regression tests for #675 -# class Ticket(models.Model): -# assigned = models.ForeignKey( -# Person, related_name='assigned_tickets') -# reviewer = models.ForeignKey( -# Person, blank=True, null=True, related_name='reviewed_tickets') - - -# class SerializerRelatedChoicesTest(TestCase): - -# def setUp(self): -# super(SerializerRelatedChoicesTest, self).setUp() - -# class RelatedChoicesSerializer(serializers.ModelSerializer): -# class Meta: -# model = Ticket -# fields = ('assigned', 'reviewer') - -# self.related_fields_serializer = RelatedChoicesSerializer - -# def test_empty_queryset_required(self): -# serializer = self.related_fields_serializer() -# self.assertEqual(serializer.fields['assigned'].queryset.count(), 0) -# self.assertEqual( -# [x for x in serializer.fields['assigned'].widget.choices], -# [] -# ) - -# def test_empty_queryset_not_required(self): -# serializer = self.related_fields_serializer() -# self.assertEqual(serializer.fields['reviewer'].queryset.count(), 0) -# self.assertEqual( -# [x for x in serializer.fields['reviewer'].widget.choices], -# [('', '---------')] -# ) - -# def test_with_some_persons_required(self): -# Person.objects.create(name="Lionel Messi") -# Person.objects.create(name="Xavi Hernandez") -# serializer = self.related_fields_serializer() -# self.assertEqual(serializer.fields['assigned'].queryset.count(), 2) -# self.assertEqual( -# [x for x in serializer.fields['assigned'].widget.choices], -# [(1, 'Person object - 1'), (2, 'Person object - 2')] -# ) - -# def test_with_some_persons_not_required(self): -# Person.objects.create(name="Lionel Messi") -# Person.objects.create(name="Xavi Hernandez") -# serializer = self.related_fields_serializer() -# self.assertEqual(serializer.fields['reviewer'].queryset.count(), 2) -# self.assertEqual( -# [x for x in serializer.fields['reviewer'].widget.choices], -# [('', '---------'), (1, 'Person object - 1'), (2, 'Person object - 2')] -# ) - - -# class DepthTest(TestCase): -# def test_implicit_nesting(self): - -# writer = Person.objects.create(name="django", age=1) -# post = BlogPost.objects.create(title="Test blog post", writer=writer) -# comment = BlogPostComment.objects.create(text="Test blog post comment", blog_post=post) - -# class BlogPostCommentSerializer(serializers.ModelSerializer): -# class Meta: -# model = BlogPostComment -# depth = 2 - -# serializer = BlogPostCommentSerializer(instance=comment) -# expected = {'id': 1, 'text': 'Test blog post comment', 'blog_post': {'id': 1, 'title': 'Test blog post', -# 'writer': {'id': 1, 'name': 'django', 'age': 1}}} - -# self.assertEqual(serializer.data, expected) - -# def test_explicit_nesting(self): -# writer = Person.objects.create(name="django", age=1) -# post = BlogPost.objects.create(title="Test blog post", writer=writer) -# comment = BlogPostComment.objects.create(text="Test blog post comment", blog_post=post) - -# class PersonSerializer(serializers.ModelSerializer): -# class Meta: -# model = Person - -# class BlogPostSerializer(serializers.ModelSerializer): -# writer = PersonSerializer() - -# class Meta: -# model = BlogPost - -# class BlogPostCommentSerializer(serializers.ModelSerializer): -# blog_post = BlogPostSerializer() - -# class Meta: -# model = BlogPostComment - -# serializer = BlogPostCommentSerializer(instance=comment) -# expected = {'id': 1, 'text': 'Test blog post comment', 'blog_post': {'id': 1, 'title': 'Test blog post', -# 'writer': {'id': 1, 'name': 'django', 'age': 1}}} - -# self.assertEqual(serializer.data, expected) - - -# class NestedSerializerContextTests(TestCase): - -# def test_nested_serializer_context(self): -# """ -# Regression for #497 - -# https://github.com/tomchristie/django-rest-framework/issues/497 -# """ -# class PhotoSerializer(serializers.ModelSerializer): -# class Meta: -# model = Photo -# fields = ("description", "callable") - -# callable = serializers.SerializerMethodField('_callable') - -# def _callable(self, instance): -# if 'context_item' not in self.context: -# raise RuntimeError("context isn't getting passed into 2nd level nested serializer") -# return "success" - -# class AlbumSerializer(serializers.ModelSerializer): -# class Meta: -# model = Album -# fields = ("photo_set", "callable") - -# photo_set = PhotoSerializer(source="photo_set", many=True) -# callable = serializers.SerializerMethodField("_callable") - -# def _callable(self, instance): -# if 'context_item' not in self.context: -# raise RuntimeError("context isn't getting passed into 1st level nested serializer") -# return "success" - -# class AlbumCollection(object): -# albums = None - -# class AlbumCollectionSerializer(serializers.Serializer): -# albums = AlbumSerializer(source="albums", many=True) - -# album1 = Album.objects.create(title="album 1") -# album2 = Album.objects.create(title="album 2") -# Photo.objects.create(description="Bigfoot", album=album1) -# Photo.objects.create(description="Unicorn", album=album1) -# Photo.objects.create(description="Yeti", album=album2) -# Photo.objects.create(description="Sasquatch", album=album2) -# album_collection = AlbumCollection() -# album_collection.albums = [album1, album2] - -# # This will raise RuntimeError if context doesn't get passed correctly to the nested Serializers -# AlbumCollectionSerializer(album_collection, context={'context_item': 'album context'}).data - - -# class DeserializeListTestCase(TestCase): - -# def setUp(self): -# self.data = { -# 'email': 'nobody@nowhere.com', -# 'content': 'This is some test content', -# 'created': datetime.datetime(2013, 3, 7), -# } - -# def test_no_errors(self): -# data = [self.data.copy() for x in range(0, 3)] -# serializer = CommentSerializer(data=data, many=True) -# self.assertTrue(serializer.is_valid()) -# self.assertTrue(isinstance(serializer.object, list)) -# self.assertTrue( -# all((isinstance(item, Comment) for item in serializer.object)) -# ) - -# def test_errors_return_as_list(self): -# invalid_item = self.data.copy() -# invalid_item['email'] = '' -# data = [self.data.copy(), invalid_item, self.data.copy()] - -# serializer = CommentSerializer(data=data, many=True) -# self.assertFalse(serializer.is_valid()) -# expected = [{}, {'email': ['This field is required.']}, {}] -# self.assertEqual(serializer.errors, expected) - - -# # Test for issue 747 - -# class LazyStringModel(object): -# def __init__(self, lazystring): -# self.lazystring = lazystring - - -# class LazyStringSerializer(serializers.Serializer): -# lazystring = serializers.Field() - -# def restore_object(self, attrs, instance=None): -# if instance is not None: -# instance.lazystring = attrs.get('lazystring', instance.lazystring) -# return instance -# return LazyStringModel(**attrs) - - -# class LazyStringsTestCase(TestCase): -# def setUp(self): -# self.model = LazyStringModel(lazystring=_('lazystring')) - -# def test_lazy_strings_are_translated(self): -# serializer = LazyStringSerializer(self.model) -# self.assertEqual(type(serializer.data['lazystring']), -# type('lazystring')) - - -# # Test for issue #467 - -# class FieldLabelTest(TestCase): -# def setUp(self): -# self.serializer_class = BasicModelSerializer - -# def test_label_from_model(self): -# """ -# Validates that label and help_text are correctly copied from the model class. -# """ -# serializer = self.serializer_class() -# text_field = serializer.fields['text'] - -# self.assertEqual('Text comes here', text_field.label) -# self.assertEqual('Text description.', text_field.help_text) - -# def test_field_ctor(self): -# """ -# This is check that ctor supports both label and help_text. -# """ -# self.assertEqual('Label', fields.Field(label='Label', help_text='Help').label) -# self.assertEqual('Help', fields.CharField(label='Label', help_text='Help').help_text) -# self.assertEqual('Label', relations.HyperlinkedRelatedField(view_name='fake', label='Label', help_text='Help', many=True).label) - - -# # Test for issue #961 - -# class ManyFieldHelpTextTest(TestCase): -# def test_help_text_no_hold_down_control_msg(self): -# """ -# Validate that help_text doesn't contain the 'Hold down "Control" ...' -# message that Django appends to choice fields. -# """ -# rel_field = fields.Field(help_text=ManyToManyModel._meta.get_field('rel').help_text) -# self.assertEqual('Some help text.', rel_field.help_text) - - -# class AttributeMappingOnAutogeneratedRelatedFields(TestCase): - -# def test_primary_key_related_field(self): -# serializer = ForeignKeySourceSerializer() -# self.assertEqual(serializer.fields['target'].help_text, 'Target') -# self.assertEqual(serializer.fields['target'].label, 'Target') - -# def test_hyperlinked_related_field(self): -# serializer = HyperlinkedForeignKeySourceSerializer() -# self.assertEqual(serializer.fields['target'].help_text, 'Target') -# self.assertEqual(serializer.fields['target'].label, 'Target') - - -# @unittest.skipUnless(PIL is not None, 'PIL is not installed') -# class AttributeMappingOnAutogeneratedFieldsTests(TestCase): - -# def setUp(self): - -# class AMOAFSerializer(serializers.ModelSerializer): -# class Meta: -# model = AMOAFModel - -# self.serializer_class = AMOAFSerializer -# self.fields_attributes = { -# 'char_field': [ -# ('max_length', 1024), -# ], -# 'comma_separated_integer_field': [ -# ('max_length', 1024), -# ], -# 'decimal_field': [ -# ('max_digits', 64), -# ('decimal_places', 32), -# ], -# 'email_field': [ -# ('max_length', 1024), -# ], -# 'file_field': [ -# ('max_length', 1024), -# ], -# 'image_field': [ -# ('max_length', 1024), -# ], -# 'slug_field': [ -# ('max_length', 1024), -# ], -# 'url_field': [ -# ('max_length', 1024), -# ], -# 'nullable_char_field': [ -# ('max_length', 1024), -# ('allow_none', True), -# ], -# } - -# def field_test(self, field): -# serializer = self.serializer_class(data={}) -# self.assertEqual(serializer.is_valid(), True) - -# for attribute in self.fields_attributes[field]: -# self.assertEqual( -# getattr(serializer.fields[field], attribute[0]), -# attribute[1] -# ) - -# def test_char_field(self): -# self.field_test('char_field') - -# def test_comma_separated_integer_field(self): -# self.field_test('comma_separated_integer_field') - -# def test_decimal_field(self): -# self.field_test('decimal_field') - -# def test_email_field(self): -# self.field_test('email_field') - -# def test_file_field(self): -# self.field_test('file_field') - -# def test_image_field(self): -# self.field_test('image_field') - -# def test_slug_field(self): -# self.field_test('slug_field') - -# def test_url_field(self): -# self.field_test('url_field') - -# def test_nullable_char_field(self): -# self.field_test('nullable_char_field') - - -# @unittest.skipUnless(PIL is not None, 'PIL is not installed') -# class DefaultValuesOnAutogeneratedFieldsTests(TestCase): - -# def setUp(self): - -# class DVOAFSerializer(serializers.ModelSerializer): -# class Meta: -# model = DVOAFModel - -# self.serializer_class = DVOAFSerializer -# self.fields_attributes = { -# 'positive_integer_field': [ -# ('min_value', 0), -# ], -# 'positive_small_integer_field': [ -# ('min_value', 0), -# ], -# 'email_field': [ -# ('max_length', 75), -# ], -# 'file_field': [ -# ('max_length', 100), -# ], -# 'image_field': [ -# ('max_length', 100), -# ], -# 'slug_field': [ -# ('max_length', 50), -# ], -# 'url_field': [ -# ('max_length', 200), -# ], -# } - -# def field_test(self, field): -# serializer = self.serializer_class(data={}) -# self.assertEqual(serializer.is_valid(), True) - -# for attribute in self.fields_attributes[field]: -# self.assertEqual( -# getattr(serializer.fields[field], attribute[0]), -# attribute[1] -# ) - -# def test_positive_integer_field(self): -# self.field_test('positive_integer_field') - -# def test_positive_small_integer_field(self): -# self.field_test('positive_small_integer_field') - -# def test_email_field(self): -# self.field_test('email_field') - -# def test_file_field(self): -# self.field_test('file_field') - -# def test_image_field(self): -# self.field_test('image_field') - -# def test_slug_field(self): -# self.field_test('slug_field') - -# def test_url_field(self): -# self.field_test('url_field') - - -# class MetadataSerializer(serializers.Serializer): -# field1 = serializers.CharField(max_length=3, required=True) -# field2 = serializers.CharField(max_length=10, required=False) - - -# class MetadataSerializerTestCase(TestCase): -# def setUp(self): -# self.serializer = MetadataSerializer() - -# def test_serializer_metadata(self): -# metadata = self.serializer.metadata() -# expected = { -# 'field1': { -# 'required': True, -# 'max_length': 3, -# 'type': 'string', -# 'read_only': False -# }, -# 'field2': { -# 'required': False, -# 'max_length': 10, -# 'type': 'string', -# 'read_only': False -# } -# } -# self.assertEqual(expected, metadata) - - -# # Regression test for #840 - -# class SimpleModel(models.Model): -# text = models.CharField(max_length=100) - - -# class SimpleModelSerializer(serializers.ModelSerializer): -# text = serializers.CharField() -# other = serializers.CharField() - -# class Meta: -# model = SimpleModel - -# def validate_other(self, attrs, source): -# del attrs['other'] -# return attrs - - -# class FieldValidationRemovingAttr(TestCase): -# def test_removing_non_model_field_in_validation(self): -# """ -# Removing an attr during field valiation should ensure that it is not -# passed through when restoring the object. - -# This allows additional non-model fields to be supported. - -# Regression test for #840. -# """ -# serializer = SimpleModelSerializer(data={'text': 'foo', 'other': 'bar'}) -# self.assertTrue(serializer.is_valid()) -# serializer.save() -# self.assertEqual(serializer.object.text, 'foo') - - -# # Regression test for #878 - -# class SimpleTargetModel(models.Model): -# text = models.CharField(max_length=100) - - -# class SimplePKSourceModelSerializer(serializers.Serializer): -# targets = serializers.PrimaryKeyRelatedField(queryset=SimpleTargetModel.objects.all(), many=True) -# text = serializers.CharField() - - -# class SimpleSlugSourceModelSerializer(serializers.Serializer): -# targets = serializers.SlugRelatedField(queryset=SimpleTargetModel.objects.all(), many=True, slug_field='pk') -# text = serializers.CharField() - - -# class SerializerSupportsManyRelationships(TestCase): -# def setUp(self): -# SimpleTargetModel.objects.create(text='foo') -# SimpleTargetModel.objects.create(text='bar') - -# def test_serializer_supports_pk_many_relationships(self): -# """ -# Regression test for #878. - -# Note that pk behavior has a different code path to usual cases, -# for performance reasons. -# """ -# serializer = SimplePKSourceModelSerializer(data={'text': 'foo', 'targets': [1, 2]}) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, {'text': 'foo', 'targets': [1, 2]}) - -# def test_serializer_supports_slug_many_relationships(self): -# """ -# Regression test for #878. -# """ -# serializer = SimpleSlugSourceModelSerializer(data={'text': 'foo', 'targets': [1, 2]}) -# self.assertTrue(serializer.is_valid()) -# self.assertEqual(serializer.data, {'text': 'foo', 'targets': [1, 2]}) - - -# class TransformMethodsSerializer(serializers.Serializer): -# a = serializers.CharField() -# b_renamed = serializers.CharField(source='b') - -# def transform_a(self, obj, value): -# return value.lower() - -# def transform_b_renamed(self, obj, value): -# if value is not None: -# return 'and ' + value - - -# class TestSerializerTransformMethods(TestCase): -# def setUp(self): -# self.s = TransformMethodsSerializer() - -# def test_transform_methods(self): -# self.assertEqual( -# self.s.to_native({'a': 'GREEN EGGS', 'b': 'HAM'}), -# { -# 'a': 'green eggs', -# 'b_renamed': 'and HAM', -# } -# ) - -# def test_missing_fields(self): -# self.assertEqual( -# self.s.to_native({'a': 'GREEN EGGS'}), -# { -# 'a': 'green eggs', -# 'b_renamed': None, -# } -# ) - - -# class DefaultTrueBooleanModel(models.Model): -# cat = models.BooleanField(default=True) -# dog = models.BooleanField(default=False) - - -# class SerializerDefaultTrueBoolean(TestCase): - -# def setUp(self): -# super(SerializerDefaultTrueBoolean, self).setUp() - -# class DefaultTrueBooleanSerializer(serializers.ModelSerializer): -# class Meta: -# model = DefaultTrueBooleanModel -# fields = ('cat', 'dog') - -# self.default_true_boolean_serializer = DefaultTrueBooleanSerializer - -# def test_enabled_as_false(self): -# serializer = self.default_true_boolean_serializer(data={'cat': False, -# 'dog': False}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.data['cat'], False) -# self.assertEqual(serializer.data['dog'], False) - -# def test_enabled_as_true(self): -# serializer = self.default_true_boolean_serializer(data={'cat': True, -# 'dog': True}) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.data['cat'], True) -# self.assertEqual(serializer.data['dog'], True) - -# def test_enabled_partial(self): -# serializer = self.default_true_boolean_serializer(data={'cat': False}, -# partial=True) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.data['cat'], False) -# self.assertEqual(serializer.data['dog'], False) - - -# class BoolenFieldTypeTest(TestCase): -# ''' -# Ensure the various Boolean based model fields are rendered as the proper -# field type - -# ''' - -# def setUp(self): -# ''' -# Setup an ActionItemSerializer for BooleanTesting -# ''' -# data = { -# 'title': 'b' * 201, -# } -# self.serializer = ActionItemSerializer(data=data) - -# def test_booleanfield_type(self): -# ''' -# Test that BooleanField is infered from models.BooleanField -# ''' -# bfield = self.serializer.get_fields()['done'] -# self.assertEqual(type(bfield), fields.BooleanField) - -# def test_nullbooleanfield_type(self): -# ''' -# Test that BooleanField is infered from models.NullBooleanField - -# https://groups.google.com/forum/#!topic/django-rest-framework/D9mXEftpuQ8 -# ''' -# bfield = self.serializer.get_fields()['started'] -# self.assertEqual(type(bfield), fields.BooleanField) +from rest_framework import serializers +import pytest + + +# Tests for core functionality. +# ----------------------------- + +class TestSerializer: + def setup(self): + class ExampleSerializer(serializers.Serializer): + char = serializers.CharField() + integer = serializers.IntegerField() + self.Serializer = ExampleSerializer + + def test_valid_serializer(self): + serializer = self.Serializer(data={'char': 'abc', 'integer': 123}) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc', 'integer': 123} + assert serializer.errors == {} + + def test_invalid_serializer(self): + serializer = self.Serializer(data={'char': 'abc'}) + assert not serializer.is_valid() + assert serializer.validated_data == {} + assert serializer.errors == {'integer': ['This field is required.']} + + def test_partial_validation(self): + serializer = self.Serializer(data={'char': 'abc'}, partial=True) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc'} + assert serializer.errors == {} + + def test_empty_serializer(self): + serializer = self.Serializer() + assert serializer.data == {'char': '', 'integer': None} + + def test_missing_attribute_during_serialization(self): + class MissingAttributes: + pass + instance = MissingAttributes() + serializer = self.Serializer(instance) + with pytest.raises(AttributeError): + serializer.data + + +class TestValidateMethod: + def test_non_field_error_validate_method(self): + class ExampleSerializer(serializers.Serializer): + char = serializers.CharField() + integer = serializers.IntegerField() + + def validate(self, attrs): + raise serializers.ValidationError('Non field error') + + serializer = ExampleSerializer(data={'char': 'abc', 'integer': 123}) + assert not serializer.is_valid() + assert serializer.errors == {'non_field_errors': ['Non field error']} + + def test_field_error_validate_method(self): + class ExampleSerializer(serializers.Serializer): + char = serializers.CharField() + integer = serializers.IntegerField() + + def validate(self, attrs): + raise serializers.ValidationError({'char': 'Field error'}) + + serializer = ExampleSerializer(data={'char': 'abc', 'integer': 123}) + assert not serializer.is_valid() + assert serializer.errors == {'char': ['Field error']} + + +class TestBaseSerializer: + def setup(self): + class ExampleSerializer(serializers.BaseSerializer): + def to_representation(self, obj): + return { + 'id': obj['id'], + 'email': obj['name'] + '@' + obj['domain'] + } + + def to_internal_value(self, data): + name, domain = str(data['email']).split('@') + return { + 'id': int(data['id']), + 'name': name, + 'domain': domain, + } + + self.Serializer = ExampleSerializer + + def test_serialize_instance(self): + instance = {'id': 1, 'name': 'tom', 'domain': 'example.com'} + serializer = self.Serializer(instance) + assert serializer.data == {'id': 1, 'email': 'tom@example.com'} + + def test_serialize_list(self): + instances = [ + {'id': 1, 'name': 'tom', 'domain': 'example.com'}, + {'id': 2, 'name': 'ann', 'domain': 'example.com'}, + ] + serializer = self.Serializer(instances, many=True) + assert serializer.data == [ + {'id': 1, 'email': 'tom@example.com'}, + {'id': 2, 'email': 'ann@example.com'} + ] + + def test_validate_data(self): + data = {'id': 1, 'email': 'tom@example.com'} + serializer = self.Serializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == { + 'id': 1, + 'name': 'tom', + 'domain': 'example.com' + } + + def test_validate_list(self): + data = [ + {'id': 1, 'email': 'tom@example.com'}, + {'id': 2, 'email': 'ann@example.com'}, + ] + serializer = self.Serializer(data=data, many=True) + assert serializer.is_valid() + assert serializer.validated_data == [ + {'id': 1, 'name': 'tom', 'domain': 'example.com'}, + {'id': 2, 'name': 'ann', 'domain': 'example.com'} + ] + + +class TestStarredSource: + """ + Tests for `source='*'` argument, which is used for nested representations. + + For example: + + nested_field = NestedField(source='*') + """ + data = { + 'nested1': {'a': 1, 'b': 2}, + 'nested2': {'c': 3, 'd': 4} + } + + def setup(self): + class NestedSerializer1(serializers.Serializer): + a = serializers.IntegerField() + b = serializers.IntegerField() + + class NestedSerializer2(serializers.Serializer): + c = serializers.IntegerField() + d = serializers.IntegerField() + + class TestSerializer(serializers.Serializer): + nested1 = NestedSerializer1(source='*') + nested2 = NestedSerializer2(source='*') + + self.Serializer = TestSerializer + + def test_nested_validate(self): + """ + A nested representation is validated into a flat internal object. + """ + serializer = self.Serializer(data=self.data) + assert serializer.is_valid() + assert serializer.validated_data == { + 'a': 1, + 'b': 2, + 'c': 3, + 'd': 4 + } + + def test_nested_serialize(self): + """ + An object can be serialized into a nested representation. + """ + instance = {'a': 1, 'b': 2, 'c': 3, 'd': 4} + serializer = self.Serializer(instance) + assert serializer.data == self.data diff --git a/tests/test_serializer_bulk_update.py b/tests/test_serializer_bulk_update.py index 3341ce59..fb881a75 100644 --- a/tests/test_serializer_bulk_update.py +++ b/tests/test_serializer_bulk_update.py @@ -1,278 +1,123 @@ -# """ -# Tests to cover bulk create and update using serializers. -# """ -# from __future__ import unicode_literals -# from django.test import TestCase -# from rest_framework import serializers - - -# class BulkCreateSerializerTests(TestCase): -# """ -# Creating multiple instances using serializers. -# """ - -# def setUp(self): -# class BookSerializer(serializers.Serializer): -# id = serializers.IntegerField() -# title = serializers.CharField(max_length=100) -# author = serializers.CharField(max_length=100) - -# self.BookSerializer = BookSerializer - -# def test_bulk_create_success(self): -# """ -# Correct bulk update serialization should return the input data. -# """ - -# data = [ -# { -# 'id': 0, -# 'title': 'The electric kool-aid acid test', -# 'author': 'Tom Wolfe' -# }, { -# 'id': 1, -# 'title': 'If this is a man', -# 'author': 'Primo Levi' -# }, { -# 'id': 2, -# 'title': 'The wind-up bird chronicle', -# 'author': 'Haruki Murakami' -# } -# ] - -# serializer = self.BookSerializer(data=data, many=True) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, data) - -# def test_bulk_create_errors(self): -# """ -# Correct bulk update serialization should return the input data. -# """ - -# data = [ -# { -# 'id': 0, -# 'title': 'The electric kool-aid acid test', -# 'author': 'Tom Wolfe' -# }, { -# 'id': 1, -# 'title': 'If this is a man', -# 'author': 'Primo Levi' -# }, { -# 'id': 'foo', -# 'title': 'The wind-up bird chronicle', -# 'author': 'Haruki Murakami' -# } -# ] -# expected_errors = [ -# {}, -# {}, -# {'id': ['Enter a whole number.']} -# ] - -# serializer = self.BookSerializer(data=data, many=True) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, expected_errors) - -# def test_invalid_list_datatype(self): -# """ -# Data containing list of incorrect data type should return errors. -# """ -# data = ['foo', 'bar', 'baz'] -# serializer = self.BookSerializer(data=data, many=True) -# self.assertEqual(serializer.is_valid(), False) - -# expected_errors = [ -# {'non_field_errors': ['Invalid data']}, -# {'non_field_errors': ['Invalid data']}, -# {'non_field_errors': ['Invalid data']} -# ] - -# self.assertEqual(serializer.errors, expected_errors) - -# def test_invalid_single_datatype(self): -# """ -# Data containing a single incorrect data type should return errors. -# """ -# data = 123 -# serializer = self.BookSerializer(data=data, many=True) -# self.assertEqual(serializer.is_valid(), False) - -# expected_errors = {'non_field_errors': ['Expected a list of items.']} - -# self.assertEqual(serializer.errors, expected_errors) - -# def test_invalid_single_object(self): -# """ -# Data containing only a single object, instead of a list of objects -# should return errors. -# """ -# data = { -# 'id': 0, -# 'title': 'The electric kool-aid acid test', -# 'author': 'Tom Wolfe' -# } -# serializer = self.BookSerializer(data=data, many=True) -# self.assertEqual(serializer.is_valid(), False) - -# expected_errors = {'non_field_errors': ['Expected a list of items.']} - -# self.assertEqual(serializer.errors, expected_errors) - - -# class BulkUpdateSerializerTests(TestCase): -# """ -# Updating multiple instances using serializers. -# """ - -# def setUp(self): -# class Book(object): -# """ -# A data type that can be persisted to a mock storage backend -# with `.save()` and `.delete()`. -# """ -# object_map = {} - -# def __init__(self, id, title, author): -# self.id = id -# self.title = title -# self.author = author - -# def save(self): -# Book.object_map[self.id] = self - -# def delete(self): -# del Book.object_map[self.id] - -# class BookSerializer(serializers.Serializer): -# id = serializers.IntegerField() -# title = serializers.CharField(max_length=100) -# author = serializers.CharField(max_length=100) - -# def restore_object(self, attrs, instance=None): -# if instance: -# instance.id = attrs['id'] -# instance.title = attrs['title'] -# instance.author = attrs['author'] -# return instance -# return Book(**attrs) - -# self.Book = Book -# self.BookSerializer = BookSerializer - -# data = [ -# { -# 'id': 0, -# 'title': 'The electric kool-aid acid test', -# 'author': 'Tom Wolfe' -# }, { -# 'id': 1, -# 'title': 'If this is a man', -# 'author': 'Primo Levi' -# }, { -# 'id': 2, -# 'title': 'The wind-up bird chronicle', -# 'author': 'Haruki Murakami' -# } -# ] - -# for item in data: -# book = Book(item['id'], item['title'], item['author']) -# book.save() - -# def books(self): -# """ -# Return all the objects in the mock storage backend. -# """ -# return self.Book.object_map.values() - -# def test_bulk_update_success(self): -# """ -# Correct bulk update serialization should return the input data. -# """ -# data = [ -# { -# 'id': 0, -# 'title': 'The electric kool-aid acid test', -# 'author': 'Tom Wolfe' -# }, { -# 'id': 2, -# 'title': 'Kafka on the shore', -# 'author': 'Haruki Murakami' -# } -# ] -# serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.data, data) -# serializer.save() -# new_data = self.BookSerializer(self.books(), many=True).data - -# self.assertEqual(data, new_data) - -# def test_bulk_update_and_create(self): -# """ -# Bulk update serialization may also include created items. -# """ -# data = [ -# { -# 'id': 0, -# 'title': 'The electric kool-aid acid test', -# 'author': 'Tom Wolfe' -# }, { -# 'id': 3, -# 'title': 'Kafka on the shore', -# 'author': 'Haruki Murakami' -# } -# ] -# serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.data, data) -# serializer.save() -# new_data = self.BookSerializer(self.books(), many=True).data -# self.assertEqual(data, new_data) - -# def test_bulk_update_invalid_create(self): -# """ -# Bulk update serialization without allow_add_remove may not create items. -# """ -# data = [ -# { -# 'id': 0, -# 'title': 'The electric kool-aid acid test', -# 'author': 'Tom Wolfe' -# }, { -# 'id': 3, -# 'title': 'Kafka on the shore', -# 'author': 'Haruki Murakami' -# } -# ] -# expected_errors = [ -# {}, -# {'non_field_errors': ['Cannot create a new item, only existing items may be updated.']} -# ] -# serializer = self.BookSerializer(self.books(), data=data, many=True) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, expected_errors) - -# def test_bulk_update_error(self): -# """ -# Incorrect bulk update serialization should return error data. -# """ -# data = [ -# { -# 'id': 0, -# 'title': 'The electric kool-aid acid test', -# 'author': 'Tom Wolfe' -# }, { -# 'id': 'foo', -# 'title': 'Kafka on the shore', -# 'author': 'Haruki Murakami' -# } -# ] -# expected_errors = [ -# {}, -# {'id': ['Enter a whole number.']} -# ] -# serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, expected_errors) +""" +Tests to cover bulk create and update using serializers. +""" +from __future__ import unicode_literals +from django.test import TestCase +from django.utils import six +from rest_framework import serializers + + +class BulkCreateSerializerTests(TestCase): + """ + Creating multiple instances using serializers. + """ + + def setUp(self): + class BookSerializer(serializers.Serializer): + id = serializers.IntegerField() + title = serializers.CharField(max_length=100) + author = serializers.CharField(max_length=100) + + self.BookSerializer = BookSerializer + + def test_bulk_create_success(self): + """ + Correct bulk update serialization should return the input data. + """ + + data = [ + { + 'id': 0, + 'title': 'The electric kool-aid acid test', + 'author': 'Tom Wolfe' + }, { + 'id': 1, + 'title': 'If this is a man', + 'author': 'Primo Levi' + }, { + 'id': 2, + 'title': 'The wind-up bird chronicle', + 'author': 'Haruki Murakami' + } + ] + + serializer = self.BookSerializer(data=data, many=True) + self.assertEqual(serializer.is_valid(), True) + self.assertEqual(serializer.validated_data, data) + + def test_bulk_create_errors(self): + """ + Incorrect bulk create serialization should return errors. + """ + + data = [ + { + 'id': 0, + 'title': 'The electric kool-aid acid test', + 'author': 'Tom Wolfe' + }, { + 'id': 1, + 'title': 'If this is a man', + 'author': 'Primo Levi' + }, { + 'id': 'foo', + 'title': 'The wind-up bird chronicle', + 'author': 'Haruki Murakami' + } + ] + expected_errors = [ + {}, + {}, + {'id': ['A valid integer is required.']} + ] + + serializer = self.BookSerializer(data=data, many=True) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(serializer.errors, expected_errors) + + def test_invalid_list_datatype(self): + """ + Data containing list of incorrect data type should return errors. + """ + data = ['foo', 'bar', 'baz'] + serializer = self.BookSerializer(data=data, many=True) + self.assertEqual(serializer.is_valid(), False) + + text_type_string = six.text_type.__name__ + message = 'Invalid data. Expected a dictionary, but got %s.' % text_type_string + expected_errors = [ + {'non_field_errors': [message]}, + {'non_field_errors': [message]}, + {'non_field_errors': [message]} + ] + + self.assertEqual(serializer.errors, expected_errors) + + def test_invalid_single_datatype(self): + """ + Data containing a single incorrect data type should return errors. + """ + data = 123 + serializer = self.BookSerializer(data=data, many=True) + self.assertEqual(serializer.is_valid(), False) + + expected_errors = {'non_field_errors': ['Expected a list of items but got type `int`.']} + + self.assertEqual(serializer.errors, expected_errors) + + def test_invalid_single_object(self): + """ + Data containing only a single object, instead of a list of objects + should return errors. + """ + data = { + 'id': 0, + 'title': 'The electric kool-aid acid test', + 'author': 'Tom Wolfe' + } + serializer = self.BookSerializer(data=data, many=True) + self.assertEqual(serializer.is_valid(), False) + + expected_errors = {'non_field_errors': ['Expected a list of items but got type `dict`.']} + + self.assertEqual(serializer.errors, expected_errors) diff --git a/tests/test_serializer_empty.py b/tests/test_serializer_empty.py deleted file mode 100644 index 805ac7d4..00000000 --- a/tests/test_serializer_empty.py +++ /dev/null @@ -1,15 +0,0 @@ -# from django.test import TestCase -# from rest_framework import serializers - - -# class EmptySerializerTestCase(TestCase): -# def test_empty_serializer(self): -# class FooBarSerializer(serializers.Serializer): -# foo = serializers.IntegerField() -# bar = serializers.SerializerMethodField() - -# def get_bar(self, obj): -# return 'bar' - -# serializer = FooBarSerializer() -# self.assertEquals(serializer.data, {'foo': 0}) diff --git a/tests/test_serializer_import.py b/tests/test_serializer_import.py deleted file mode 100644 index d029c3c5..00000000 --- a/tests/test_serializer_import.py +++ /dev/null @@ -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) diff --git a/tests/test_serializer_lists.py b/tests/test_serializer_lists.py new file mode 100644 index 00000000..640067e3 --- /dev/null +++ b/tests/test_serializer_lists.py @@ -0,0 +1,274 @@ +from rest_framework import serializers +from django.utils.datastructures import MultiValueDict + + +class BasicObject: + """ + A mock object for testing serializer save behavior. + """ + def __init__(self, **kwargs): + self._data = kwargs + for key, value in kwargs.items(): + setattr(self, key, value) + + def __eq__(self, other): + if self._data.keys() != other._data.keys(): + return False + for key in self._data.keys(): + if self._data[key] != other._data[key]: + return False + return True + + +class TestListSerializer: + """ + Tests for using a ListSerializer as a top-level serializer. + Note that this is in contrast to using ListSerializer as a field. + """ + + def setup(self): + class IntegerListSerializer(serializers.ListSerializer): + child = serializers.IntegerField() + self.Serializer = IntegerListSerializer + + def test_validate(self): + """ + Validating a list of items should return a list of validated items. + """ + input_data = ["123", "456"] + expected_output = [123, 456] + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output + + def test_validate_html_input(self): + """ + HTML input should be able to mock list structures using [x] style ids. + """ + input_data = MultiValueDict({"[0]": ["123"], "[1]": ["456"]}) + expected_output = [123, 456] + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output + + +class TestListSerializerContainingNestedSerializer: + """ + Tests for using a ListSerializer containing another serializer. + """ + + def setup(self): + class TestSerializer(serializers.Serializer): + integer = serializers.IntegerField() + boolean = serializers.BooleanField() + + def create(self, validated_data): + return BasicObject(**validated_data) + + class ObjectListSerializer(serializers.ListSerializer): + child = TestSerializer() + + self.Serializer = ObjectListSerializer + + def test_validate(self): + """ + Validating a list of dictionaries should return a list of + validated dictionaries. + """ + input_data = [ + {"integer": "123", "boolean": "true"}, + {"integer": "456", "boolean": "false"} + ] + expected_output = [ + {"integer": 123, "boolean": True}, + {"integer": 456, "boolean": False} + ] + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output + + def test_create(self): + """ + Creating from a list of dictionaries should return a list of objects. + """ + input_data = [ + {"integer": "123", "boolean": "true"}, + {"integer": "456", "boolean": "false"} + ] + expected_output = [ + BasicObject(integer=123, boolean=True), + BasicObject(integer=456, boolean=False), + ] + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.save() == expected_output + + def test_serialize(self): + """ + Serialization of a list of objects should return a list of dictionaries. + """ + input_objects = [ + BasicObject(integer=123, boolean=True), + BasicObject(integer=456, boolean=False) + ] + expected_output = [ + {"integer": 123, "boolean": True}, + {"integer": 456, "boolean": False} + ] + serializer = self.Serializer(input_objects) + assert serializer.data == expected_output + + def test_validate_html_input(self): + """ + HTML input should be able to mock list structures using [x] + style prefixes. + """ + input_data = MultiValueDict({ + "[0]integer": ["123"], + "[0]boolean": ["true"], + "[1]integer": ["456"], + "[1]boolean": ["false"] + }) + expected_output = [ + {"integer": 123, "boolean": True}, + {"integer": 456, "boolean": False} + ] + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output + + +class TestNestedListSerializer: + """ + Tests for using a ListSerializer as a field. + """ + + def setup(self): + class TestSerializer(serializers.Serializer): + integers = serializers.ListSerializer(child=serializers.IntegerField()) + booleans = serializers.ListSerializer(child=serializers.BooleanField()) + + def create(self, validated_data): + return BasicObject(**validated_data) + + self.Serializer = TestSerializer + + def test_validate(self): + """ + Validating a list of items should return a list of validated items. + """ + input_data = { + "integers": ["123", "456"], + "booleans": ["true", "false"] + } + expected_output = { + "integers": [123, 456], + "booleans": [True, False] + } + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output + + def test_create(self): + """ + Creation with a list of items return an object with an attribute that + is a list of items. + """ + input_data = { + "integers": ["123", "456"], + "booleans": ["true", "false"] + } + expected_output = BasicObject( + integers=[123, 456], + booleans=[True, False] + ) + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.save() == expected_output + + def test_serialize(self): + """ + Serialization of a list of items should return a list of items. + """ + input_object = BasicObject( + integers=[123, 456], + booleans=[True, False] + ) + expected_output = { + "integers": [123, 456], + "booleans": [True, False] + } + serializer = self.Serializer(input_object) + assert serializer.data == expected_output + + def test_validate_html_input(self): + """ + HTML input should be able to mock list structures using [x] + style prefixes. + """ + input_data = MultiValueDict({ + "integers[0]": ["123"], + "integers[1]": ["456"], + "booleans[0]": ["true"], + "booleans[1]": ["false"] + }) + expected_output = { + "integers": [123, 456], + "booleans": [True, False] + } + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output + + +class TestNestedListOfListsSerializer: + def setup(self): + class TestSerializer(serializers.Serializer): + integers = serializers.ListSerializer( + child=serializers.ListSerializer( + child=serializers.IntegerField() + ) + ) + booleans = serializers.ListSerializer( + child=serializers.ListSerializer( + child=serializers.BooleanField() + ) + ) + + self.Serializer = TestSerializer + + def test_validate(self): + input_data = { + 'integers': [['123', '456'], ['789', '0']], + 'booleans': [['true', 'true'], ['false', 'true']] + } + expected_output = { + "integers": [[123, 456], [789, 0]], + "booleans": [[True, True], [False, True]] + } + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output + + def test_validate_html_input(self): + """ + HTML input should be able to mock lists of lists using [x][y] + style prefixes. + """ + input_data = MultiValueDict({ + "integers[0][0]": ["123"], + "integers[0][1]": ["456"], + "integers[1][0]": ["789"], + "integers[1][1]": ["000"], + "booleans[0][0]": ["true"], + "booleans[0][1]": ["true"], + "booleans[1][0]": ["false"], + "booleans[1][1]": ["true"] + }) + expected_output = { + "integers": [[123, 456], [789, 0]], + "booleans": [[True, True], [False, True]] + } + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output diff --git a/tests/test_serializer_nested.py b/tests/test_serializer_nested.py index b0f64ca7..f5e4b26a 100644 --- a/tests/test_serializer_nested.py +++ b/tests/test_serializer_nested.py @@ -1,349 +1,40 @@ -# """ -# Tests to cover nested serializers. - -# Doesn't cover model serializers. -# """ -# from __future__ import unicode_literals -# from django.test import TestCase -# from rest_framework import serializers -# from . import models - - -# class WritableNestedSerializerBasicTests(TestCase): -# """ -# Tests for deserializing nested entities. -# Basic tests that use serializers that simply restore to dicts. -# """ - -# def setUp(self): -# class TrackSerializer(serializers.Serializer): -# order = serializers.IntegerField() -# title = serializers.CharField(max_length=100) -# duration = serializers.IntegerField() - -# class AlbumSerializer(serializers.Serializer): -# album_name = serializers.CharField(max_length=100) -# artist = serializers.CharField(max_length=100) -# tracks = TrackSerializer(many=True) - -# self.AlbumSerializer = AlbumSerializer - -# def test_nested_validation_success(self): -# """ -# Correct nested serialization should return the input data. -# """ - -# data = { -# 'album_name': 'Discovery', -# 'artist': 'Daft Punk', -# 'tracks': [ -# {'order': 1, 'title': 'One More Time', 'duration': 235}, -# {'order': 2, 'title': 'Aerodynamic', 'duration': 184}, -# {'order': 3, 'title': 'Digital Love', 'duration': 239} -# ] -# } - -# serializer = self.AlbumSerializer(data=data) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, data) - -# def test_nested_validation_error(self): -# """ -# Incorrect nested serialization should return appropriate error data. -# """ - -# data = { -# 'album_name': 'Discovery', -# 'artist': 'Daft Punk', -# 'tracks': [ -# {'order': 1, 'title': 'One More Time', 'duration': 235}, -# {'order': 2, 'title': 'Aerodynamic', 'duration': 184}, -# {'order': 3, 'title': 'Digital Love', 'duration': 'foobar'} -# ] -# } -# expected_errors = { -# 'tracks': [ -# {}, -# {}, -# {'duration': ['Enter a whole number.']} -# ] -# } - -# serializer = self.AlbumSerializer(data=data) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, expected_errors) - -# def test_many_nested_validation_error(self): -# """ -# Incorrect nested serialization should return appropriate error data -# when multiple entities are being deserialized. -# """ - -# data = [ -# { -# 'album_name': 'Russian Red', -# 'artist': 'I Love Your Glasses', -# 'tracks': [ -# {'order': 1, 'title': 'Cigarettes', 'duration': 121}, -# {'order': 2, 'title': 'No Past Land', 'duration': 198}, -# {'order': 3, 'title': 'They Don\'t Believe', 'duration': 191} -# ] -# }, -# { -# 'album_name': 'Discovery', -# 'artist': 'Daft Punk', -# 'tracks': [ -# {'order': 1, 'title': 'One More Time', 'duration': 235}, -# {'order': 2, 'title': 'Aerodynamic', 'duration': 184}, -# {'order': 3, 'title': 'Digital Love', 'duration': 'foobar'} -# ] -# } -# ] -# expected_errors = [ -# {}, -# { -# 'tracks': [ -# {}, -# {}, -# {'duration': ['Enter a whole number.']} -# ] -# } -# ] - -# serializer = self.AlbumSerializer(data=data, many=True) -# self.assertEqual(serializer.is_valid(), False) -# self.assertEqual(serializer.errors, expected_errors) - - -# class WritableNestedSerializerObjectTests(TestCase): -# """ -# Tests for deserializing nested entities. -# These tests use serializers that restore to concrete objects. -# """ - -# def setUp(self): -# # Couple of concrete objects that we're going to deserialize into -# class Track(object): -# def __init__(self, order, title, duration): -# self.order, self.title, self.duration = order, title, duration - -# def __eq__(self, other): -# return ( -# self.order == other.order and -# self.title == other.title and -# self.duration == other.duration -# ) - -# class Album(object): -# def __init__(self, album_name, artist, tracks): -# self.album_name, self.artist, self.tracks = album_name, artist, tracks - -# def __eq__(self, other): -# return ( -# self.album_name == other.album_name and -# self.artist == other.artist and -# self.tracks == other.tracks -# ) - -# # And their corresponding serializers -# class TrackSerializer(serializers.Serializer): -# order = serializers.IntegerField() -# title = serializers.CharField(max_length=100) -# duration = serializers.IntegerField() - -# def restore_object(self, attrs, instance=None): -# return Track(attrs['order'], attrs['title'], attrs['duration']) - -# class AlbumSerializer(serializers.Serializer): -# album_name = serializers.CharField(max_length=100) -# artist = serializers.CharField(max_length=100) -# tracks = TrackSerializer(many=True) - -# def restore_object(self, attrs, instance=None): -# return Album(attrs['album_name'], attrs['artist'], attrs['tracks']) - -# self.Album, self.Track = Album, Track -# self.AlbumSerializer = AlbumSerializer - -# def test_nested_validation_success(self): -# """ -# Correct nested serialization should return a restored object -# that corresponds to the input data. -# """ - -# data = { -# 'album_name': 'Discovery', -# 'artist': 'Daft Punk', -# 'tracks': [ -# {'order': 1, 'title': 'One More Time', 'duration': 235}, -# {'order': 2, 'title': 'Aerodynamic', 'duration': 184}, -# {'order': 3, 'title': 'Digital Love', 'duration': 239} -# ] -# } -# expected_object = self.Album( -# album_name='Discovery', -# artist='Daft Punk', -# tracks=[ -# self.Track(order=1, title='One More Time', duration=235), -# self.Track(order=2, title='Aerodynamic', duration=184), -# self.Track(order=3, title='Digital Love', duration=239), -# ] -# ) - -# serializer = self.AlbumSerializer(data=data) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, expected_object) - -# def test_many_nested_validation_success(self): -# """ -# Correct nested serialization should return multiple restored objects -# that corresponds to the input data when multiple objects are -# being deserialized. -# """ - -# data = [ -# { -# 'album_name': 'Russian Red', -# 'artist': 'I Love Your Glasses', -# 'tracks': [ -# {'order': 1, 'title': 'Cigarettes', 'duration': 121}, -# {'order': 2, 'title': 'No Past Land', 'duration': 198}, -# {'order': 3, 'title': 'They Don\'t Believe', 'duration': 191} -# ] -# }, -# { -# 'album_name': 'Discovery', -# 'artist': 'Daft Punk', -# 'tracks': [ -# {'order': 1, 'title': 'One More Time', 'duration': 235}, -# {'order': 2, 'title': 'Aerodynamic', 'duration': 184}, -# {'order': 3, 'title': 'Digital Love', 'duration': 239} -# ] -# } -# ] -# expected_object = [ -# self.Album( -# album_name='Russian Red', -# artist='I Love Your Glasses', -# tracks=[ -# self.Track(order=1, title='Cigarettes', duration=121), -# self.Track(order=2, title='No Past Land', duration=198), -# self.Track(order=3, title='They Don\'t Believe', duration=191), -# ] -# ), -# self.Album( -# album_name='Discovery', -# artist='Daft Punk', -# tracks=[ -# self.Track(order=1, title='One More Time', duration=235), -# self.Track(order=2, title='Aerodynamic', duration=184), -# self.Track(order=3, title='Digital Love', duration=239), -# ] -# ) -# ] - -# serializer = self.AlbumSerializer(data=data, many=True) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, expected_object) - - -# class ForeignKeyNestedSerializerUpdateTests(TestCase): -# def setUp(self): -# class Artist(object): -# def __init__(self, name): -# self.name = name - -# def __eq__(self, other): -# return self.name == other.name - -# class Album(object): -# def __init__(self, name, artist): -# self.name, self.artist = name, artist - -# def __eq__(self, other): -# return self.name == other.name and self.artist == other.artist - -# class ArtistSerializer(serializers.Serializer): -# name = serializers.CharField() - -# def restore_object(self, attrs, instance=None): -# if instance: -# instance.name = attrs['name'] -# else: -# instance = Artist(attrs['name']) -# return instance - -# class AlbumSerializer(serializers.Serializer): -# name = serializers.CharField() -# by = ArtistSerializer(source='artist') - -# def restore_object(self, attrs, instance=None): -# if instance: -# instance.name = attrs['name'] -# instance.artist = attrs['artist'] -# else: -# instance = Album(attrs['name'], attrs['artist']) -# return instance - -# self.Artist = Artist -# self.Album = Album -# self.AlbumSerializer = AlbumSerializer - -# def test_create_via_foreign_key_with_source(self): -# """ -# Check that we can both *create* and *update* into objects across -# ForeignKeys that have a `source` specified. -# Regression test for #1170 -# """ -# data = { -# 'name': 'Discovery', -# 'by': {'name': 'Daft Punk'}, -# } - -# expected = self.Album(artist=self.Artist('Daft Punk'), name='Discovery') - -# # create -# serializer = self.AlbumSerializer(data=data) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, expected) - -# # update -# original = self.Album(artist=self.Artist('The Bats'), name='Free All the Monsters') -# serializer = self.AlbumSerializer(instance=original, data=data) -# self.assertEqual(serializer.is_valid(), True) -# self.assertEqual(serializer.object, expected) - - -# class NestedModelSerializerUpdateTests(TestCase): -# def test_second_nested_level(self): -# john = models.Person.objects.create(name="john") - -# post = john.blogpost_set.create(title="Test blog post") -# post.blogpostcomment_set.create(text="I hate this blog post") -# post.blogpostcomment_set.create(text="I love this blog post") - -# class BlogPostCommentSerializer(serializers.ModelSerializer): -# class Meta: -# model = models.BlogPostComment - -# class BlogPostSerializer(serializers.ModelSerializer): -# comments = BlogPostCommentSerializer(many=True, source='blogpostcomment_set') - -# class Meta: -# model = models.BlogPost -# fields = ('id', 'title', 'comments') - -# class PersonSerializer(serializers.ModelSerializer): -# posts = BlogPostSerializer(many=True, source='blogpost_set') - -# class Meta: -# model = models.Person -# fields = ('id', 'name', 'age', 'posts') - -# serialize = PersonSerializer(instance=john) -# deserialize = PersonSerializer(data=serialize.data, instance=john) -# self.assertTrue(deserialize.is_valid()) - -# result = deserialize.object -# result.save() -# self.assertEqual(result.id, john.id) +from rest_framework import serializers + + +class TestNestedSerializer: + def setup(self): + class NestedSerializer(serializers.Serializer): + one = serializers.IntegerField(max_value=10) + two = serializers.IntegerField(max_value=10) + + class TestSerializer(serializers.Serializer): + nested = NestedSerializer() + + self.Serializer = TestSerializer + + def test_nested_validate(self): + input_data = { + 'nested': { + 'one': '1', + 'two': '2', + } + } + expected_data = { + 'nested': { + 'one': 1, + 'two': 2, + } + } + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_data + + def test_nested_serialize_empty(self): + expected_data = { + 'nested': { + 'one': None, + 'two': None + } + } + serializer = self.Serializer() + assert serializer.data == expected_data diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 75ee0eaa..b04a937e 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -4,6 +4,7 @@ from django.test import TestCase from rest_framework.test import APIRequestFactory from rest_framework.templatetags.rest_framework import add_query_param, urlize_quoted_links + factory = APIRequestFactory() @@ -49,3 +50,37 @@ class Issue1386Tests(TestCase): # example from issue #1386, this shouldn't raise an exception urlize_quoted_links("asdf:[/p]zxcv.com") + + +class URLizerTests(TestCase): + """ + Test if both JSON and YAML URLs are transformed into links well + """ + def _urlize_dict_check(self, data): + """ + For all items in dict test assert that the value is urlized key + """ + for original, urlized in data.items(): + assert urlize_quoted_links(original, nofollow=False) == urlized + + def test_json_with_url(self): + """ + Test if JSON URLs are transformed into links well + """ + data = {} + data['"url": "http://api/users/1/", '] = \ + '"url": "<a href="http://api/users/1/">http://api/users/1/</a>", ' + data['"foo_set": [\n "http://api/foos/1/"\n], '] = \ + '"foo_set": [\n "<a href="http://api/foos/1/">http://api/foos/1/</a>"\n], ' + self._urlize_dict_check(data) + + def test_yaml_with_url(self): + """ + Test if YAML URLs are transformed into links well + """ + data = {} + data['''{users: 'http://api/users/'}'''] = \ + '''{users: '<a href="http://api/users/">http://api/users/</a>'}''' + data['''foo_set: ['http://api/foos/1/']'''] = \ + '''foo_set: ['<a href="http://api/foos/1/">http://api/foos/1/</a>']''' + self._urlize_dict_check(data) diff --git a/tests/test_testing.py b/tests/test_testing.py index 9c472026..9fd5966e 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -5,6 +5,7 @@ from django.conf.urls import patterns, url from io import BytesIO from django.contrib.auth.models import User +from django.shortcuts import redirect from django.test import TestCase from rest_framework.decorators import api_view from rest_framework.response import Response @@ -28,10 +29,16 @@ def session_view(request): }) +@api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) +def redirect_view(request): + return redirect('/view/') + + urlpatterns = patterns( '', url(r'^view/$', view), url(r'^session-view/$', session_view), + url(r'^redirect-view/$', redirect_view), ) @@ -111,6 +118,46 @@ class TestAPITestClient(TestCase): response = self.client.get('/view/') self.assertEqual(response.data['auth'], b'') + def test_follow_redirect(self): + """ + Follow redirect by setting follow argument. + """ + response = self.client.get('/redirect-view/') + self.assertEqual(response.status_code, 302) + response = self.client.get('/redirect-view/', follow=True) + self.assertIsNotNone(response.redirect_chain) + self.assertEqual(response.status_code, 200) + + response = self.client.post('/redirect-view/') + self.assertEqual(response.status_code, 302) + response = self.client.post('/redirect-view/', follow=True) + self.assertIsNotNone(response.redirect_chain) + self.assertEqual(response.status_code, 200) + + response = self.client.put('/redirect-view/') + self.assertEqual(response.status_code, 302) + response = self.client.put('/redirect-view/', follow=True) + self.assertIsNotNone(response.redirect_chain) + self.assertEqual(response.status_code, 200) + + response = self.client.patch('/redirect-view/') + self.assertEqual(response.status_code, 302) + response = self.client.patch('/redirect-view/', follow=True) + self.assertIsNotNone(response.redirect_chain) + self.assertEqual(response.status_code, 200) + + response = self.client.delete('/redirect-view/') + self.assertEqual(response.status_code, 302) + response = self.client.delete('/redirect-view/', follow=True) + self.assertIsNotNone(response.redirect_chain) + self.assertEqual(response.status_code, 200) + + response = self.client.options('/redirect-view/') + self.assertEqual(response.status_code, 302) + response = self.client.options('/redirect-view/', follow=True) + self.assertIsNotNone(response.redirect_chain) + self.assertEqual(response.status_code, 200) + class TestAPIRequestFactory(TestCase): def test_csrf_exempt_by_default(self): diff --git a/tests/test_urlizer.py b/tests/test_urlizer.py deleted file mode 100644 index a77aa22a..00000000 --- a/tests/test_urlizer.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import unicode_literals -from django.test import TestCase -from rest_framework.templatetags.rest_framework import urlize_quoted_links - - -class URLizerTests(TestCase): - """ - Test if both JSON and YAML URLs are transformed into links well - """ - def _urlize_dict_check(self, data): - """ - For all items in dict test assert that the value is urlized key - """ - for original, urlized in data.items(): - assert urlize_quoted_links(original, nofollow=False) == urlized - - def test_json_with_url(self): - """ - Test if JSON URLs are transformed into links well - """ - data = {} - data['"url": "http://api/users/1/", '] = \ - '"url": "<a href="http://api/users/1/">http://api/users/1/</a>", ' - data['"foo_set": [\n "http://api/foos/1/"\n], '] = \ - '"foo_set": [\n "<a href="http://api/foos/1/">http://api/foos/1/</a>"\n], ' - self._urlize_dict_check(data) - - def test_yaml_with_url(self): - """ - Test if YAML URLs are transformed into links well - """ - data = {} - data['''{users: 'http://api/users/'}'''] = \ - '''{users: '<a href="http://api/users/">http://api/users/</a>'}''' - data['''foo_set: ['http://api/foos/1/']'''] = \ - '''foo_set: ['<a href="http://api/foos/1/">http://api/foos/1/</a>']''' - self._urlize_dict_check(data) diff --git a/tests/test_breadcrumbs.py b/tests/test_utils.py index 780fd5c4..8c286ea4 100644 --- a/tests/test_breadcrumbs.py +++ b/tests/test_utils.py @@ -1,8 +1,14 @@ from __future__ import unicode_literals +from django.core.exceptions import ImproperlyConfigured from django.conf.urls import patterns, url from django.test import TestCase +from django.utils import six +from rest_framework.utils.model_meta import _resolve_model from rest_framework.utils.breadcrumbs import get_breadcrumbs from rest_framework.views import APIView +from tests.models import BasicModel + +import rest_framework.utils.model_meta class Root(APIView): @@ -24,6 +30,7 @@ class NestedResourceRoot(APIView): class NestedResourceInstance(APIView): pass + urlpatterns = patterns( '', url(r'^$', Root.as_view()), @@ -35,9 +42,10 @@ urlpatterns = patterns( class BreadcrumbTests(TestCase): - """Tests the breadcrumb functionality used by the HTML renderer.""" - - urls = 'tests.test_breadcrumbs' + """ + Tests the breadcrumb functionality used by the HTML renderer. + """ + urls = 'tests.test_utils' def test_root_breadcrumbs(self): url = '/' @@ -98,3 +106,61 @@ class BreadcrumbTests(TestCase): get_breadcrumbs(url), [('Root', '/')] ) + + +class ResolveModelTests(TestCase): + """ + `_resolve_model` should return a Django model class given the + provided argument is a Django model class itself, or a properly + formatted string representation of one. + """ + def test_resolve_django_model(self): + resolved_model = _resolve_model(BasicModel) + self.assertEqual(resolved_model, BasicModel) + + def test_resolve_string_representation(self): + resolved_model = _resolve_model('tests.BasicModel') + self.assertEqual(resolved_model, BasicModel) + + def test_resolve_unicode_representation(self): + resolved_model = _resolve_model(six.text_type('tests.BasicModel')) + self.assertEqual(resolved_model, BasicModel) + + def test_resolve_non_django_model(self): + with self.assertRaises(ValueError): + _resolve_model(TestCase) + + def test_resolve_improper_string_representation(self): + with self.assertRaises(ValueError): + _resolve_model('BasicModel') + + +class ResolveModelWithPatchedDjangoTests(TestCase): + """ + Test coverage for when Django's `get_model` returns `None`. + + Under certain circumstances Django may return `None` with `get_model`: + http://git.io/get-model-source + + It usually happens with circular imports so it is important that DRF + excepts early, otherwise fault happens downstream and is much more + difficult to debug. + + """ + + def setUp(self): + """Monkeypatch get_model.""" + self.get_model = rest_framework.utils.model_meta.models.get_model + + def get_model(app_label, model_name): + return None + + rest_framework.utils.model_meta.models.get_model = get_model + + def tearDown(self): + """Revert monkeypatching.""" + rest_framework.utils.model_meta.models.get_model = self.get_model + + def test_blows_up_if_model_does_not_resolve(self): + with self.assertRaises(ImproperlyConfigured): + _resolve_model('tests.BasicModel') diff --git a/tests/test_validation.py b/tests/test_validation.py index ce39714d..4234efd3 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -1,10 +1,10 @@ from __future__ import unicode_literals -from django.core.validators import MaxValueValidator -from django.core.exceptions import ValidationError +from django.core.validators import RegexValidator, MaxValueValidator from django.db import models from django.test import TestCase from rest_framework import generics, serializers, status from rest_framework.test import APIRequestFactory +import re factory = APIRequestFactory() @@ -88,8 +88,11 @@ class TestAvoidValidation(TestCase): def test_serializer_errors_has_only_invalid_data_error(self): serializer = ValidationSerializer(data='invalid data') self.assertFalse(serializer.is_valid()) - self.assertDictEqual(serializer.errors, - {'non_field_errors': ['Invalid data']}) + self.assertDictEqual(serializer.errors, { + 'non_field_errors': [ + 'Invalid data. Expected a dictionary, but got %s.' % type('').__name__ + ] + }) # regression tests for issue: 1493 @@ -159,16 +162,22 @@ class TestChoiceFieldChoicesValidate(TestCase): value = self.CHOICES[0][0] try: f.to_internal_value(value) - except ValidationError: + except serializers.ValidationError: self.fail("Value %s does not validate" % str(value)) - # def test_nested_choices(self): - # """ - # Make sure a nested value for choices works as expected. - # """ - # f = serializers.ChoiceField(choices=self.CHOICES_NESTED) - # value = self.CHOICES_NESTED[0][1][0][0] - # try: - # f.to_native(value) - # except ValidationError: - # self.fail("Value %s does not validate" % str(value)) + +class RegexSerializer(serializers.Serializer): + pin = serializers.CharField( + validators=[RegexValidator(regex=re.compile('^[0-9]{4,6}$'), + message='A PIN is 4-6 digits')]) + +expected_repr = """ +RegexSerializer(): + pin = CharField(validators=[<django.core.validators.RegexValidator object>]) +""".strip() + + +class TestRegexSerializer(TestCase): + def test_regex_repr(self): + serializer_repr = repr(RegexSerializer()) + assert serializer_repr == expected_repr diff --git a/tests/test_validators.py b/tests/test_validators.py new file mode 100644 index 00000000..1df0641c --- /dev/null +++ b/tests/test_validators.py @@ -0,0 +1,273 @@ +from django.db import models +from django.test import TestCase +from rest_framework import serializers +import datetime + + +def dedent(blocktext): + return '\n'.join([line[12:] for line in blocktext.splitlines()[1:-1]]) + + +# Tests for `UniqueValidator` +# --------------------------- + +class UniquenessModel(models.Model): + username = models.CharField(unique=True, max_length=100) + + +class UniquenessSerializer(serializers.ModelSerializer): + class Meta: + model = UniquenessModel + + +class TestUniquenessValidation(TestCase): + def setUp(self): + self.instance = UniquenessModel.objects.create(username='existing') + + def test_repr(self): + serializer = UniquenessSerializer() + expected = dedent(""" + UniquenessSerializer(): + id = IntegerField(label='ID', read_only=True) + username = CharField(max_length=100, validators=[<UniqueValidator(queryset=UniquenessModel.objects.all())>]) + """) + assert repr(serializer) == expected + + def test_is_not_unique(self): + data = {'username': 'existing'} + serializer = UniquenessSerializer(data=data) + assert not serializer.is_valid() + assert serializer.errors == {'username': ['This field must be unique.']} + + def test_is_unique(self): + data = {'username': 'other'} + serializer = UniquenessSerializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == {'username': 'other'} + + def test_updated_instance_excluded(self): + data = {'username': 'existing'} + serializer = UniquenessSerializer(self.instance, data=data) + assert serializer.is_valid() + assert serializer.validated_data == {'username': 'existing'} + + +# Tests for `UniqueTogetherValidator` +# ----------------------------------- + +class UniquenessTogetherModel(models.Model): + race_name = models.CharField(max_length=100) + position = models.IntegerField() + + class Meta: + unique_together = ('race_name', 'position') + + +class UniquenessTogetherSerializer(serializers.ModelSerializer): + class Meta: + model = UniquenessTogetherModel + + +class TestUniquenessTogetherValidation(TestCase): + def setUp(self): + self.instance = UniquenessTogetherModel.objects.create( + race_name='example', + position=1 + ) + UniquenessTogetherModel.objects.create( + race_name='example', + position=2 + ) + UniquenessTogetherModel.objects.create( + race_name='other', + position=1 + ) + + def test_repr(self): + serializer = UniquenessTogetherSerializer() + expected = dedent(""" + UniquenessTogetherSerializer(): + id = IntegerField(label='ID', read_only=True) + race_name = CharField(max_length=100, required=True) + position = IntegerField(required=True) + class Meta: + validators = [<UniqueTogetherValidator(queryset=UniquenessTogetherModel.objects.all(), fields=('race_name', 'position'))>] + """) + assert repr(serializer) == expected + + def test_is_not_unique_together(self): + """ + Failing unique together validation should result in non field errors. + """ + data = {'race_name': 'example', 'position': 2} + serializer = UniquenessTogetherSerializer(data=data) + assert not serializer.is_valid() + assert serializer.errors == { + 'non_field_errors': [ + 'The fields race_name, position must make a unique set.' + ] + } + + def test_is_unique_together(self): + """ + In a unique together validation, one field may be non-unique + so long as the set as a whole is unique. + """ + data = {'race_name': 'other', 'position': 2} + serializer = UniquenessTogetherSerializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == { + 'race_name': 'other', + 'position': 2 + } + + def test_updated_instance_excluded_from_unique_together(self): + """ + When performing an update, the existing instance does not count + as a match against uniqueness. + """ + data = {'race_name': 'example', 'position': 1} + serializer = UniquenessTogetherSerializer(self.instance, data=data) + assert serializer.is_valid() + assert serializer.validated_data == { + 'race_name': 'example', + 'position': 1 + } + + def test_unique_together_is_required(self): + """ + In a unique together validation, all fields are required. + """ + data = {'position': 2} + serializer = UniquenessTogetherSerializer(data=data, partial=True) + assert not serializer.is_valid() + assert serializer.errors == { + 'race_name': ['This field is required.'] + } + + def test_ignore_excluded_fields(self): + """ + When model fields are not included in a serializer, then uniqueness + validtors should not be added for that field. + """ + class ExcludedFieldSerializer(serializers.ModelSerializer): + class Meta: + model = UniquenessTogetherModel + fields = ('id', 'race_name',) + serializer = ExcludedFieldSerializer() + expected = dedent(""" + ExcludedFieldSerializer(): + id = IntegerField(label='ID', read_only=True) + race_name = CharField(max_length=100) + """) + assert repr(serializer) == expected + + +# Tests for `UniqueForDateValidator` +# ---------------------------------- + +class UniqueForDateModel(models.Model): + slug = models.CharField(max_length=100, unique_for_date='published') + published = models.DateField() + + +class UniqueForDateSerializer(serializers.ModelSerializer): + class Meta: + model = UniqueForDateModel + + +class TestUniquenessForDateValidation(TestCase): + def setUp(self): + self.instance = UniqueForDateModel.objects.create( + slug='existing', + published='2000-01-01' + ) + + def test_repr(self): + serializer = UniqueForDateSerializer() + expected = dedent(""" + UniqueForDateSerializer(): + id = IntegerField(label='ID', read_only=True) + slug = CharField(max_length=100) + published = DateField(required=True) + class Meta: + validators = [<UniqueForDateValidator(queryset=UniqueForDateModel.objects.all(), field='slug', date_field='published')>] + """) + assert repr(serializer) == expected + + def test_is_not_unique_for_date(self): + """ + Failing unique for date validation should result in field error. + """ + data = {'slug': 'existing', 'published': '2000-01-01'} + serializer = UniqueForDateSerializer(data=data) + assert not serializer.is_valid() + assert serializer.errors == { + 'slug': ['This field must be unique for the "published" date.'] + } + + def test_is_unique_for_date(self): + """ + Passing unique for date validation. + """ + data = {'slug': 'existing', 'published': '2000-01-02'} + serializer = UniqueForDateSerializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == { + 'slug': 'existing', + 'published': datetime.date(2000, 1, 2) + } + + def test_updated_instance_excluded_from_unique_for_date(self): + """ + When performing an update, the existing instance does not count + as a match against unique_for_date. + """ + data = {'slug': 'existing', 'published': '2000-01-01'} + serializer = UniqueForDateSerializer(instance=self.instance, data=data) + assert serializer.is_valid() + assert serializer.validated_data == { + 'slug': 'existing', + 'published': datetime.date(2000, 1, 1) + } + + +class HiddenFieldUniqueForDateModel(models.Model): + slug = models.CharField(max_length=100, unique_for_date='published') + published = models.DateTimeField(auto_now_add=True) + + +class TestHiddenFieldUniquenessForDateValidation(TestCase): + def test_repr_date_field_not_included(self): + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = HiddenFieldUniqueForDateModel + fields = ('id', 'slug') + + serializer = TestSerializer() + expected = dedent(""" + TestSerializer(): + id = IntegerField(label='ID', read_only=True) + slug = CharField(max_length=100) + published = HiddenField(default=CreateOnlyDefault(<function now>)) + class Meta: + validators = [<UniqueForDateValidator(queryset=HiddenFieldUniqueForDateModel.objects.all(), field='slug', date_field='published')>] + """) + assert repr(serializer) == expected + + def test_repr_date_field_included(self): + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = HiddenFieldUniqueForDateModel + fields = ('id', 'slug', 'published') + + serializer = TestSerializer() + expected = dedent(""" + TestSerializer(): + id = IntegerField(label='ID', read_only=True) + slug = CharField(max_length=100) + published = DateTimeField(default=CreateOnlyDefault(<function now>), read_only=True) + class Meta: + validators = [<UniqueForDateValidator(queryset=HiddenFieldUniqueForDateModel.objects.all(), field='slug', date_field='published')>] + """) + assert repr(serializer) == expected diff --git a/tests/users/__init__.py b/tests/users/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/tests/users/__init__.py +++ /dev/null diff --git a/tests/users/models.py b/tests/users/models.py deleted file mode 100644 index 128bac90..00000000 --- a/tests/users/models.py +++ /dev/null @@ -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) diff --git a/tests/users/serializers.py b/tests/users/serializers.py deleted file mode 100644 index 4893ddb3..00000000 --- a/tests/users/serializers.py +++ /dev/null @@ -1,8 +0,0 @@ -from rest_framework import serializers - -from tests.users.models import User - - -class UserSerializer(serializers.ModelSerializer): - class Meta: - model = User |
