Skip to content

Commit 75751cc

Browse files
Allow relative style hyperlinked URLs
1 parent 9c996d7 commit 75751cc

File tree

3 files changed

+34
-4
lines changed

3 files changed

+34
-4
lines changed

docs/api-guide/serializers.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,25 @@ You can explicitly include the primary key by adding it to the `fields` option,
678678
model = Account
679679
fields = ('url', 'id', 'account_name', 'users', 'created')
680680

681+
## Absolute and relative URLs
682+
683+
When instantiating a `HyperlinkedModelSerializer` you must include the current
684+
`request` in the serializer context, for example:
685+
686+
serializer = AccountSerializer(queryset, context={'request': request})
687+
688+
Doing so will ensure that the hyperlinks can include an appropriate hostname,
689+
so that the resulting representation uses fully qualified URLs, such as:
690+
691+
http://api.example.com/accounts/1/
692+
693+
Rather than relative URLs, such as:
694+
695+
/accounts/1/
696+
697+
If you *do* want to use relative URLs, you should explicitly pass `{'request': None}`
698+
in the serializer context.
699+
681700
## How hyperlinked views are determined
682701

683702
There needs to be a way of determining which views should be used for hyperlinking to model instances.

rest_framework/relations.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,15 +325,15 @@ def to_internal_value(self, data):
325325
self.fail('does_not_exist')
326326

327327
def to_representation(self, value):
328-
request = self.context.get('request', None)
329-
format = self.context.get('format', None)
330-
331-
assert request is not None, (
328+
assert 'request' in self.context, (
332329
"`%s` requires the request in the serializer"
333330
" context. Add `context={'request': request}` when instantiating "
334331
"the serializer." % self.__class__.__name__
335332
)
336333

334+
request = self.context['request']
335+
format = self.context.get('format', None)
336+
337337
# By default use whatever format is given for the current context
338338
# unless the target is a different type to the source.
339339
#

tests/test_relations_hyperlink.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ def setUp(self):
8282
for target in ManyToManyTarget.objects.all():
8383
source.targets.add(target)
8484

85+
def test_relative_hyperlinks(self):
86+
queryset = ManyToManySource.objects.all()
87+
serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': None})
88+
expected = [
89+
{'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/']},
90+
{'url': '/manytomanysource/2/', 'name': 'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']},
91+
{'url': '/manytomanysource/3/', 'name': 'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}
92+
]
93+
with self.assertNumQueries(4):
94+
self.assertEqual(serializer.data, expected)
95+
8596
def test_many_to_many_retrieve(self):
8697
queryset = ManyToManySource.objects.all()
8798
serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request})

0 commit comments

Comments
 (0)