More test passing

This commit is contained in:
Tom Christie 2014-09-03 16:34:09 +01:00
parent f2852811f9
commit c1036c1753
7 changed files with 2516 additions and 2462 deletions

View File

@ -1,6 +1,7 @@
from rest_framework.fields import Field from rest_framework.fields import Field
from rest_framework.reverse import reverse
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import resolve, get_script_prefix from django.core.urlresolvers import resolve, get_script_prefix, NoReverseMatch
from rest_framework.compat import urlparse from rest_framework.compat import urlparse
@ -100,11 +101,64 @@ class HyperlinkedIdentityField(RelatedField):
lookup_field = 'pk' lookup_field = 'pk'
def __init__(self, **kwargs): def __init__(self, **kwargs):
kwargs['read_only'] = True
self.view_name = kwargs.pop('view_name') self.view_name = kwargs.pop('view_name')
self.lookup_field = kwargs.pop('lookup_field', self.lookup_field) self.lookup_field = kwargs.pop('lookup_field', self.lookup_field)
self.lookup_url_kwarg = kwargs.pop('lookup_url_kwarg', self.lookup_field) self.lookup_url_kwarg = kwargs.pop('lookup_url_kwarg', self.lookup_field)
super(HyperlinkedIdentityField, self).__init__(**kwargs) super(HyperlinkedIdentityField, self).__init__(**kwargs)
def get_attribute(self, instance):
return instance
def to_primative(self, value):
request = self.context.get('request', None)
format = self.context.get('format', None)
assert request is not None, (
"`HyperlinkedIdentityField` requires the request in the serializer"
" context. Add `context={'request': request}` when instantiating "
"the serializer."
)
# By default use whatever format is given for the current context
# unless the target is a different type to the source.
#
# Eg. Consider a HyperlinkedIdentityField pointing from a json
# representation to an html property of that representation...
#
# '/snippets/1/' should link to '/snippets/1/highlight/'
# ...but...
# '/snippets/1/.json' should link to '/snippets/1/highlight/.html'
if format and self.format and self.format != format:
format = self.format
# Return the hyperlink, or error if incorrectly configured.
try:
return self.get_url(value, self.view_name, request, format)
except NoReverseMatch:
msg = (
'Could not resolve URL for hyperlinked relationship using '
'view name "%s". You may have failed to include the related '
'model in your API, or incorrectly configured the '
'`lookup_field` attribute on this field.'
)
raise Exception(msg % self.view_name)
def get_url(self, obj, view_name, request, format):
"""
Given an object, return the URL that hyperlinks to the object.
May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
attributes are not configured to correctly match the URL conf.
"""
# Unsaved objects will not yet have a valid URL.
if obj.pk is None:
return None
lookup_value = getattr(obj, self.lookup_field)
kwargs = {self.lookup_url_kwarg: lookup_value}
return reverse(view_name, kwargs=kwargs, request=request, format=format)
class SlugRelatedField(RelatedField): class SlugRelatedField(RelatedField):
def __init__(self, **kwargs): def __init__(self, **kwargs):

File diff suppressed because it is too large Load Diff

View File

@ -1,278 +1,278 @@
""" # """
Tests to cover bulk create and update using serializers. # Tests to cover bulk create and update using serializers.
""" # """
from __future__ import unicode_literals # from __future__ import unicode_literals
from django.test import TestCase # from django.test import TestCase
from rest_framework import serializers # from rest_framework import serializers
class BulkCreateSerializerTests(TestCase): # class BulkCreateSerializerTests(TestCase):
""" # """
Creating multiple instances using serializers. # Creating multiple instances using serializers.
""" # """
def setUp(self): # def setUp(self):
class BookSerializer(serializers.Serializer): # class BookSerializer(serializers.Serializer):
id = serializers.IntegerField() # id = serializers.IntegerField()
title = serializers.CharField(max_length=100) # title = serializers.CharField(max_length=100)
author = serializers.CharField(max_length=100) # author = serializers.CharField(max_length=100)
self.BookSerializer = BookSerializer # self.BookSerializer = BookSerializer
def test_bulk_create_success(self): # def test_bulk_create_success(self):
""" # """
Correct bulk update serialization should return the input data. # Correct bulk update serialization should return the input data.
""" # """
data = [ # data = [
{ # {
'id': 0, # 'id': 0,
'title': 'The electric kool-aid acid test', # 'title': 'The electric kool-aid acid test',
'author': 'Tom Wolfe' # 'author': 'Tom Wolfe'
}, { # }, {
'id': 1, # 'id': 1,
'title': 'If this is a man', # 'title': 'If this is a man',
'author': 'Primo Levi' # 'author': 'Primo Levi'
}, { # }, {
'id': 2, # 'id': 2,
'title': 'The wind-up bird chronicle', # 'title': 'The wind-up bird chronicle',
'author': 'Haruki Murakami' # 'author': 'Haruki Murakami'
} # }
] # ]
serializer = self.BookSerializer(data=data, many=True) # serializer = self.BookSerializer(data=data, many=True)
self.assertEqual(serializer.is_valid(), True) # self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, data) # self.assertEqual(serializer.object, data)
def test_bulk_create_errors(self): # def test_bulk_create_errors(self):
""" # """
Correct bulk update serialization should return the input data. # Correct bulk update serialization should return the input data.
""" # """
data = [ # data = [
{ # {
'id': 0, # 'id': 0,
'title': 'The electric kool-aid acid test', # 'title': 'The electric kool-aid acid test',
'author': 'Tom Wolfe' # 'author': 'Tom Wolfe'
}, { # }, {
'id': 1, # 'id': 1,
'title': 'If this is a man', # 'title': 'If this is a man',
'author': 'Primo Levi' # 'author': 'Primo Levi'
}, { # }, {
'id': 'foo', # 'id': 'foo',
'title': 'The wind-up bird chronicle', # 'title': 'The wind-up bird chronicle',
'author': 'Haruki Murakami' # 'author': 'Haruki Murakami'
} # }
] # ]
expected_errors = [ # expected_errors = [
{}, # {},
{}, # {},
{'id': ['Enter a whole number.']} # {'id': ['Enter a whole number.']}
] # ]
serializer = self.BookSerializer(data=data, many=True) # serializer = self.BookSerializer(data=data, many=True)
self.assertEqual(serializer.is_valid(), False) # self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, expected_errors) # self.assertEqual(serializer.errors, expected_errors)
def test_invalid_list_datatype(self): # def test_invalid_list_datatype(self):
""" # """
Data containing list of incorrect data type should return errors. # Data containing list of incorrect data type should return errors.
""" # """
data = ['foo', 'bar', 'baz'] # data = ['foo', 'bar', 'baz']
serializer = self.BookSerializer(data=data, many=True) # serializer = self.BookSerializer(data=data, many=True)
self.assertEqual(serializer.is_valid(), False) # self.assertEqual(serializer.is_valid(), False)
expected_errors = [ # expected_errors = [
{'non_field_errors': ['Invalid data']}, # {'non_field_errors': ['Invalid data']},
{'non_field_errors': ['Invalid data']}, # {'non_field_errors': ['Invalid data']},
{'non_field_errors': ['Invalid data']} # {'non_field_errors': ['Invalid data']}
] # ]
self.assertEqual(serializer.errors, expected_errors) # self.assertEqual(serializer.errors, expected_errors)
def test_invalid_single_datatype(self): # def test_invalid_single_datatype(self):
""" # """
Data containing a single incorrect data type should return errors. # Data containing a single incorrect data type should return errors.
""" # """
data = 123 # data = 123
serializer = self.BookSerializer(data=data, many=True) # serializer = self.BookSerializer(data=data, many=True)
self.assertEqual(serializer.is_valid(), False) # self.assertEqual(serializer.is_valid(), False)
expected_errors = {'non_field_errors': ['Expected a list of items.']} # expected_errors = {'non_field_errors': ['Expected a list of items.']}
self.assertEqual(serializer.errors, expected_errors) # self.assertEqual(serializer.errors, expected_errors)
def test_invalid_single_object(self): # def test_invalid_single_object(self):
""" # """
Data containing only a single object, instead of a list of objects # Data containing only a single object, instead of a list of objects
should return errors. # should return errors.
""" # """
data = { # data = {
'id': 0, # 'id': 0,
'title': 'The electric kool-aid acid test', # 'title': 'The electric kool-aid acid test',
'author': 'Tom Wolfe' # 'author': 'Tom Wolfe'
} # }
serializer = self.BookSerializer(data=data, many=True) # serializer = self.BookSerializer(data=data, many=True)
self.assertEqual(serializer.is_valid(), False) # self.assertEqual(serializer.is_valid(), False)
expected_errors = {'non_field_errors': ['Expected a list of items.']} # expected_errors = {'non_field_errors': ['Expected a list of items.']}
self.assertEqual(serializer.errors, expected_errors) # self.assertEqual(serializer.errors, expected_errors)
class BulkUpdateSerializerTests(TestCase): # class BulkUpdateSerializerTests(TestCase):
""" # """
Updating multiple instances using serializers. # Updating multiple instances using serializers.
""" # """
def setUp(self): # def setUp(self):
class Book(object): # class Book(object):
""" # """
A data type that can be persisted to a mock storage backend # A data type that can be persisted to a mock storage backend
with `.save()` and `.delete()`. # with `.save()` and `.delete()`.
""" # """
object_map = {} # object_map = {}
def __init__(self, id, title, author): # def __init__(self, id, title, author):
self.id = id # self.id = id
self.title = title # self.title = title
self.author = author # self.author = author
def save(self): # def save(self):
Book.object_map[self.id] = self # Book.object_map[self.id] = self
def delete(self): # def delete(self):
del Book.object_map[self.id] # del Book.object_map[self.id]
class BookSerializer(serializers.Serializer): # class BookSerializer(serializers.Serializer):
id = serializers.IntegerField() # id = serializers.IntegerField()
title = serializers.CharField(max_length=100) # title = serializers.CharField(max_length=100)
author = serializers.CharField(max_length=100) # author = serializers.CharField(max_length=100)
def restore_object(self, attrs, instance=None): # def restore_object(self, attrs, instance=None):
if instance: # if instance:
instance.id = attrs['id'] # instance.id = attrs['id']
instance.title = attrs['title'] # instance.title = attrs['title']
instance.author = attrs['author'] # instance.author = attrs['author']
return instance # return instance
return Book(**attrs) # return Book(**attrs)
self.Book = Book # self.Book = Book
self.BookSerializer = BookSerializer # self.BookSerializer = BookSerializer
data = [ # data = [
{ # {
'id': 0, # 'id': 0,
'title': 'The electric kool-aid acid test', # 'title': 'The electric kool-aid acid test',
'author': 'Tom Wolfe' # 'author': 'Tom Wolfe'
}, { # }, {
'id': 1, # 'id': 1,
'title': 'If this is a man', # 'title': 'If this is a man',
'author': 'Primo Levi' # 'author': 'Primo Levi'
}, { # }, {
'id': 2, # 'id': 2,
'title': 'The wind-up bird chronicle', # 'title': 'The wind-up bird chronicle',
'author': 'Haruki Murakami' # 'author': 'Haruki Murakami'
} # }
] # ]
for item in data: # for item in data:
book = Book(item['id'], item['title'], item['author']) # book = Book(item['id'], item['title'], item['author'])
book.save() # book.save()
def books(self): # def books(self):
""" # """
Return all the objects in the mock storage backend. # Return all the objects in the mock storage backend.
""" # """
return self.Book.object_map.values() # return self.Book.object_map.values()
def test_bulk_update_success(self): # def test_bulk_update_success(self):
""" # """
Correct bulk update serialization should return the input data. # Correct bulk update serialization should return the input data.
""" # """
data = [ # data = [
{ # {
'id': 0, # 'id': 0,
'title': 'The electric kool-aid acid test', # 'title': 'The electric kool-aid acid test',
'author': 'Tom Wolfe' # 'author': 'Tom Wolfe'
}, { # }, {
'id': 2, # 'id': 2,
'title': 'Kafka on the shore', # 'title': 'Kafka on the shore',
'author': 'Haruki Murakami' # 'author': 'Haruki Murakami'
} # }
] # ]
serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True) # serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True)
self.assertEqual(serializer.is_valid(), True) # self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data, data) # self.assertEqual(serializer.data, data)
serializer.save() # serializer.save()
new_data = self.BookSerializer(self.books(), many=True).data # new_data = self.BookSerializer(self.books(), many=True).data
self.assertEqual(data, new_data) # self.assertEqual(data, new_data)
def test_bulk_update_and_create(self): # def test_bulk_update_and_create(self):
""" # """
Bulk update serialization may also include created items. # Bulk update serialization may also include created items.
""" # """
data = [ # data = [
{ # {
'id': 0, # 'id': 0,
'title': 'The electric kool-aid acid test', # 'title': 'The electric kool-aid acid test',
'author': 'Tom Wolfe' # 'author': 'Tom Wolfe'
}, { # }, {
'id': 3, # 'id': 3,
'title': 'Kafka on the shore', # 'title': 'Kafka on the shore',
'author': 'Haruki Murakami' # 'author': 'Haruki Murakami'
} # }
] # ]
serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True) # serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True)
self.assertEqual(serializer.is_valid(), True) # self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data, data) # self.assertEqual(serializer.data, data)
serializer.save() # serializer.save()
new_data = self.BookSerializer(self.books(), many=True).data # new_data = self.BookSerializer(self.books(), many=True).data
self.assertEqual(data, new_data) # self.assertEqual(data, new_data)
def test_bulk_update_invalid_create(self): # def test_bulk_update_invalid_create(self):
""" # """
Bulk update serialization without allow_add_remove may not create items. # Bulk update serialization without allow_add_remove may not create items.
""" # """
data = [ # data = [
{ # {
'id': 0, # 'id': 0,
'title': 'The electric kool-aid acid test', # 'title': 'The electric kool-aid acid test',
'author': 'Tom Wolfe' # 'author': 'Tom Wolfe'
}, { # }, {
'id': 3, # 'id': 3,
'title': 'Kafka on the shore', # 'title': 'Kafka on the shore',
'author': 'Haruki Murakami' # 'author': 'Haruki Murakami'
} # }
] # ]
expected_errors = [ # expected_errors = [
{}, # {},
{'non_field_errors': ['Cannot create a new item, only existing items may be updated.']} # {'non_field_errors': ['Cannot create a new item, only existing items may be updated.']}
] # ]
serializer = self.BookSerializer(self.books(), data=data, many=True) # serializer = self.BookSerializer(self.books(), data=data, many=True)
self.assertEqual(serializer.is_valid(), False) # self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, expected_errors) # self.assertEqual(serializer.errors, expected_errors)
def test_bulk_update_error(self): # def test_bulk_update_error(self):
""" # """
Incorrect bulk update serialization should return error data. # Incorrect bulk update serialization should return error data.
""" # """
data = [ # data = [
{ # {
'id': 0, # 'id': 0,
'title': 'The electric kool-aid acid test', # 'title': 'The electric kool-aid acid test',
'author': 'Tom Wolfe' # 'author': 'Tom Wolfe'
}, { # }, {
'id': 'foo', # 'id': 'foo',
'title': 'Kafka on the shore', # 'title': 'Kafka on the shore',
'author': 'Haruki Murakami' # 'author': 'Haruki Murakami'
} # }
] # ]
expected_errors = [ # expected_errors = [
{}, # {},
{'id': ['Enter a whole number.']} # {'id': ['Enter a whole number.']}
] # ]
serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True) # serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True)
self.assertEqual(serializer.is_valid(), False) # self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, expected_errors) # self.assertEqual(serializer.errors, expected_errors)

View File

@ -1,15 +1,15 @@
from django.test import TestCase # from django.test import TestCase
from rest_framework import serializers # from rest_framework import serializers
class EmptySerializerTestCase(TestCase): # class EmptySerializerTestCase(TestCase):
def test_empty_serializer(self): # def test_empty_serializer(self):
class FooBarSerializer(serializers.Serializer): # class FooBarSerializer(serializers.Serializer):
foo = serializers.IntegerField() # foo = serializers.IntegerField()
bar = serializers.SerializerMethodField('get_bar') # bar = serializers.SerializerMethodField('get_bar')
def get_bar(self, obj): # def get_bar(self, obj):
return 'bar' # return 'bar'
serializer = FooBarSerializer() # serializer = FooBarSerializer()
self.assertEquals(serializer.data, {'foo': 0}) # self.assertEquals(serializer.data, {'foo': 0})

View File

@ -1,19 +1,19 @@
from django.test import TestCase # from django.test import TestCase
from rest_framework import serializers # from rest_framework import serializers
from tests.accounts.serializers import AccountSerializer # from tests.accounts.serializers import AccountSerializer
class ImportingModelSerializerTests(TestCase): # class ImportingModelSerializerTests(TestCase):
""" # """
In some situations like, GH #1225, it is possible, especially in # In some situations like, GH #1225, it is possible, especially in
testing, to import a serializer who's related models have not yet # testing, to import a serializer who's related models have not yet
been resolved by Django. `AccountSerializer` is an example of such # been resolved by Django. `AccountSerializer` is an example of such
a serializer (imported at the top of this file). # a serializer (imported at the top of this file).
""" # """
def test_import_model_serializer(self): # def test_import_model_serializer(self):
""" # """
The serializer at the top of this file should have been # The serializer at the top of this file should have been
imported successfully, and we should be able to instantiate it. # imported successfully, and we should be able to instantiate it.
""" # """
self.assertIsInstance(AccountSerializer(), serializers.ModelSerializer) # self.assertIsInstance(AccountSerializer(), serializers.ModelSerializer)

View File

@ -1,349 +1,349 @@
""" # """
Tests to cover nested serializers. # Tests to cover nested serializers.
Doesn't cover model serializers. # Doesn't cover model serializers.
""" # """
from __future__ import unicode_literals # from __future__ import unicode_literals
from django.test import TestCase # from django.test import TestCase
from rest_framework import serializers # from rest_framework import serializers
from . import models # from . import models
class WritableNestedSerializerBasicTests(TestCase): # class WritableNestedSerializerBasicTests(TestCase):
""" # """
Tests for deserializing nested entities. # Tests for deserializing nested entities.
Basic tests that use serializers that simply restore to dicts. # Basic tests that use serializers that simply restore to dicts.
""" # """
def setUp(self): # def setUp(self):
class TrackSerializer(serializers.Serializer): # class TrackSerializer(serializers.Serializer):
order = serializers.IntegerField() # order = serializers.IntegerField()
title = serializers.CharField(max_length=100) # title = serializers.CharField(max_length=100)
duration = serializers.IntegerField() # duration = serializers.IntegerField()
class AlbumSerializer(serializers.Serializer): # class AlbumSerializer(serializers.Serializer):
album_name = serializers.CharField(max_length=100) # album_name = serializers.CharField(max_length=100)
artist = serializers.CharField(max_length=100) # artist = serializers.CharField(max_length=100)
tracks = TrackSerializer(many=True) # tracks = TrackSerializer(many=True)
self.AlbumSerializer = AlbumSerializer # self.AlbumSerializer = AlbumSerializer
def test_nested_validation_success(self): # def test_nested_validation_success(self):
""" # """
Correct nested serialization should return the input data. # Correct nested serialization should return the input data.
""" # """
data = { # data = {
'album_name': 'Discovery', # 'album_name': 'Discovery',
'artist': 'Daft Punk', # 'artist': 'Daft Punk',
'tracks': [ # 'tracks': [
{'order': 1, 'title': 'One More Time', 'duration': 235}, # {'order': 1, 'title': 'One More Time', 'duration': 235},
{'order': 2, 'title': 'Aerodynamic', 'duration': 184}, # {'order': 2, 'title': 'Aerodynamic', 'duration': 184},
{'order': 3, 'title': 'Digital Love', 'duration': 239} # {'order': 3, 'title': 'Digital Love', 'duration': 239}
] # ]
} # }
serializer = self.AlbumSerializer(data=data) # serializer = self.AlbumSerializer(data=data)
self.assertEqual(serializer.is_valid(), True) # self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, data) # self.assertEqual(serializer.object, data)
def test_nested_validation_error(self): # def test_nested_validation_error(self):
""" # """
Incorrect nested serialization should return appropriate error data. # Incorrect nested serialization should return appropriate error data.
""" # """
data = { # data = {
'album_name': 'Discovery', # 'album_name': 'Discovery',
'artist': 'Daft Punk', # 'artist': 'Daft Punk',
'tracks': [ # 'tracks': [
{'order': 1, 'title': 'One More Time', 'duration': 235}, # {'order': 1, 'title': 'One More Time', 'duration': 235},
{'order': 2, 'title': 'Aerodynamic', 'duration': 184}, # {'order': 2, 'title': 'Aerodynamic', 'duration': 184},
{'order': 3, 'title': 'Digital Love', 'duration': 'foobar'} # {'order': 3, 'title': 'Digital Love', 'duration': 'foobar'}
] # ]
} # }
expected_errors = { # expected_errors = {
'tracks': [ # 'tracks': [
{}, # {},
{}, # {},
{'duration': ['Enter a whole number.']} # {'duration': ['Enter a whole number.']}
] # ]
} # }
serializer = self.AlbumSerializer(data=data) # serializer = self.AlbumSerializer(data=data)
self.assertEqual(serializer.is_valid(), False) # self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, expected_errors) # self.assertEqual(serializer.errors, expected_errors)
def test_many_nested_validation_error(self): # def test_many_nested_validation_error(self):
""" # """
Incorrect nested serialization should return appropriate error data # Incorrect nested serialization should return appropriate error data
when multiple entities are being deserialized. # when multiple entities are being deserialized.
""" # """
data = [ # data = [
{ # {
'album_name': 'Russian Red', # 'album_name': 'Russian Red',
'artist': 'I Love Your Glasses', # 'artist': 'I Love Your Glasses',
'tracks': [ # 'tracks': [
{'order': 1, 'title': 'Cigarettes', 'duration': 121}, # {'order': 1, 'title': 'Cigarettes', 'duration': 121},
{'order': 2, 'title': 'No Past Land', 'duration': 198}, # {'order': 2, 'title': 'No Past Land', 'duration': 198},
{'order': 3, 'title': 'They Don\'t Believe', 'duration': 191} # {'order': 3, 'title': 'They Don\'t Believe', 'duration': 191}
] # ]
}, # },
{ # {
'album_name': 'Discovery', # 'album_name': 'Discovery',
'artist': 'Daft Punk', # 'artist': 'Daft Punk',
'tracks': [ # 'tracks': [
{'order': 1, 'title': 'One More Time', 'duration': 235}, # {'order': 1, 'title': 'One More Time', 'duration': 235},
{'order': 2, 'title': 'Aerodynamic', 'duration': 184}, # {'order': 2, 'title': 'Aerodynamic', 'duration': 184},
{'order': 3, 'title': 'Digital Love', 'duration': 'foobar'} # {'order': 3, 'title': 'Digital Love', 'duration': 'foobar'}
] # ]
} # }
] # ]
expected_errors = [ # expected_errors = [
{}, # {},
{ # {
'tracks': [ # 'tracks': [
{}, # {},
{}, # {},
{'duration': ['Enter a whole number.']} # {'duration': ['Enter a whole number.']}
] # ]
} # }
] # ]
serializer = self.AlbumSerializer(data=data, many=True) # serializer = self.AlbumSerializer(data=data, many=True)
self.assertEqual(serializer.is_valid(), False) # self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, expected_errors) # self.assertEqual(serializer.errors, expected_errors)
class WritableNestedSerializerObjectTests(TestCase): # class WritableNestedSerializerObjectTests(TestCase):
""" # """
Tests for deserializing nested entities. # Tests for deserializing nested entities.
These tests use serializers that restore to concrete objects. # These tests use serializers that restore to concrete objects.
""" # """
def setUp(self): # def setUp(self):
# Couple of concrete objects that we're going to deserialize into # # Couple of concrete objects that we're going to deserialize into
class Track(object): # class Track(object):
def __init__(self, order, title, duration): # def __init__(self, order, title, duration):
self.order, self.title, self.duration = order, title, duration # self.order, self.title, self.duration = order, title, duration
def __eq__(self, other): # def __eq__(self, other):
return ( # return (
self.order == other.order and # self.order == other.order and
self.title == other.title and # self.title == other.title and
self.duration == other.duration # self.duration == other.duration
) # )
class Album(object): # class Album(object):
def __init__(self, album_name, artist, tracks): # def __init__(self, album_name, artist, tracks):
self.album_name, self.artist, self.tracks = album_name, artist, tracks # self.album_name, self.artist, self.tracks = album_name, artist, tracks
def __eq__(self, other): # def __eq__(self, other):
return ( # return (
self.album_name == other.album_name and # self.album_name == other.album_name and
self.artist == other.artist and # self.artist == other.artist and
self.tracks == other.tracks # self.tracks == other.tracks
) # )
# And their corresponding serializers # # And their corresponding serializers
class TrackSerializer(serializers.Serializer): # class TrackSerializer(serializers.Serializer):
order = serializers.IntegerField() # order = serializers.IntegerField()
title = serializers.CharField(max_length=100) # title = serializers.CharField(max_length=100)
duration = serializers.IntegerField() # duration = serializers.IntegerField()
def restore_object(self, attrs, instance=None): # def restore_object(self, attrs, instance=None):
return Track(attrs['order'], attrs['title'], attrs['duration']) # return Track(attrs['order'], attrs['title'], attrs['duration'])
class AlbumSerializer(serializers.Serializer): # class AlbumSerializer(serializers.Serializer):
album_name = serializers.CharField(max_length=100) # album_name = serializers.CharField(max_length=100)
artist = serializers.CharField(max_length=100) # artist = serializers.CharField(max_length=100)
tracks = TrackSerializer(many=True) # tracks = TrackSerializer(many=True)
def restore_object(self, attrs, instance=None): # def restore_object(self, attrs, instance=None):
return Album(attrs['album_name'], attrs['artist'], attrs['tracks']) # return Album(attrs['album_name'], attrs['artist'], attrs['tracks'])
self.Album, self.Track = Album, Track # self.Album, self.Track = Album, Track
self.AlbumSerializer = AlbumSerializer # self.AlbumSerializer = AlbumSerializer
def test_nested_validation_success(self): # def test_nested_validation_success(self):
""" # """
Correct nested serialization should return a restored object # Correct nested serialization should return a restored object
that corresponds to the input data. # that corresponds to the input data.
""" # """
data = { # data = {
'album_name': 'Discovery', # 'album_name': 'Discovery',
'artist': 'Daft Punk', # 'artist': 'Daft Punk',
'tracks': [ # 'tracks': [
{'order': 1, 'title': 'One More Time', 'duration': 235}, # {'order': 1, 'title': 'One More Time', 'duration': 235},
{'order': 2, 'title': 'Aerodynamic', 'duration': 184}, # {'order': 2, 'title': 'Aerodynamic', 'duration': 184},
{'order': 3, 'title': 'Digital Love', 'duration': 239} # {'order': 3, 'title': 'Digital Love', 'duration': 239}
] # ]
} # }
expected_object = self.Album( # expected_object = self.Album(
album_name='Discovery', # album_name='Discovery',
artist='Daft Punk', # artist='Daft Punk',
tracks=[ # tracks=[
self.Track(order=1, title='One More Time', duration=235), # self.Track(order=1, title='One More Time', duration=235),
self.Track(order=2, title='Aerodynamic', duration=184), # self.Track(order=2, title='Aerodynamic', duration=184),
self.Track(order=3, title='Digital Love', duration=239), # self.Track(order=3, title='Digital Love', duration=239),
] # ]
) # )
serializer = self.AlbumSerializer(data=data) # serializer = self.AlbumSerializer(data=data)
self.assertEqual(serializer.is_valid(), True) # self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, expected_object) # self.assertEqual(serializer.object, expected_object)
def test_many_nested_validation_success(self): # def test_many_nested_validation_success(self):
""" # """
Correct nested serialization should return multiple restored objects # Correct nested serialization should return multiple restored objects
that corresponds to the input data when multiple objects are # that corresponds to the input data when multiple objects are
being deserialized. # being deserialized.
""" # """
data = [ # data = [
{ # {
'album_name': 'Russian Red', # 'album_name': 'Russian Red',
'artist': 'I Love Your Glasses', # 'artist': 'I Love Your Glasses',
'tracks': [ # 'tracks': [
{'order': 1, 'title': 'Cigarettes', 'duration': 121}, # {'order': 1, 'title': 'Cigarettes', 'duration': 121},
{'order': 2, 'title': 'No Past Land', 'duration': 198}, # {'order': 2, 'title': 'No Past Land', 'duration': 198},
{'order': 3, 'title': 'They Don\'t Believe', 'duration': 191} # {'order': 3, 'title': 'They Don\'t Believe', 'duration': 191}
] # ]
}, # },
{ # {
'album_name': 'Discovery', # 'album_name': 'Discovery',
'artist': 'Daft Punk', # 'artist': 'Daft Punk',
'tracks': [ # 'tracks': [
{'order': 1, 'title': 'One More Time', 'duration': 235}, # {'order': 1, 'title': 'One More Time', 'duration': 235},
{'order': 2, 'title': 'Aerodynamic', 'duration': 184}, # {'order': 2, 'title': 'Aerodynamic', 'duration': 184},
{'order': 3, 'title': 'Digital Love', 'duration': 239} # {'order': 3, 'title': 'Digital Love', 'duration': 239}
] # ]
} # }
] # ]
expected_object = [ # expected_object = [
self.Album( # self.Album(
album_name='Russian Red', # album_name='Russian Red',
artist='I Love Your Glasses', # artist='I Love Your Glasses',
tracks=[ # tracks=[
self.Track(order=1, title='Cigarettes', duration=121), # self.Track(order=1, title='Cigarettes', duration=121),
self.Track(order=2, title='No Past Land', duration=198), # self.Track(order=2, title='No Past Land', duration=198),
self.Track(order=3, title='They Don\'t Believe', duration=191), # self.Track(order=3, title='They Don\'t Believe', duration=191),
] # ]
), # ),
self.Album( # self.Album(
album_name='Discovery', # album_name='Discovery',
artist='Daft Punk', # artist='Daft Punk',
tracks=[ # tracks=[
self.Track(order=1, title='One More Time', duration=235), # self.Track(order=1, title='One More Time', duration=235),
self.Track(order=2, title='Aerodynamic', duration=184), # self.Track(order=2, title='Aerodynamic', duration=184),
self.Track(order=3, title='Digital Love', duration=239), # self.Track(order=3, title='Digital Love', duration=239),
] # ]
) # )
] # ]
serializer = self.AlbumSerializer(data=data, many=True) # serializer = self.AlbumSerializer(data=data, many=True)
self.assertEqual(serializer.is_valid(), True) # self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, expected_object) # self.assertEqual(serializer.object, expected_object)
class ForeignKeyNestedSerializerUpdateTests(TestCase): # class ForeignKeyNestedSerializerUpdateTests(TestCase):
def setUp(self): # def setUp(self):
class Artist(object): # class Artist(object):
def __init__(self, name): # def __init__(self, name):
self.name = name # self.name = name
def __eq__(self, other): # def __eq__(self, other):
return self.name == other.name # return self.name == other.name
class Album(object): # class Album(object):
def __init__(self, name, artist): # def __init__(self, name, artist):
self.name, self.artist = name, artist # self.name, self.artist = name, artist
def __eq__(self, other): # def __eq__(self, other):
return self.name == other.name and self.artist == other.artist # return self.name == other.name and self.artist == other.artist
class ArtistSerializer(serializers.Serializer): # class ArtistSerializer(serializers.Serializer):
name = serializers.CharField() # name = serializers.CharField()
def restore_object(self, attrs, instance=None): # def restore_object(self, attrs, instance=None):
if instance: # if instance:
instance.name = attrs['name'] # instance.name = attrs['name']
else: # else:
instance = Artist(attrs['name']) # instance = Artist(attrs['name'])
return instance # return instance
class AlbumSerializer(serializers.Serializer): # class AlbumSerializer(serializers.Serializer):
name = serializers.CharField() # name = serializers.CharField()
by = ArtistSerializer(source='artist') # by = ArtistSerializer(source='artist')
def restore_object(self, attrs, instance=None): # def restore_object(self, attrs, instance=None):
if instance: # if instance:
instance.name = attrs['name'] # instance.name = attrs['name']
instance.artist = attrs['artist'] # instance.artist = attrs['artist']
else: # else:
instance = Album(attrs['name'], attrs['artist']) # instance = Album(attrs['name'], attrs['artist'])
return instance # return instance
self.Artist = Artist # self.Artist = Artist
self.Album = Album # self.Album = Album
self.AlbumSerializer = AlbumSerializer # self.AlbumSerializer = AlbumSerializer
def test_create_via_foreign_key_with_source(self): # def test_create_via_foreign_key_with_source(self):
""" # """
Check that we can both *create* and *update* into objects across # Check that we can both *create* and *update* into objects across
ForeignKeys that have a `source` specified. # ForeignKeys that have a `source` specified.
Regression test for #1170 # Regression test for #1170
""" # """
data = { # data = {
'name': 'Discovery', # 'name': 'Discovery',
'by': {'name': 'Daft Punk'}, # 'by': {'name': 'Daft Punk'},
} # }
expected = self.Album(artist=self.Artist('Daft Punk'), name='Discovery') # expected = self.Album(artist=self.Artist('Daft Punk'), name='Discovery')
# create # # create
serializer = self.AlbumSerializer(data=data) # serializer = self.AlbumSerializer(data=data)
self.assertEqual(serializer.is_valid(), True) # self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, expected) # self.assertEqual(serializer.object, expected)
# update # # update
original = self.Album(artist=self.Artist('The Bats'), name='Free All the Monsters') # original = self.Album(artist=self.Artist('The Bats'), name='Free All the Monsters')
serializer = self.AlbumSerializer(instance=original, data=data) # serializer = self.AlbumSerializer(instance=original, data=data)
self.assertEqual(serializer.is_valid(), True) # self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, expected) # self.assertEqual(serializer.object, expected)
class NestedModelSerializerUpdateTests(TestCase): # class NestedModelSerializerUpdateTests(TestCase):
def test_second_nested_level(self): # def test_second_nested_level(self):
john = models.Person.objects.create(name="john") # john = models.Person.objects.create(name="john")
post = john.blogpost_set.create(title="Test blog post") # 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 hate this blog post")
post.blogpostcomment_set.create(text="I love this blog post") # post.blogpostcomment_set.create(text="I love this blog post")
class BlogPostCommentSerializer(serializers.ModelSerializer): # class BlogPostCommentSerializer(serializers.ModelSerializer):
class Meta: # class Meta:
model = models.BlogPostComment # model = models.BlogPostComment
class BlogPostSerializer(serializers.ModelSerializer): # class BlogPostSerializer(serializers.ModelSerializer):
comments = BlogPostCommentSerializer(many=True, source='blogpostcomment_set') # comments = BlogPostCommentSerializer(many=True, source='blogpostcomment_set')
class Meta: # class Meta:
model = models.BlogPost # model = models.BlogPost
fields = ('id', 'title', 'comments') # fields = ('id', 'title', 'comments')
class PersonSerializer(serializers.ModelSerializer): # class PersonSerializer(serializers.ModelSerializer):
posts = BlogPostSerializer(many=True, source='blogpost_set') # posts = BlogPostSerializer(many=True, source='blogpost_set')
class Meta: # class Meta:
model = models.Person # model = models.Person
fields = ('id', 'name', 'age', 'posts') # fields = ('id', 'name', 'age', 'posts')
serialize = PersonSerializer(instance=john) # serialize = PersonSerializer(instance=john)
deserialize = PersonSerializer(data=serialize.data, instance=john) # deserialize = PersonSerializer(data=serialize.data, instance=john)
self.assertTrue(deserialize.is_valid()) # self.assertTrue(deserialize.is_valid())
result = deserialize.object # result = deserialize.object
result.save() # result.save()
self.assertEqual(result.id, john.id) # self.assertEqual(result.id, john.id)

View File

@ -1,31 +1,31 @@
from django.test import TestCase # from django.test import TestCase
from django.utils import six # from django.utils import six
from rest_framework.serializers import _resolve_model # from rest_framework.serializers import _resolve_model
from tests.models import BasicModel # from tests.models import BasicModel
class ResolveModelTests(TestCase): # class ResolveModelTests(TestCase):
""" # """
`_resolve_model` should return a Django model class given the # `_resolve_model` should return a Django model class given the
provided argument is a Django model class itself, or a properly # provided argument is a Django model class itself, or a properly
formatted string representation of one. # formatted string representation of one.
""" # """
def test_resolve_django_model(self): # def test_resolve_django_model(self):
resolved_model = _resolve_model(BasicModel) # resolved_model = _resolve_model(BasicModel)
self.assertEqual(resolved_model, BasicModel) # self.assertEqual(resolved_model, BasicModel)
def test_resolve_string_representation(self): # def test_resolve_string_representation(self):
resolved_model = _resolve_model('tests.BasicModel') # resolved_model = _resolve_model('tests.BasicModel')
self.assertEqual(resolved_model, BasicModel) # self.assertEqual(resolved_model, BasicModel)
def test_resolve_unicode_representation(self): # def test_resolve_unicode_representation(self):
resolved_model = _resolve_model(six.text_type('tests.BasicModel')) # resolved_model = _resolve_model(six.text_type('tests.BasicModel'))
self.assertEqual(resolved_model, BasicModel) # self.assertEqual(resolved_model, BasicModel)
def test_resolve_non_django_model(self): # def test_resolve_non_django_model(self):
with self.assertRaises(ValueError): # with self.assertRaises(ValueError):
_resolve_model(TestCase) # _resolve_model(TestCase)
def test_resolve_improper_string_representation(self): # def test_resolve_improper_string_representation(self):
with self.assertRaises(ValueError): # with self.assertRaises(ValueError):
_resolve_model('BasicModel') # _resolve_model('BasicModel')