diff --git a/README.md b/README.md index 3e299923d..049f9075e 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ To run the tests. **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) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 6c0a7d3c7..6e7008f1f 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -11,9 +11,11 @@ **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 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/serializers.py b/rest_framework/serializers.py index 08f0a6a81..05795ae7c 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -93,8 +93,8 @@ class BaseSerializer(Field): 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 @@ -128,7 +128,7 @@ 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(nested) @@ -153,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 @@ -369,7 +369,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/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/pk_relations.py b/rest_framework/tests/pk_relations.py index 89f53316a..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