Skip to content

Commit a5df955

Browse files
Properly support formatting of link segments in related urls (#897)
1 parent 7d2970a commit a5df955

File tree

5 files changed

+44
-7
lines changed

5 files changed

+44
-7
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ any parts of the framework not mentioned in the documentation should generally b
1919

2020
* Allow `get_serializer_class` to be overwritten when using related urls without defining `serializer_class` fallback
2121
* Preserve field names when no formatting is configured.
22+
* Properly support `JSON_API_FORMAT_RELATED_LINKS` setting in related urls. In case you want to use `dasherize` for formatting links make sure that your url pattern matches dashes as well like following example:
23+
```
24+
url(r'^orders/(?P<pk>[^/.]+)/(?P<related_field>[-\w]+)/$',
25+
OrderViewSet.as_view({'get': 'retrieve_related'}),
26+
name='order-related'),
27+
```
28+
2229

2330
### Deprecated
2431

docs/usage.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,11 @@ For example, with a serializer property `created_by` and with `'dasherize'` form
515515

516516
The relationship name is formatted by the `JSON_API_FORMAT_FIELD_NAMES` setting, but the URL segments are formatted by the `JSON_API_FORMAT_RELATED_LINKS` setting.
517517

518+
<div class="warning">
519+
<strong>Note:</strong>
520+
When using this setting make sure that your url pattern matches the formatted url segement.
521+
</div>
522+
518523
### Related fields
519524

520525
#### ResourceRelatedField
@@ -702,7 +707,7 @@ All you need is just add to `urls.py`:
702707
url(r'^orders/(?P<pk>[^/.]+)/$',
703708
OrderViewSet.as_view({'get': 'retrieve'}),
704709
name='order-detail'),
705-
url(r'^orders/(?P<pk>[^/.]+)/(?P<related_field>\w+)/$',
710+
url(r'^orders/(?P<pk>[^/.]+)/(?P<related_field>[-\w]+)/$',
706711
OrderViewSet.as_view({'get': 'retrieve_related'}),
707712
name='order-related'),
708713
```
@@ -775,7 +780,7 @@ The urlconf would need to contain a route like the following:
775780

776781
```python
777782
url(
778-
regex=r'^orders/(?P<pk>[^/.]+)/relationships/(?P<related_field>[^/.]+)$',
783+
regex=r'^orders/(?P<pk>[^/.]+)/relationships/(?P<related_field>[-/w]+)$',
779784
view=OrderRelationshipView.as_view(),
780785
name='order-relationships'
781786
)

example/tests/test_views.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22
from datetime import datetime
33

4-
from django.test import RequestFactory
4+
from django.test import RequestFactory, override_settings
55
from django.utils import timezone
66
from rest_framework import status
77
from rest_framework.decorators import action
@@ -92,6 +92,18 @@ def test_get_blog_relationship_entry_set(self):
9292

9393
assert response.data == expected_data
9494

95+
@override_settings(JSON_API_FORMAT_RELATED_LINKS="dasherize")
96+
def test_get_blog_relationship_entry_set_with_formatted_link(self):
97+
response = self.client.get(
98+
"/blogs/{}/relationships/entry-set".format(self.blog.id)
99+
)
100+
expected_data = [
101+
{"type": format_resource_type("Entry"), "id": str(self.first_entry.id)},
102+
{"type": format_resource_type("Entry"), "id": str(self.second_entry.id)},
103+
]
104+
105+
assert response.data == expected_data
106+
95107
def test_put_entry_relationship_blog_returns_405(self):
96108
url = "/entries/{}/relationships/blog".format(self.first_entry.id)
97109
response = self.client.put(url, data={})
@@ -507,6 +519,17 @@ def test_retrieve_related_None(self):
507519
self.assertEqual(resp.status_code, 200)
508520
self.assertEqual(resp.json(), {"data": None})
509521

522+
@override_settings(JSON_API_FORMAT_RELATED_LINKS="dasherize")
523+
def test_retrieve_related_with_formatted_link(self):
524+
first_entry = EntryFactory(authors=(self.author,))
525+
526+
kwargs = {"pk": self.author.pk, "related_field": "first-entry"}
527+
url = reverse("author-related", kwargs=kwargs)
528+
resp = self.client.get(url)
529+
530+
self.assertEqual(resp.status_code, 200)
531+
self.assertEqual(resp.json()["data"]["id"], str(first_entry.id))
532+
510533

511534
class TestValidationErrorResponses(TestBase):
512535
def test_if_returns_error_on_empty_post(self):

example/urls_test.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,17 @@
7878
name="entry-featured",
7979
),
8080
re_path(
81-
r"^authors/(?P<pk>[^/.]+)/(?P<related_field>\w+)/$",
81+
r"^authors/(?P<pk>[^/.]+)/(?P<related_field>[-\w]+)/$",
8282
AuthorViewSet.as_view({"get": "retrieve_related"}),
8383
name="author-related",
8484
),
8585
re_path(
86-
r"^entries/(?P<pk>[^/.]+)/relationships/(?P<related_field>\w+)$",
86+
r"^entries/(?P<pk>[^/.]+)/relationships/(?P<related_field>[\-\w]+)$",
8787
EntryRelationshipView.as_view(),
8888
name="entry-relationships",
8989
),
9090
re_path(
91-
r"^blogs/(?P<pk>[^/.]+)/relationships/(?P<related_field>\w+)$",
91+
r"^blogs/(?P<pk>[^/.]+)/relationships/(?P<related_field>[^/.]+)$",
9292
BlogRelationshipView.as_view(),
9393
name="blog-relationships",
9494
),

rest_framework_json_api/views.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def get_related_serializer_class(self):
157157
parent_serializer_class = self.get_serializer_class()
158158

159159
if "related_field" in self.kwargs:
160-
field_name = self.kwargs["related_field"]
160+
field_name = self.get_related_field_name()
161161

162162
# Try get the class from related_serializers
163163
if hasattr(parent_serializer_class, "related_serializers"):
@@ -402,6 +402,8 @@ def get_related_instance(self):
402402

403403
def get_related_field_name(self):
404404
field_name = self.kwargs["related_field"]
405+
field_name = undo_format_link_segment(field_name)
406+
405407
if field_name in self.field_name_mapping:
406408
return self.field_name_mapping[field_name]
407409
return field_name

0 commit comments

Comments
 (0)