diff options
| -rw-r--r-- | docs/api-guide/pagination.md | 18 | ||||
| -rw-r--r-- | rest_framework/relations.py | 4 | ||||
| -rw-r--r-- | tests/test_relations_pk.py | 20 | 
3 files changed, 33 insertions, 9 deletions
| diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index bc65267f..3518fb34 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -80,11 +80,11 @@ This pagination style accepts a single number page number in the request query p  #### Setup -To enable the `PageNumberPagination` style globally, use the following configuration, modifying the `DEFAULT_PAGE_SIZE` as desired: +To enable the `PageNumberPagination` style globally, use the following configuration, modifying the `PAGE_SIZE` as desired:      REST_FRAMEWORK = {          'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', -        'DEFAULT_PAGE_SIZE': 100 +        'PAGE_SIZE': 100      }  On `GenericAPIView` subclasses you may also set the `pagination_class` attribute to select `PageNumberPagination` on a per-view basis. @@ -95,7 +95,7 @@ The `PageNumberPagination` class includes a number of attributes that may be ove  To set these attributes you should override the `PageNumberPagination` class, and then enable your custom pagination class as above. -* `page_size` - A numeric value indicating the page size. If set, this overrides the `DEFAULT_PAGE_SIZE` setting. Defaults to the same value as the `DEFAULT_PAGE_SIZE` settings key. +* `page_size` - A numeric value indicating the page size. If set, this overrides the `PAGE_SIZE` setting. Defaults to the same value as the `PAGE_SIZE` settings key.  * `page_query_param` - A string value indicating the name of the query parameter to use for the pagination control.  * `page_size_query_param` - If set, this is a string value indicating the name of a query parameter that allows the client to set the page size on a per-request basis. Defaults to `None`, indicating that the client may not control the requested page size.  * `max_page_size` - If set, this is a numeric value indicating the maximum allowable requested page size. This attribute is only valid if `page_size_query_param` is also set. @@ -133,7 +133,7 @@ To enable the `PageNumberPagination` style globally, use the following configura          'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'      } -Optionally, you may also set a `DEFAULT_PAGE_SIZE` key. If the `DEFAULT_PAGE_SIZE` parameter is also used then the `limit` query parameter will be optional, and may be omitted by the client. +Optionally, you may also set a `PAGE_SIZE` key. If the `PAGE_SIZE` parameter is also used then the `limit` query parameter will be optional, and may be omitted by the client.  On `GenericAPIView` subclasses you may also set the `pagination_class` attribute to select `LimitOffsetPagination` on a per-view basis. @@ -143,7 +143,7 @@ The `LimitOffsetPagination` class includes a number of attributes that may be ov  To set these attributes you should override the `LimitOffsetPagination` class, and then enable your custom pagination class as above. -* `default_limit` - A numeric value indicating the limit to use if one is not provided by the client in a query parameter. Defaults to the same value as the `DEFAULT_PAGE_SIZE` settings key. +* `default_limit` - A numeric value indicating the limit to use if one is not provided by the client in a query parameter. Defaults to the same value as the `PAGE_SIZE` settings key.  * `limit_query_param` - A string value indicating the name of the "limit" query parameter. Defaults to `'limit'`.  * `offset_query_param` - A string value indicating the name of the "offset" query parameter. Defaults to `'offset'`.  * `max_limit` - If set this is a numeric value indicating the maximum allowable limit that may be requested by the client. Defaults to `None`. @@ -164,7 +164,7 @@ Cursor based pagination is more complex than other schemes. It also requires tha  #### Details and limitations -Proper use of cursor based pagination a little attention to detail. You'll need to think about what ordering you want the scheme to be applied against. The default is to order by `"-created"`. This assumes that **there must be a 'created' timestamp field** on the model instances, and will present a "timeline" style paginated view, with the most recently added items first. +Proper use of cursor based pagination requires a little attention to detail. You'll need to think about what ordering you want the scheme to be applied against. The default is to order by `"-created"`. This assumes that **there must be a 'created' timestamp field** on the model instances, and will present a "timeline" style paginated view, with the most recently added items first.  You can modify the ordering by overriding the `'ordering'` attribute on the pagination class, or by using the `OrderingFilter` filter class together with `CursorPagination`. When used with `OrderingFilter` you should strongly consider restricting the fields that the user may order by. @@ -181,11 +181,11 @@ For more technical details on the implementation we use for cursor pagination, t  #### Setup -To enable the `CursorPagination` style globally, use the following configuration, modifying the `DEFAULT_PAGE_SIZE` as desired: +To enable the `CursorPagination` style globally, use the following configuration, modifying the `PAGE_SIZE` as desired:      REST_FRAMEWORK = {          'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination', -        'DEFAULT_PAGE_SIZE': 100 +        'PAGE_SIZE': 100      }  On `GenericAPIView` subclasses you may also set the `pagination_class` attribute to select `CursorPagination` on a per-view basis. @@ -314,4 +314,4 @@ The [`DRF-extensions` package][drf-extensions] includes a [`PaginateByMaxMixin`  [link-header]: ../img/link-header-pagination.png  [drf-extensions]: http://chibisov.github.io/drf-extensions/docs/  [paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin -[disqus-cursor-api]: http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api/
\ No newline at end of file +[disqus-cursor-api]: http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api/ diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 0b7c9d86..3a966c5b 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -360,6 +360,10 @@ class ManyRelatedField(Field):          ]      def get_attribute(self, instance): +        # Can't have any relationships if not created +        if not instance.pk: +            return [] +          relationship = get_attribute(instance, self.source_attrs)          return relationship.all() if (hasattr(relationship, 'all')) else relationship diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py index f872a8dc..ca43272b 100644 --- a/tests/test_relations_pk.py +++ b/tests/test_relations_pk.py @@ -143,6 +143,16 @@ class PKManyToManyTests(TestCase):          ]          self.assertEqual(serializer.data, expected) +    def test_many_to_many_unsaved(self): +        source = ManyToManySource(name='source-unsaved') + +        serializer = ManyToManySourceSerializer(source) + +        expected = {'id': None, 'name': 'source-unsaved', 'targets': []} +        # no query if source hasn't been created yet +        with self.assertNumQueries(0): +            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) @@ -296,6 +306,16 @@ class PKForeignKeyTests(TestCase):          self.assertFalse(serializer.is_valid())          self.assertEqual(serializer.errors, {'target': ['This field may not be null.']}) +    def test_foreign_key_with_unsaved(self): +        source = ForeignKeySource(name='source-unsaved') +        expected = {'id': None, 'name': 'source-unsaved', 'target': None} + +        serializer = ForeignKeySourceSerializer(source) + +        # no query if source hasn't been created yet +        with self.assertNumQueries(0): +            self.assertEqual(serializer.data, expected) +      def test_foreign_key_with_empty(self):          """          Regression test for #1072 | 
