Skip to content

Commit 4ad1094

Browse files
committed
HyperlinkedModelSerializer supports overriding of 'url' field. Closes #936
1 parent a7a9e87 commit 4ad1094

File tree

3 files changed

+47
-23
lines changed

3 files changed

+47
-23
lines changed

rest_framework/serializers.py

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -904,34 +904,20 @@ class HyperlinkedModelSerializer(ModelSerializer):
904904
_default_view_name = '%(model_name)s-detail'
905905
_hyperlink_field_class = HyperlinkedRelatedField
906906

907-
# Just a placeholder to ensure 'url' is the first field
908-
# The field itself is actually created on initialization,
909-
# when the view_name and lookup_field arguments are available.
910-
url = Field()
911-
912-
def __init__(self, *args, **kwargs):
913-
super(HyperlinkedModelSerializer, self).__init__(*args, **kwargs)
907+
def get_default_fields(self):
908+
fields = super(HyperlinkedModelSerializer, self).get_default_fields()
914909

915910
if self.opts.view_name is None:
916911
self.opts.view_name = self._get_default_view_name(self.opts.model)
917912

918-
url_field = HyperlinkedIdentityField(
919-
view_name=self.opts.view_name,
920-
lookup_field=self.opts.lookup_field
921-
)
922-
url_field.initialize(self, 'url')
923-
self.fields['url'] = url_field
913+
if 'url' not in fields:
914+
url_field = HyperlinkedIdentityField(
915+
view_name=self.opts.view_name,
916+
lookup_field=self.opts.lookup_field
917+
)
918+
fields.insert(0, 'url', url_field)
924919

925-
def _get_default_view_name(self, model):
926-
"""
927-
Return the view name to use if 'view_name' is not specified in 'Meta'
928-
"""
929-
model_meta = model._meta
930-
format_kwargs = {
931-
'app_label': model_meta.app_label,
932-
'model_name': model_meta.object_name.lower()
933-
}
934-
return self._default_view_name % format_kwargs
920+
return fields
935921

936922
def get_pk_field(self, model_field):
937923
if self.opts.fields and model_field.name in self.opts.fields:
@@ -966,3 +952,14 @@ def get_identity(self, data):
966952
return data.get('url', None)
967953
except AttributeError:
968954
return None
955+
956+
def _get_default_view_name(self, model):
957+
"""
958+
Return the view name to use if 'view_name' is not specified in 'Meta'
959+
"""
960+
model_meta = model._meta
961+
format_kwargs = {
962+
'app_label': model_meta.app_label,
963+
'model_name': model_meta.object_name.lower()
964+
}
965+
return self._default_view_name % format_kwargs

rest_framework/tests/test_hyperlinkedserializers.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,30 @@ def test_put_detail_view(self):
301301
data=json.dumps(self.data),
302302
content_type='application/json')
303303
self.assertEqual(response.status_code, status.HTTP_200_OK)
304+
305+
306+
class TestOverriddenURLField(TestCase):
307+
def setUp(self):
308+
class OverriddenURLSerializer(serializers.HyperlinkedModelSerializer):
309+
url = serializers.SerializerMethodField('get_url')
310+
311+
class Meta:
312+
model = BlogPost
313+
fields = ('title', 'url')
314+
315+
def get_url(self, obj):
316+
return 'foo bar'
317+
318+
self.Serializer = OverriddenURLSerializer
319+
self.obj = BlogPost.objects.create(title='New blog post')
320+
321+
def test_overridden_url_field(self):
322+
"""
323+
The 'url' field should respect overriding.
324+
Regression test for #936.
325+
"""
326+
serializer = self.Serializer(self.obj)
327+
self.assertEqual(
328+
serializer.data,
329+
{'title': 'New blog post', 'url': 'foo bar'}
330+
)

tmp.html

Whitespace-only changes.

0 commit comments

Comments
 (0)