diff --git a/README.md b/README.md index 3d8dce948..049f9075e 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,17 @@ To run the tests. # Changelog -## Master +## 2.1.0 -* Minor field improvements (don't stringify dicts, more robust many-pk fields) +**Date**: 5th Nov 2012 + +**Warning**: Please read [this thread][2.1.0-notes] regarding the `instance` and `data` keyword args before updating to 2.1.0. + +* **Serializer `instance` and `data` keyword args have their position swapped.** +* `queryset` argument is now optional on writable model fields. +* Support Django's cache framework. +* Minor field improvements. (Don't stringify dicts, more robust many-pk fields.) +* Bugfixes (Support choice field in Browseable API) ## 2.0.2 @@ -113,6 +121,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X [sandbox]: http://restframework.herokuapp.com/ [rest-framework-2-announcement]: topics/rest-framework-2-announcement.md +[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion [docs]: http://django-rest-framework.org/ [urlobject]: https://github.com/zacharyvoase/urlobject diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index c88b9b0c6..ee7f72dd7 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -47,7 +47,7 @@ The first part of serializer class defines the fields that get serialized/deseri We can now use `CommentSerializer` to serialize a comment, or list of comments. Again, using the `Serializer` class looks a lot like using a `Form` class. - serializer = CommentSerializer(instance=comment) + serializer = CommentSerializer(comment) serializer.data # {'email': u'leila@example.com', 'content': u'foo bar', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774)} @@ -65,20 +65,29 @@ Deserialization is similar. First we parse a stream into python native datatype ...then we restore those native datatypes into a fully populated object instance. - serializer = CommentSerializer(data) + serializer = CommentSerializer(data=data) serializer.is_valid() # True serializer.object # >>> serializer.deserialize('json', stream) +When deserializing data, we can either create a new instance, or update an existing instance. + + serializer = CommentSerializer(data=data) # Create new instance + serializer = CommentSerializer(comment, data=data) # Update `instance` + ## Validation When deserializing data, you always need to call `is_valid()` before attempting to access the deserialized object. If any validation errors occur, the `.errors` and `.non_field_errors` properties will contain the resulting error messages. ### Field-level validation -You can specify custom field-level validation by adding `validate_()` methods to your `Serializer` subclass. These are analagous to `clean_` methods on Django forms, but accept slightly different arguments. They take a dictionary of deserialized attributes as a first argument, and the field name in that dictionary as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). Your `validate_` methods should either just return the attrs dictionary or raise a `ValidationError`. For example: +You can specify custom field-level validation by adding `.validate_` methods to your `Serializer` subclass. These are analagous to `.clean_` methods on Django forms, but accept slightly different arguments. + +They take a dictionary of deserialized attributes as a first argument, and the field name in that dictionary as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). + +Your `validate_` methods should either just return the `attrs` dictionary or raise a `ValidationError`. For example: from rest_framework import serializers @@ -88,16 +97,22 @@ You can specify custom field-level validation by adding `validate_()` def validate_title(self, attrs, source): """ - Check that the blog post is about Django + Check that the blog post is about Django. """ value = attrs[source] - if "Django" not in value: + if "django" not in value.lower(): raise serializers.ValidationError("Blog post is not about Django") return attrs -### Final cross-field validation +### Object-level validation -To do any other validation that requires access to multiple fields, add a method called `validate` to your `Serializer` subclass. This method takes a single argument, which is the `attrs` dictionary. It should raise a `ValidationError` if necessary, or just return `attrs`. +To do any other validation that requires access to multiple fields, add a method called `.validate()` to your `Serializer` subclass. This method takes a single argument, which is the `attrs` dictionary. It should raise a `ValidationError` if necessary, or just return `attrs`. + +## Saving object state + +Serializers also include a `.save()` method that you can override if you want to provide a method of persisting the state of a deserialized object. The default behavior of the method is to simply call `.save()` on the deserialized object instance. + +The generic views provided by REST framework call the `.save()` method when updating or creating entities. ## Dealing with nested objects diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 14d910bd9..6e7008f1f 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -4,11 +4,18 @@ > > — Eric S. Raymond, [The Cathedral and the Bazaar][cite]. -## Master +## 2.1.0 +**Date**: 5th Nov 2012 + +**Warning**: Please read [this thread][2.1.0-notes] regarding the `instance` and `data` keyword args before updating to 2.1.0. + +* **Serializer `instance` and `data` keyword args have their position swapped.** +* `queryset` argument is now optional on writable model fields. * Support Django's cache framework. * Minor field improvements. (Don't stringify dicts, more robust many-pk fields.) -* Bugfixes (Support choice field in Browseable API) +* Bugfix: Support choice field in Browseable API. +* Bugfix: Related fields with `read_only=True` do not require a `queryset` argument. ## 2.0.2 @@ -29,7 +36,7 @@ **Date**: 30th Oct 2012 * **Fix all of the things.** (Well, almost.) -* For more information please see the [2.0 migration guide][migration]. +* For more information please see the [2.0 announcement][announcement]. --- @@ -135,4 +142,5 @@ * Initial release. [cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html -[migration]: migration.md \ No newline at end of file +[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion +[announcement]: rest-framework-2-announcement.md \ No newline at end of file diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 316a3c255..ba64f2aa7 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -162,7 +162,7 @@ Okay, once we've got a few imports out of the way, let's create a code snippet t We've now got a few snippet instances to play with. Let's take a look at serializing one of those instances. - serializer = SnippetSerializer(instance=snippet) + serializer = SnippetSerializer(snippet) serializer.data # {'pk': 1, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'} @@ -181,7 +181,7 @@ Deserialization is similar. First we parse a stream into python native datatype ...then we restore those native datatypes into to a fully populated object instance. - serializer = SnippetSerializer(data) + serializer = SnippetSerializer(data=data) serializer.is_valid() # True serializer.object @@ -240,12 +240,12 @@ The root of our API is going to be a view that supports listing all the existing """ if request.method == 'GET': snippets = Snippet.objects.all() - serializer = SnippetSerializer(instance=snippets) + serializer = SnippetSerializer(snippets) return JSONResponse(serializer.data) elif request.method == 'POST': data = JSONParser().parse(request) - serializer = SnippetSerializer(data) + serializer = SnippetSerializer(data=data) if serializer.is_valid(): serializer.save() return JSONResponse(serializer.data, status=201) @@ -267,12 +267,12 @@ We'll also need a view which corresponds to an individual snippet, and can be us return HttpResponse(status=404) if request.method == 'GET': - serializer = SnippetSerializer(instance=snippet) + serializer = SnippetSerializer(snippet) return JSONResponse(serializer.data) elif request.method == 'PUT': data = JSONParser().parse(request) - serializer = SnippetSerializer(data, instance=snippet) + serializer = SnippetSerializer(snippet, data=data) if serializer.is_valid(): serializer.save() return JSONResponse(serializer.data) diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index a7c23cba6..b29daf05a 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -52,11 +52,11 @@ We don't need our `JSONResponse` class anymore, so go ahead and delete that. On """ if request.method == 'GET': snippets = Snippet.objects.all() - serializer = SnippetSerializer(instance=snippets) + serializer = SnippetSerializer(snippets) return Response(serializer.data) elif request.method == 'POST': - serializer = SnippetSerializer(request.DATA) + serializer = SnippetSerializer(data=request.DATA) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -77,11 +77,11 @@ Our instance view is an improvement over the previous example. It's a little mo return Response(status=status.HTTP_404_NOT_FOUND) if request.method == 'GET': - serializer = SnippetSerializer(instance=snippet) + serializer = SnippetSerializer(snippet) return Response(serializer.data) elif request.method == 'PUT': - serializer = SnippetSerializer(request.DATA, instance=snippet) + serializer = SnippetSerializer(snippet, data=request.DATA) if serializer.is_valid(): serializer.save() return Response(serializer.data) diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 91ef40387..eddf63110 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -20,11 +20,11 @@ We'll start by rewriting the root view as a class based view. All this involves """ def get(self, request, format=None): snippets = Snippet.objects.all() - serializer = SnippetSerializer(instance=snippets) + serializer = SnippetSerializer(snippets) return Response(serializer.data) def post(self, request, format=None): - serializer = SnippetSerializer(request.DATA) + serializer = SnippetSerializer(data=request.DATA) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -44,12 +44,12 @@ So far, so good. It looks pretty similar to the previous case, but we've got be def get(self, request, pk, format=None): snippet = self.get_object(pk) - serializer = SnippetSerializer(instance=snippet) + serializer = SnippetSerializer(snippet) return Response(serializer.data) def put(self, request, pk, format=None): snippet = self.get_object(pk) - serializer = SnippetSerializer(request.DATA, instance=snippet) + serializer = SnippetSerializer(snippet, data=request.DATA) if serializer.is_valid(): serializer.save() return Response(serializer.data) diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 3113249b5..98c45b821 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -167,7 +167,7 @@ We've reached the end of our tutorial. If you want to get more involved in the * Join the [REST framework discussion group][group], and help build the community. * Follow the author [on Twitter][twitter] and say hi. -**Now go build some awesome things.** +**Now go build awesome things.** [repo]: https://github.com/tomchristie/rest-framework-tutorial [sandbox]: http://restframework.herokuapp.com/ diff --git a/rest_framework/fields.py b/rest_framework/fields.py index eb4c5e1f2..dc726a326 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -40,7 +40,7 @@ class Field(object): self.source = source - def initialize(self, parent): + def initialize(self, parent, field_name): """ Called to set up a field prior to field_to_native or field_from_native. @@ -248,7 +248,22 @@ class RelatedField(WritableField): def __init__(self, *args, **kwargs): self.queryset = kwargs.pop('queryset', None) super(RelatedField, self).__init__(*args, **kwargs) - self.read_only = self.default_read_only + self.read_only = kwargs.pop('read_only', self.default_read_only) + + def initialize(self, parent, field_name): + super(RelatedField, self).initialize(parent, field_name) + if self.queryset is None and not self.read_only: + try: + manager = getattr(self.parent.opts.model, self.source or field_name) + if hasattr(manager, 'related'): # Forward + self.queryset = manager.related.model._default_manager.all() + else: # Reverse + self.queryset = manager.field.rel.to._default_manager.all() + except: + raise + msg = ('Serializer related fields must include a `queryset`' + + ' argument or set `read_only=True') + raise Exception(msg) ### We need this stuff to make form choices work... diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 27540a57f..7675d7f99 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -48,7 +48,7 @@ class GenericAPIView(views.APIView): # TODO: add support for seperate serializer/deserializer serializer_class = self.get_serializer_class() context = self.get_serializer_context() - return serializer_class(data, instance=instance, context=context) + return serializer_class(instance, data=data, context=context) class MultipleObjectAPIView(MultipleObjectMixin, GenericAPIView): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 3d134a741..28767b16b 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -6,6 +6,15 @@ from django.db import models from django.forms import widgets from django.utils.datastructures import SortedDict from rest_framework.compat import get_concrete_model + +# Note: We do the following so that users of the framework can use this style: +# +# example_field = serializers.CharField(...) +# +# This helps keep the seperation between model fields, form fields, and +# serializer fields more explicit. + + from rest_framework.fields import * @@ -82,10 +91,10 @@ class BaseSerializer(Field): _options_class = SerializerOptions _dict_class = SortedDictWithMetadata # Set to unsorted dict for backwards compatability with unsorted implementations. - def __init__(self, data=None, instance=None, context=None, **kwargs): + def __init__(self, instance=None, data=None, context=None, **kwargs): super(BaseSerializer, self).__init__(**kwargs) - self.fields = copy.deepcopy(self.base_fields) self.opts = self._options_class(self.Meta) + self.fields = copy.deepcopy(self.base_fields) self.parent = None self.root = None @@ -100,13 +109,13 @@ class BaseSerializer(Field): ##### # Methods to determine which fields to use when (de)serializing objects. - def default_fields(self, serialize, obj=None, data=None, nested=False): + def default_fields(self, nested=False): """ Return the complete set of default fields for the object, as a dict. """ return {} - def get_fields(self, serialize, obj=None, data=None, nested=False): + def get_fields(self, nested=False): """ Returns the complete set of fields for the object as a dict. @@ -119,10 +128,10 @@ class BaseSerializer(Field): for key, field in self.fields.items(): ret[key] = field # Set up the field - field.initialize(parent=self) + field.initialize(parent=self, field_name=key) # Add in the default fields - fields = self.default_fields(serialize, obj, data, nested) + fields = self.default_fields(nested) for key, val in fields.items(): if key not in ret: ret[key] = val @@ -144,12 +153,12 @@ class BaseSerializer(Field): ##### # Field methods - used when the serializer class is itself used as a field. - def initialize(self, parent): + def initialize(self, parent, field_name): """ Same behaviour as usual Field, except that we need to keep track of state so that we can deal with handling maximum depth. """ - super(BaseSerializer, self).initialize(parent) + super(BaseSerializer, self).initialize(parent, field_name) if parent.opts.depth: self.opts.depth = parent.opts.depth - 1 @@ -170,7 +179,7 @@ class BaseSerializer(Field): ret = self._dict_class() ret.fields = {} - fields = self.get_fields(serialize=True, obj=obj, nested=bool(self.opts.depth)) + fields = self.get_fields(nested=bool(self.opts.depth)) for field_name, field in fields.items(): key = self.get_field_key(field_name) value = field.field_to_native(obj, field_name) @@ -183,7 +192,7 @@ class BaseSerializer(Field): Core of deserialization, together with `restore_object`. Converts a dictionary of data into a dictionary of deserialized fields. """ - fields = self.get_fields(serialize=False, data=data, nested=bool(self.opts.depth)) + fields = self.get_fields(nested=bool(self.opts.depth)) reverted_data = {} for field_name, field in fields.items(): try: @@ -198,7 +207,7 @@ class BaseSerializer(Field): Run `validate_()` and `validate()` methods on the serializer """ # TODO: refactor this so we're not determining the fields again - fields = self.get_fields(serialize=False, data=attrs, nested=bool(self.opts.depth)) + fields = self.get_fields(nested=bool(self.opts.depth)) for field_name, field in fields.items(): try: @@ -237,11 +246,8 @@ class BaseSerializer(Field): """ Serialize objects -> primatives. """ - if isinstance(obj, dict): - return dict([(key, self.to_native(val)) - for (key, val) in obj.items()]) - elif hasattr(obj, '__iter__'): - return [self.to_native(item) for item in obj] + if hasattr(obj, '__iter__'): + return [self.convert_object(item) for item in obj] return self.convert_object(obj) def from_native(self, data): @@ -323,7 +329,7 @@ class ModelSerializer(Serializer): """ _options_class = ModelSerializerOptions - def default_fields(self, serialize, obj=None, data=None, nested=False): + def default_fields(self, nested=False): """ Return all the fields that should be serialized for the model. """ @@ -360,7 +366,7 @@ class ModelSerializer(Serializer): field = self.get_field(model_field) if field: - field.initialize(parent=self) + field.initialize(parent=self, field_name=model_field.name) ret[model_field.name] = field return ret diff --git a/rest_framework/tests/genericrelations.py b/rest_framework/tests/genericrelations.py index 1d7e33bcb..bc7378e12 100644 --- a/rest_framework/tests/genericrelations.py +++ b/rest_framework/tests/genericrelations.py @@ -25,7 +25,7 @@ class TestGenericRelations(TestCase): model = Bookmark exclude = ('id',) - serializer = BookmarkSerializer(instance=self.bookmark) + serializer = BookmarkSerializer(self.bookmark) expected = { 'tags': [u'django', u'python'], 'url': u'https://www.djangoproject.com/' diff --git a/rest_framework/tests/hyperlinkedserializers.py b/rest_framework/tests/hyperlinkedserializers.py index 92c3691e1..147943c6f 100644 --- a/rest_framework/tests/hyperlinkedserializers.py +++ b/rest_framework/tests/hyperlinkedserializers.py @@ -7,12 +7,13 @@ from rest_framework.tests.models import Anchor, BasicModel, ManyToManyModel, Blo factory = RequestFactory() -class BlogPostCommentSerializer(serializers.Serializer): +class BlogPostCommentSerializer(serializers.ModelSerializer): text = serializers.CharField() - blog_post_url = serializers.HyperlinkedRelatedField(source='blog_post', view_name='blogpost-detail', queryset=BlogPost.objects.all()) + blog_post_url = serializers.HyperlinkedRelatedField(source='blog_post', view_name='blogpost-detail') - def restore_object(self, attrs, instance=None): - return BlogPostComment(**attrs) + class Meta: + model = BlogPostComment + fields = ('text', 'blog_post_url') class BasicList(generics.ListCreateAPIView): @@ -42,7 +43,7 @@ class ManyToManyDetail(generics.RetrieveAPIView): class BlogPostCommentListCreate(generics.ListCreateAPIView): model = BlogPostComment - model_serializer_class = BlogPostCommentSerializer + serializer_class = BlogPostCommentSerializer class BlogPostDetail(generics.RetrieveAPIView): diff --git a/rest_framework/tests/pagination.py b/rest_framework/tests/pagination.py index a939c9ef5..64e8d822f 100644 --- a/rest_framework/tests/pagination.py +++ b/rest_framework/tests/pagination.py @@ -74,13 +74,13 @@ class UnitTestPagination(TestCase): self.last_page = paginator.page(3) def test_native_pagination(self): - serializer = pagination.PaginationSerializer(instance=self.first_page) + serializer = pagination.PaginationSerializer(self.first_page) self.assertEquals(serializer.data['count'], 26) self.assertEquals(serializer.data['next'], '?page=2') self.assertEquals(serializer.data['previous'], None) self.assertEquals(serializer.data['results'], self.objects[:10]) - serializer = pagination.PaginationSerializer(instance=self.last_page) + serializer = pagination.PaginationSerializer(self.last_page) self.assertEquals(serializer.data['count'], 26) self.assertEquals(serializer.data['next'], None) self.assertEquals(serializer.data['previous'], '?page=2') diff --git a/rest_framework/tests/pk_relations.py b/rest_framework/tests/pk_relations.py index 9095dcd81..947098107 100644 --- a/rest_framework/tests/pk_relations.py +++ b/rest_framework/tests/pk_relations.py @@ -15,7 +15,7 @@ class ManyToManySource(models.Model): class ManyToManyTargetSerializer(serializers.ModelSerializer): - sources = serializers.ManyPrimaryKeyRelatedField(queryset=ManyToManySource.objects.all()) + sources = serializers.ManyPrimaryKeyRelatedField() class Meta: model = ManyToManyTarget @@ -63,7 +63,7 @@ class PrimaryKeyManyToManyTests(TestCase): def test_many_to_many_retrieve(self): queryset = ManyToManySource.objects.all() - serializer = ManyToManySourceSerializer(instance=queryset) + serializer = ManyToManySourceSerializer(queryset) expected = [ {'id': 1, 'name': u'source-1', 'targets': [1]}, {'id': 2, 'name': u'source-2', 'targets': [1, 2]}, @@ -73,7 +73,7 @@ class PrimaryKeyManyToManyTests(TestCase): def test_reverse_many_to_many_retrieve(self): queryset = ManyToManyTarget.objects.all() - serializer = ManyToManyTargetSerializer(instance=queryset) + serializer = ManyToManyTargetSerializer(queryset) expected = [ {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]}, {'id': 2, 'name': u'target-2', 'sources': [2, 3]}, @@ -84,14 +84,14 @@ class PrimaryKeyManyToManyTests(TestCase): def test_many_to_many_update(self): data = {'id': 1, 'name': u'source-1', 'targets': [1, 2, 3]} instance = ManyToManySource.objects.get(pk=1) - serializer = ManyToManySourceSerializer(data, instance=instance) + serializer = ManyToManySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) self.assertEquals(serializer.data, data) serializer.save() # Ensure source 1 is updated, and everything else is as expected queryset = ManyToManySource.objects.all() - serializer = ManyToManySourceSerializer(instance=queryset) + serializer = ManyToManySourceSerializer(queryset) expected = [ {'id': 1, 'name': u'source-1', 'targets': [1, 2, 3]}, {'id': 2, 'name': u'source-2', 'targets': [1, 2]}, @@ -102,14 +102,14 @@ class PrimaryKeyManyToManyTests(TestCase): def test_reverse_many_to_many_update(self): data = {'id': 1, 'name': u'target-1', 'sources': [1]} instance = ManyToManyTarget.objects.get(pk=1) - serializer = ManyToManyTargetSerializer(data, instance=instance) + serializer = ManyToManyTargetSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) self.assertEquals(serializer.data, data) serializer.save() # Ensure target 1 is updated, and everything else is as expected queryset = ManyToManyTarget.objects.all() - serializer = ManyToManyTargetSerializer(instance=queryset) + serializer = ManyToManyTargetSerializer(queryset) expected = [ {'id': 1, 'name': u'target-1', 'sources': [1]}, {'id': 2, 'name': u'target-2', 'sources': [2, 3]}, @@ -130,7 +130,7 @@ class PrimaryKeyForeignKeyTests(TestCase): def test_foreign_key_retrieve(self): queryset = ForeignKeySource.objects.all() - serializer = ForeignKeySourceSerializer(instance=queryset) + serializer = ForeignKeySourceSerializer(queryset) expected = [ {'id': 1, 'name': u'source-1', 'target': 1}, {'id': 2, 'name': u'source-2', 'target': 1}, @@ -140,7 +140,7 @@ class PrimaryKeyForeignKeyTests(TestCase): def test_reverse_foreign_key_retrieve(self): queryset = ForeignKeyTarget.objects.all() - serializer = ForeignKeyTargetSerializer(instance=queryset) + serializer = ForeignKeyTargetSerializer(queryset) expected = [ {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]}, {'id': 2, 'name': u'target-2', 'sources': []}, @@ -150,14 +150,14 @@ class PrimaryKeyForeignKeyTests(TestCase): def test_foreign_key_update(self): data = {'id': 1, 'name': u'source-1', 'target': 2} instance = ForeignKeySource.objects.get(pk=1) - serializer = ForeignKeySourceSerializer(data, instance=instance) + serializer = ForeignKeySourceSerializer(instance, data=data) self.assertTrue(serializer.is_valid()) self.assertEquals(serializer.data, data) serializer.save() # # Ensure source 1 is updated, and everything else is as expected queryset = ForeignKeySource.objects.all() - serializer = ForeignKeySourceSerializer(instance=queryset) + serializer = ForeignKeySourceSerializer(queryset) expected = [ {'id': 1, 'name': u'source-1', 'target': 2}, {'id': 2, 'name': u'source-2', 'target': 1}, @@ -172,14 +172,14 @@ class PrimaryKeyForeignKeyTests(TestCase): # def test_reverse_foreign_key_update(self): # data = {'id': 1, 'name': u'target-1', 'sources': [1]} # instance = ForeignKeyTarget.objects.get(pk=1) - # serializer = ForeignKeyTargetSerializer(data, instance=instance) + # serializer = ForeignKeyTargetSerializer(instance, data=data) # self.assertTrue(serializer.is_valid()) # self.assertEquals(serializer.data, data) # serializer.save() # # Ensure target 1 is updated, and everything else is as expected # queryset = ForeignKeyTarget.objects.all() - # serializer = ForeignKeyTargetSerializer(instance=queryset) + # serializer = ForeignKeyTargetSerializer(queryset) # expected = [ # {'id': 1, 'name': u'target-1', 'sources': [1]}, # {'id': 2, 'name': u'target-2', 'sources': []}, diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index 3fd490644..8d1de4298 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -87,11 +87,11 @@ class BasicTests(TestCase): self.assertEquals(serializer.data, expected) def test_retrieve(self): - serializer = CommentSerializer(instance=self.comment) + serializer = CommentSerializer(self.comment) self.assertEquals(serializer.data, self.expected) def test_create(self): - serializer = CommentSerializer(self.data) + serializer = CommentSerializer(data=self.data) expected = self.comment self.assertEquals(serializer.is_valid(), True) self.assertEquals(serializer.object, expected) @@ -99,25 +99,25 @@ class BasicTests(TestCase): self.assertEquals(serializer.data['sub_comment'], 'And Merry Christmas!') def test_update(self): - serializer = CommentSerializer(self.data, instance=self.comment) + serializer = CommentSerializer(self.comment, data=self.data) expected = self.comment self.assertEquals(serializer.is_valid(), True) self.assertEquals(serializer.object, expected) self.assertTrue(serializer.object is expected) self.assertEquals(serializer.data['sub_comment'], 'And Merry Christmas!') - + def test_model_fields_as_expected(self): """ Make sure that the fields returned are the same as defined in the Meta data """ - serializer = PersonSerializer(instance=self.person) + serializer = PersonSerializer(self.person) self.assertEquals(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(instance=self.person) + serializer = PersonSerializer(self.person) expected = self.person_data self.assertEquals(serializer.data['info'], expected) @@ -138,12 +138,12 @@ class ValidationTests(TestCase): ) def test_create(self): - serializer = CommentSerializer(self.data) + serializer = CommentSerializer(data=self.data) self.assertEquals(serializer.is_valid(), False) self.assertEquals(serializer.errors, {'content': [u'Ensure this value has at most 1000 characters (it has 1001).']}) def test_update(self): - serializer = CommentSerializer(self.data, instance=self.comment) + serializer = CommentSerializer(self.comment, data=self.data) self.assertEquals(serializer.is_valid(), False) self.assertEquals(serializer.errors, {'content': [u'Ensure this value has at most 1000 characters (it has 1001).']}) @@ -152,7 +152,7 @@ class ValidationTests(TestCase): 'content': 'xxx', 'created': datetime.datetime(2012, 1, 1) } - serializer = CommentSerializer(data, instance=self.comment) + serializer = CommentSerializer(self.comment, data=data) self.assertEquals(serializer.is_valid(), False) self.assertEquals(serializer.errors, {'email': [u'This field is required.']}) @@ -163,7 +163,7 @@ class ValidationTests(TestCase): 'title': 'Some action item', #No 'done' value. } - serializer = ActionItemSerializer(data, instance=self.actionitem) + serializer = ActionItemSerializer(self.actionitem, data=data) self.assertEquals(serializer.is_valid(), True) self.assertEquals(serializer.errors, {}) @@ -183,12 +183,12 @@ class ValidationTests(TestCase): 'created': datetime.datetime(2012, 1, 1) } - serializer = CommentSerializerWithFieldValidator(data) + serializer = CommentSerializerWithFieldValidator(data=data) self.assertTrue(serializer.is_valid()) data['content'] = 'This should not validate' - serializer = CommentSerializerWithFieldValidator(data) + serializer = CommentSerializerWithFieldValidator(data=data) self.assertFalse(serializer.is_valid()) self.assertEquals(serializer.errors, {'content': [u'Test not in value']}) @@ -207,12 +207,12 @@ class ValidationTests(TestCase): 'created': datetime.datetime(2012, 1, 1) } - serializer = CommentSerializerWithCrossFieldValidator(data) + serializer = CommentSerializerWithCrossFieldValidator(data=data) self.assertTrue(serializer.is_valid()) data['content'] = 'A comment from foo@bar.com' - serializer = CommentSerializerWithCrossFieldValidator(data) + serializer = CommentSerializerWithCrossFieldValidator(data=data) self.assertFalse(serializer.is_valid()) self.assertEquals(serializer.errors, {'non_field_errors': [u'Email address not in content']}) @@ -220,7 +220,7 @@ class ValidationTests(TestCase): """ Omitting a value for null-field should validate. """ - serializer = PersonSerializer({'name': 'marko'}) + serializer = PersonSerializer(data={'name': 'marko'}) self.assertEquals(serializer.is_valid(), True) self.assertEquals(serializer.errors, {}) @@ -270,7 +270,7 @@ class ManyToManyTests(TestCase): Create an instance of a model with a ManyToMany relationship. """ data = {'rel': [self.anchor.id]} - serializer = self.serializer_class(data) + serializer = self.serializer_class(data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(ManyToManyModel.objects.all()), 2) @@ -284,7 +284,7 @@ class ManyToManyTests(TestCase): new_anchor = Anchor() new_anchor.save() data = {'rel': [self.anchor.id, new_anchor.id]} - serializer = self.serializer_class(data, instance=self.instance) + serializer = self.serializer_class(self.instance, data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(ManyToManyModel.objects.all()), 1) @@ -297,7 +297,7 @@ class ManyToManyTests(TestCase): containing no items. """ data = {'rel': []} - serializer = self.serializer_class(data) + serializer = self.serializer_class(data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(ManyToManyModel.objects.all()), 2) @@ -312,7 +312,7 @@ class ManyToManyTests(TestCase): new_anchor = Anchor() new_anchor.save() data = {'rel': []} - serializer = self.serializer_class(data, instance=self.instance) + serializer = self.serializer_class(self.instance, data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(ManyToManyModel.objects.all()), 1) @@ -326,7 +326,7 @@ class ManyToManyTests(TestCase): lists (eg form data). """ data = {'rel': ''} - serializer = self.serializer_class(data) + serializer = self.serializer_class(data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(ManyToManyModel.objects.all()), 2) @@ -364,7 +364,7 @@ class ReadOnlyManyToManyTests(TestCase): new_anchor = Anchor() new_anchor.save() data = {'rel': [self.anchor.id, new_anchor.id]} - serializer = self.serializer_class(data, instance=self.instance) + serializer = self.serializer_class(self.instance, data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(ReadOnlyManyToManyModel.objects.all()), 1) @@ -380,7 +380,7 @@ class ReadOnlyManyToManyTests(TestCase): new_anchor = Anchor() new_anchor.save() data = {} - serializer = self.serializer_class(data, instance=self.instance) + serializer = self.serializer_class(self.instance, data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(ReadOnlyManyToManyModel.objects.all()), 1) @@ -400,7 +400,7 @@ class DefaultValueTests(TestCase): def test_create_using_default(self): data = {} - serializer = self.serializer_class(data) + serializer = self.serializer_class(data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(self.objects.all()), 1) @@ -409,7 +409,7 @@ class DefaultValueTests(TestCase): def test_create_overriding_default(self): data = {'text': 'overridden'} - serializer = self.serializer_class(data) + serializer = self.serializer_class(data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(self.objects.all()), 1) @@ -428,7 +428,7 @@ class CallableDefaultValueTests(TestCase): def test_create_using_default(self): data = {} - serializer = self.serializer_class(data) + serializer = self.serializer_class(data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(self.objects.all()), 1) @@ -437,7 +437,7 @@ class CallableDefaultValueTests(TestCase): def test_create_overriding_default(self): data = {'text': 'overridden'} - serializer = self.serializer_class(data) + serializer = self.serializer_class(data=data) self.assertEquals(serializer.is_valid(), True) instance = serializer.save() self.assertEquals(len(self.objects.all()), 1) @@ -499,11 +499,11 @@ class BlankFieldTests(TestCase): self.data = {'title': ''} def test_create_blank_field(self): - serializer = self.serializer_class(self.data) + serializer = self.serializer_class(data=self.data) self.assertEquals(serializer.is_valid(), True) def test_create_model_blank_field(self): - serializer = self.model_serializer_class(self.data) + serializer = self.model_serializer_class(data=self.data) self.assertEquals(serializer.is_valid(), True) def test_create_not_blank_field(self): @@ -511,7 +511,7 @@ class BlankFieldTests(TestCase): 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(self.data) + serializer = self.not_blank_serializer_class(data=self.data) self.assertEquals(serializer.is_valid(), False) def test_create_model_not_blank_field(self): @@ -519,5 +519,5 @@ class BlankFieldTests(TestCase): 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(self.data) + serializer = self.not_blank_model_serializer_class(data=self.data) self.assertEquals(serializer.is_valid(), False)