From 1fb988b533b2da7aab6c214b0915e672a24e7009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Wed, 12 Dec 2012 13:43:04 +0100 Subject: [PATCH 1/7] added support for absolute/relative url switching --- docs/api-guide/settings.md | 4 ++++ docs/topics/release-notes.md | 5 +++++ rest_framework/pagination.py | 13 +++++++++++-- rest_framework/reverse.py | 6 ++++-- rest_framework/settings.py | 2 ++ 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 7884d096b..84fef1cf9 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -166,4 +166,8 @@ Default: `'format'` Default: `'format'` +## USE_ABSOLUTE_URLS + +Default: `True` + [cite]: http://www.python.org/dev/peps/pep-0020/ diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 4f83cfd8f..c6715df6d 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -4,6 +4,11 @@ > > — Eric S. Raymond, [The Cathedral and the Bazaar][cite]. +## Master + +* Added support for absolute/relative url switching +* Bugfix: Fix absolute/relative url mix + ## 2.1.9 **Date**: 11th Dec 2012 diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index d241ade7c..4e4831390 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -1,4 +1,5 @@ from rest_framework import serializers +from rest_framework.settings import api_settings from rest_framework.templatetags.rest_framework import replace_query_param # TODO: Support URLconf kwarg-style paging @@ -15,7 +16,11 @@ class NextPageField(serializers.Field): return None page = value.next_page_number() request = self.context.get('request') - url = request and request.build_absolute_uri() or '' + if api_settings.USE_ABSOLUTE_URLS: + assert request, "request is required for building absolute url" + url = request.build_absolute_uri() + else: + url = request and request.get_full_path() or '' return replace_query_param(url, self.page_field, page) @@ -30,7 +35,11 @@ class PreviousPageField(serializers.Field): return None page = value.previous_page_number() request = self.context.get('request') - url = request and request.build_absolute_uri() or '' + if api_settings.USE_ABSOLUTE_URLS: + assert request, "request is required for building absolute url" + url = request.build_absolute_uri() + else: + url = request and request.get_full_path() or '' return replace_query_param(url, self.page_field, page) diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py index c9db02f06..33a8b5eba 100644 --- a/rest_framework/reverse.py +++ b/rest_framework/reverse.py @@ -1,6 +1,7 @@ """ Provide reverse functions that return fully qualified URLs """ +from rest_framework.settings import api_settings from django.core.urlresolvers import reverse as django_reverse from django.utils.functional import lazy @@ -14,8 +15,9 @@ def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra kwargs = kwargs or {} kwargs['format'] = format url = django_reverse(viewname, args=args, kwargs=kwargs, **extra) - if request: - return request.build_absolute_uri(url) + if api_settings.USE_ABSOLUTE_URLS: + assert request, "request is required for building absolute url" + url = request.build_absolute_uri(url) return url diff --git a/rest_framework/settings.py b/rest_framework/settings.py index ee24a4ad9..c65ee998b 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -74,6 +74,8 @@ DEFAULTS = { 'URL_FORMAT_OVERRIDE': 'format', 'FORMAT_SUFFIX_KWARG': 'format', + + 'USE_ABSOLUTE_URLS': True, } From 5b351567d8ce7a39e85a6ed00b7a62df23fcf903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Wed, 12 Dec 2012 15:05:33 +0100 Subject: [PATCH 2/7] added "use_absolute_urls" option to serializer --- rest_framework/pagination.py | 5 ++--- rest_framework/reverse.py | 2 +- rest_framework/serializers.py | 5 ++++- rest_framework/tests/pagination.py | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 4e4831390..203391515 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -1,5 +1,4 @@ from rest_framework import serializers -from rest_framework.settings import api_settings from rest_framework.templatetags.rest_framework import replace_query_param # TODO: Support URLconf kwarg-style paging @@ -16,7 +15,7 @@ class NextPageField(serializers.Field): return None page = value.next_page_number() request = self.context.get('request') - if api_settings.USE_ABSOLUTE_URLS: + if self.parent.use_absolute_urls: assert request, "request is required for building absolute url" url = request.build_absolute_uri() else: @@ -35,7 +34,7 @@ class PreviousPageField(serializers.Field): return None page = value.previous_page_number() request = self.context.get('request') - if api_settings.USE_ABSOLUTE_URLS: + if self.parent.use_absolute_urls: assert request, "request is required for building absolute url" url = request.build_absolute_uri() else: diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py index 33a8b5eba..4e2c683be 100644 --- a/rest_framework/reverse.py +++ b/rest_framework/reverse.py @@ -1,9 +1,9 @@ """ Provide reverse functions that return fully qualified URLs """ -from rest_framework.settings import api_settings from django.core.urlresolvers import reverse as django_reverse from django.utils.functional import lazy +from rest_framework.settings import api_settings def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index caa7c980f..b46a5e388 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -16,6 +16,7 @@ from rest_framework.compat import get_concrete_model from rest_framework.fields import * +from rest_framework.settings import api_settings class DictWithMetadata(dict): @@ -91,6 +92,7 @@ class SerializerOptions(object): self.depth = getattr(meta, 'depth', 0) self.fields = getattr(meta, 'fields', ()) self.exclude = getattr(meta, 'exclude', ()) + self.use_absolute_urls = getattr(meta, 'use_absolute_urls', api_settings.USE_ABSOLUTE_URLS) class BaseSerializer(Field): @@ -101,12 +103,13 @@ class BaseSerializer(Field): _dict_class = SortedDictWithMetadata # Set to unsorted dict for backwards compatibility with unsorted implementations. def __init__(self, instance=None, data=None, files=None, - context=None, partial=False, **kwargs): + context=None, partial=False, use_absolute_urls=None, **kwargs): super(BaseSerializer, self).__init__(**kwargs) self.opts = self._options_class(self.Meta) self.parent = None self.root = None self.partial = partial + self.use_absolute_urls = use_absolute_urls if use_absolute_urls is not None else self.opts.use_absolute_urls self.context = context or {} diff --git a/rest_framework/tests/pagination.py b/rest_framework/tests/pagination.py index 3062007d4..146b36ab9 100644 --- a/rest_framework/tests/pagination.py +++ b/rest_framework/tests/pagination.py @@ -160,13 +160,13 @@ class UnitTestPagination(TestCase): self.last_page = paginator.page(3) def test_native_pagination(self): - serializer = pagination.PaginationSerializer(self.first_page) + serializer = pagination.PaginationSerializer(self.first_page, use_absolute_urls=False) 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(self.last_page) + serializer = pagination.PaginationSerializer(self.last_page, use_absolute_urls=False) self.assertEquals(serializer.data['count'], 26) self.assertEquals(serializer.data['next'], None) self.assertEquals(serializer.data['previous'], '?page=2') From 11375127e2e0551fab352fd29841449a995530b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Thu, 13 Dec 2012 10:40:05 +0100 Subject: [PATCH 3/7] added tests for absolute url pagination --- rest_framework/tests/pagination.py | 59 +++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/rest_framework/tests/pagination.py b/rest_framework/tests/pagination.py index 146b36ab9..2908e1269 100644 --- a/rest_framework/tests/pagination.py +++ b/rest_framework/tests/pagination.py @@ -4,7 +4,7 @@ from django.core.paginator import Paginator from django.test import TestCase from django.test.client import RequestFactory from django.utils import unittest -from rest_framework import generics, status, pagination, filters +from rest_framework import generics, status, pagination, filters, serializers from rest_framework.compat import django_filters from rest_framework.tests.models import BasicModel, FilterableItem @@ -34,6 +34,23 @@ if django_filters: filter_backend = filters.DjangoFilterBackend +class AbsoluteUrlsSerializer(serializers.ModelSerializer): + """ + Serializer for testing absolute urls + """ + class Meta: + use_absolute_urls = True + + +class AbsoluteUrlsView(generics.ListAPIView): + """ + View for testing absolute urls + """ + model = BasicModel + paginate_by = 1 + + + class DefaultPageSizeKwargView(generics.ListAPIView): """ View for testing default paginate_by_param usage @@ -173,6 +190,46 @@ class UnitTestPagination(TestCase): self.assertEquals(serializer.data['results'], self.objects[20:]) +class TestPaginationWithAbsoluteUrls(TestCase): + """ + Tests for using absolute urls + """ + + def setUp(self): + items = ['foo', 'bar', 'baz'] + for item in items: + BasicModel(text=item).save() + self.objects = BasicModel.objects + self.data = [ + {'id': obj.id, 'text': obj.text} + for obj in self.objects.all() + ] + self.view = AbsoluteUrlsView.as_view() + + def test_paginated_root_view_urls(self): + """ + Tests absolute/relative url switch + """ + request = factory.get('/') + response = self.view(request).render() + self.assertEquals(response.data['count'], 3) + self.assertEquals(response.data['next'], 'http://testserver/?page=2') + self.assertEquals(response.data['previous'], None) + self.assertEquals(response.data['results'], self.data[0:1]) + request = factory.get('/?page=2') + response = self.view(request).render() + self.assertEquals(response.data['count'], 3) + self.assertEquals(response.data['next'], 'http://testserver/?page=3') + self.assertEquals(response.data['previous'], 'http://testserver/?page=1') + self.assertEquals(response.data['results'], self.data[1:2]) + request = factory.get('/?page=3') + response = self.view(request).render() + self.assertEquals(response.data['count'], 3) + self.assertEquals(response.data['next'], None) + self.assertEquals(response.data['previous'], 'http://testserver/?page=2') + self.assertEquals(response.data['results'], self.data[2:3]) + + class TestUnpaginated(TestCase): """ Tests for list views without pagination. From f7497a2c0af901622dddf6ff47fbe73be258ab29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Thu, 13 Dec 2012 12:32:28 +0100 Subject: [PATCH 4/7] added tests for url switch in fields --- rest_framework/fields.py | 19 ++++--- rest_framework/reverse.py | 4 +- .../tests/hyperlinkedserializers.py | 54 +++++++++++++++++++ 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 75ce1b9f9..73bd9e2bc 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -543,6 +543,9 @@ class HyperlinkedRelatedField(RelatedField): self.slug_url_kwarg = kwargs.pop('slug_url_kwarg', default_slug_kwarg) self.format = kwargs.pop('format', None) + + self.use_absolute_urls = kwargs.pop('use_absolute_urls', self.parent.use_absolute_urls) + super(HyperlinkedRelatedField, self).__init__(*args, **kwargs) def get_slug_field(self): @@ -555,12 +558,13 @@ class HyperlinkedRelatedField(RelatedField): view_name = self.view_name request = self.context.get('request', None) format = self.format or self.context.get('format', None) + use_absolute_urls = self.use_absolute_urls pk = getattr(obj, 'pk', None) if pk is None: return kwargs = {self.pk_url_kwarg: pk} try: - return reverse(view_name, kwargs=kwargs, request=request, format=format) + return reverse(view_name, kwargs=kwargs, request=request, format=format, use_absolute_urls=use_absolute_urls) except: pass @@ -571,13 +575,13 @@ class HyperlinkedRelatedField(RelatedField): kwargs = {self.slug_url_kwarg: slug} try: - return reverse(self.view_name, kwargs=kwargs, request=request, format=format) + return reverse(self.view_name, kwargs=kwargs, request=request, format=format, use_absolute_urls=use_absolute_urls) except: pass kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug} try: - return reverse(self.view_name, kwargs=kwargs, request=request, format=format) + return reverse(self.view_name, kwargs=kwargs, request=request, format=format, use_absolute_urls=use_absolute_urls) except: pass @@ -651,6 +655,8 @@ class HyperlinkedIdentityField(Field): self.pk_url_kwarg = kwargs.pop('pk_url_kwarg', self.pk_url_kwarg) self.slug_url_kwarg = kwargs.pop('slug_url_kwarg', default_slug_kwarg) + self.use_absolute_urls = kwargs.pop('use_absolute_urls', self.parent.use_absolute_urls) + super(HyperlinkedIdentityField, self).__init__(*args, **kwargs) def field_to_native(self, obj, field_name): @@ -658,8 +664,9 @@ class HyperlinkedIdentityField(Field): format = self.format or self.context.get('format', None) view_name = self.view_name or self.parent.opts.view_name kwargs = {self.pk_url_kwarg: obj.pk} + use_absolute_urls = self.use_absolute_urls try: - return reverse(view_name, kwargs=kwargs, request=request, format=format) + return reverse(view_name, kwargs=kwargs, request=request, format=format, use_absolute_urls=use_absolute_urls) except: pass @@ -670,13 +677,13 @@ class HyperlinkedIdentityField(Field): kwargs = {self.slug_url_kwarg: slug} try: - return reverse(self.view_name, kwargs=kwargs, request=request, format=format) + return reverse(self.view_name, kwargs=kwargs, request=request, format=format, use_absolute_urls=use_absolute_urls) except: pass kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug} try: - return reverse(self.view_name, kwargs=kwargs, request=request, format=format) + return reverse(self.view_name, kwargs=kwargs, request=request, format=format, use_absolute_urls=use_absolute_urls) except: pass diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py index 4e2c683be..34108fa85 100644 --- a/rest_framework/reverse.py +++ b/rest_framework/reverse.py @@ -6,7 +6,7 @@ from django.utils.functional import lazy from rest_framework.settings import api_settings -def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): +def reverse(viewname, args=None, kwargs=None, request=None, format=None, use_absolute_urls=api_settings.USE_ABSOLUTE_URLS, **extra): """ Same as `django.core.urlresolvers.reverse`, but optionally takes a request and returns a fully qualified URL, using the request to get the base URL. @@ -15,7 +15,7 @@ def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra kwargs = kwargs or {} kwargs['format'] = format url = django_reverse(viewname, args=args, kwargs=kwargs, **extra) - if api_settings.USE_ABSOLUTE_URLS: + if use_absolute_urls: assert request, "request is required for building absolute url" url = request.build_absolute_uri(url) return url diff --git a/rest_framework/tests/hyperlinkedserializers.py b/rest_framework/tests/hyperlinkedserializers.py index 24bf61bf8..b9120ccca 100644 --- a/rest_framework/tests/hyperlinkedserializers.py +++ b/rest_framework/tests/hyperlinkedserializers.py @@ -26,6 +26,14 @@ class PhotoSerializer(serializers.Serializer): return Photo(**attrs) +class PhotoUrlSerializer(PhotoSerializer): + url = serializers.HyperlinkedIdentityField(view_name='photoswithabsoluteurls-detail', use_absolute_urls=True) + + class Meta: + model = Photo + use_absolute_urls = False + + class BasicList(generics.ListCreateAPIView): model = BasicModel model_serializer_class = serializers.HyperlinkedModelSerializer @@ -74,6 +82,16 @@ class AlbumDetail(generics.RetrieveAPIView): model = Album +class PhotoUrlList(generics.ListAPIView): + model = Photo + serializer_class = PhotoUrlSerializer + + +class PhotoUrlDetail(generics.RetrieveAPIView): + model = Photo + serializer_class = PhotoUrlSerializer + + class OptionalRelationDetail(generics.RetrieveUpdateDestroyAPIView): model = OptionalRelationModel model_serializer_class = serializers.HyperlinkedModelSerializer @@ -90,6 +108,8 @@ urlpatterns = patterns('', url(r'^comments/(?P\d+)/$', BlogPostCommentDetail.as_view(), name='blogpostcomment-detail'), url(r'^albums/(?P\w[\w-]*)/$', AlbumDetail.as_view(), name='album-detail'), url(r'^photos/$', PhotoListCreate.as_view(), name='photo-list'), + url(r'^photos-with-absolute-urls/$', PhotoAbsoluteUrlList.as_view(), name='photoswithabsoluteurls-list'), + url(r'^photos-with-absolute-urls/(?P<pk>\d+)/$', PhotoAbsoluteUrlDetail.as_view(), name='photoswithabsoluteurls-detail'), url(r'^optionalrelation/(?P<pk>\d+)/$', OptionalRelationDetail.as_view(), name='optionalrelationmodel-detail'), ) @@ -260,3 +280,37 @@ class TestOptionalRelationHyperlinkedView(TestCase): data=json.dumps(self.data), content_type='application/json') self.assertEqual(response.status_code, status.HTTP_200_OK) + + +class TestUrlOptionsView(TestCase): + urls = 'rest_framework.tests.hyperlinkedserializers' + + def setUp(self): + """ + Create a album and photos + """ + self.album = Album.objects.create(title="Test album") + items = ['beach', 'sunset', 'moon'] + for item in items: + Photo(description=item, album=self.album).save() + self.objects = Photo.objects + self.data = [ + { + 'url': '/photos-with-absolute-urls/%d/' % obj.id, + 'description': obj.text, + 'album_url': 'http://testserver/optionalrelation/%d/' % obj.album + } + for obj in self.objects.all() + ] + self.list_view = PhotoUrlList.as_view() + self.detail_view = PhotoUrlDetail.as_view() + + def test_get_detail_view(self): + """ + GET requests to RetrieveAPIView with optional relations should return None + for non existing relations. + """ + request = factory.get('/photos-with-absolute-urls/1') + response = self.detail_view(request) + self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEquals(response.data, self.data[0:2]) \ No newline at end of file From f2cebb117ac261fdcf036f7d3ef34bb2654872aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= <stephan@minddust.com> Date: Thu, 13 Dec 2012 14:04:22 +0100 Subject: [PATCH 5/7] fixed field attribute assigning --- rest_framework/fields.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 73bd9e2bc..01812bd24 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -544,10 +544,13 @@ class HyperlinkedRelatedField(RelatedField): self.format = kwargs.pop('format', None) - self.use_absolute_urls = kwargs.pop('use_absolute_urls', self.parent.use_absolute_urls) - + self.use_absolute_urls = kwargs.pop('use_absolute_urls', None) super(HyperlinkedRelatedField, self).__init__(*args, **kwargs) + def initialize(self, parent, field_name): + super(HyperlinkedRelatedField, self).initialize(parent, field_name) + self.use_absolute_urls = self.use_absolute_urls or self.parent.use_absolute_urls + def get_slug_field(self): """ Get the name of a slug field to be used to look up by slug. @@ -655,10 +658,13 @@ class HyperlinkedIdentityField(Field): self.pk_url_kwarg = kwargs.pop('pk_url_kwarg', self.pk_url_kwarg) self.slug_url_kwarg = kwargs.pop('slug_url_kwarg', default_slug_kwarg) - self.use_absolute_urls = kwargs.pop('use_absolute_urls', self.parent.use_absolute_urls) - + self.use_absolute_urls = kwargs.pop('use_absolute_urls', None) super(HyperlinkedIdentityField, self).__init__(*args, **kwargs) + def initialize(self, parent, field_name): + super(HyperlinkedIdentityField, self).initialize(parent, field_name) + self.use_absolute_urls = self.use_absolute_urls or self.parent.use_absolute_urls + def field_to_native(self, obj, field_name): request = self.context.get('request', None) format = self.format or self.context.get('format', None) From 5743e50690bcfb500f9a6075052178777ab72e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= <stephan@minddust.com> Date: Thu, 13 Dec 2012 14:04:38 +0100 Subject: [PATCH 6/7] fixed url test cases --- .../tests/hyperlinkedserializers.py | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/rest_framework/tests/hyperlinkedserializers.py b/rest_framework/tests/hyperlinkedserializers.py index b9120ccca..c0ba8ca75 100644 --- a/rest_framework/tests/hyperlinkedserializers.py +++ b/rest_framework/tests/hyperlinkedserializers.py @@ -27,7 +27,7 @@ class PhotoSerializer(serializers.Serializer): class PhotoUrlSerializer(PhotoSerializer): - url = serializers.HyperlinkedIdentityField(view_name='photoswithabsoluteurls-detail', use_absolute_urls=True) + url = serializers.HyperlinkedIdentityField(view_name='photoswithmixedurls-detail', use_absolute_urls=True) class Meta: model = Photo @@ -108,8 +108,8 @@ urlpatterns = patterns('', 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'^photos-with-absolute-urls/$', PhotoAbsoluteUrlList.as_view(), name='photoswithabsoluteurls-list'), - url(r'^photos-with-absolute-urls/(?P<pk>\d+)/$', PhotoAbsoluteUrlDetail.as_view(), name='photoswithabsoluteurls-detail'), + url(r'^photos-with-mixed-urls/$', PhotoUrlList.as_view(), name='photoswithmixedurls-list'), + url(r'^photos-with-mixed-urls/(?P<pk>\d+)/$', PhotoUrlDetail.as_view(), name='photoswithmixedurls-detail'), url(r'^optionalrelation/(?P<pk>\d+)/$', OptionalRelationDetail.as_view(), name='optionalrelationmodel-detail'), ) @@ -289,28 +289,38 @@ class TestUrlOptionsView(TestCase): """ Create a album and photos """ - self.album = Album.objects.create(title="Test album") + self.album = Album.objects.create(title="test-album") items = ['beach', 'sunset', 'moon'] for item in items: Photo(description=item, album=self.album).save() self.objects = Photo.objects self.data = [ - { - 'url': '/photos-with-absolute-urls/%d/' % obj.id, - 'description': obj.text, - 'album_url': 'http://testserver/optionalrelation/%d/' % obj.album - } - for obj in self.objects.all() + { + 'url': 'http://testserver/photos-with-mixed-urls/%d/' % obj.id, + 'description': obj.description, + 'album_url': '/albums/%s/' % obj.album.title + } + for obj in self.objects.all() ] self.list_view = PhotoUrlList.as_view() self.detail_view = PhotoUrlDetail.as_view() + def test_get_list_view(self): + """ + GET requests to RetrieveAPIView with optional relations should return None + for non existing relations. + """ + request = factory.get('/photos-with-mixed-urls/') + response = self.list_view(request) + self.assertEquals(response.status_code, status.HTTP_200_OK) + self.assertEquals(response.data, self.data[:]) + def test_get_detail_view(self): """ GET requests to RetrieveAPIView with optional relations should return None for non existing relations. """ - request = factory.get('/photos-with-absolute-urls/1') - response = self.detail_view(request) + request = factory.get('/photos-with-mixed-urls/1/') + response = self.detail_view(request, pk=1) self.assertEquals(response.status_code, status.HTTP_200_OK) - self.assertEquals(response.data, self.data[0:2]) \ No newline at end of file + self.assertEquals(response.data, self.data[0]) \ No newline at end of file From ec29d44df3f3a309da21f6e878e63c2f4c3b4443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= <stephan@minddust.com> Date: Thu, 13 Dec 2012 14:25:56 +0100 Subject: [PATCH 7/7] added remaining docs --- docs/api-guide/fields.md | 1 + docs/api-guide/serializers.md | 8 ++++++++ docs/api-guide/settings.md | 2 ++ 3 files changed, 11 insertions(+) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 50a09701e..80c7a859f 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -322,6 +322,7 @@ By default, `HyperlinkedRelatedField` is read-write, although you can change thi * `pk_url_kwarg` - The named url parameter for the pk field lookup. Default is `pk`. * `slug_url_kwarg` - The named url parameter for the slug field lookup. Default is to use the same value as given for `slug_field`. * `null` - If set to `True`, the field will accept values of `None` or the emptystring for nullable relationships. +* `use_absolute_urls` - This option will overwrite the serializer option `use_absolute_urls`. ## HyperLinkedIdentityField diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 19efde3c7..5f7839f4c 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -189,6 +189,14 @@ As an example, let's create a field that can be used represent the class name of """ pass +## Meta options + +**TODO** add remaining serializer meta options + +### `use_absolute_urls` + +This setting will overwrite the `REST_FRAMEWORK` option `USE_ABSOLUTE_URLS` and can also be overwritten by the field attribute `use_absolute_urls`. + --- # ModelSerializers diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 84fef1cf9..a9ab12319 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -170,4 +170,6 @@ Default: `'format'` Default: `True` +This setting can overwritten by the serializer meta option `use_absolute_urls` which also can be overwritten by the field attribute `use_absolute_urls`. + [cite]: http://www.python.org/dev/peps/pep-0020/