diff --git a/CHANGELOG.md b/CHANGELOG.md index ab9ec4f8..32799366 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,13 @@ any parts of the framework not mentioned in the documentation should generally b * Allow `get_serializer_class` to be overwritten when using related urls without defining `serializer_class` fallback * Preserve field names when no formatting is configured. +* 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: + ``` + url(r'^orders/(?P[^/.]+)/(?P[-\w]+)/$', + OrderViewSet.as_view({'get': 'retrieve_related'}), + name='order-related'), + ``` + ### Deprecated diff --git a/docs/usage.md b/docs/usage.md index 81e52989..79a2d918 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -515,6 +515,11 @@ For example, with a serializer property `created_by` and with `'dasherize'` form 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. +
+ Note: + When using this setting make sure that your url pattern matches the formatted url segement. +
+ ### Related fields #### ResourceRelatedField @@ -702,7 +707,7 @@ All you need is just add to `urls.py`: url(r'^orders/(?P[^/.]+)/$', OrderViewSet.as_view({'get': 'retrieve'}), name='order-detail'), -url(r'^orders/(?P[^/.]+)/(?P\w+)/$', +url(r'^orders/(?P[^/.]+)/(?P[-\w]+)/$', OrderViewSet.as_view({'get': 'retrieve_related'}), name='order-related'), ``` @@ -775,7 +780,7 @@ The urlconf would need to contain a route like the following: ```python url( - regex=r'^orders/(?P[^/.]+)/relationships/(?P[^/.]+)$', + regex=r'^orders/(?P[^/.]+)/relationships/(?P[-/w]+)$', view=OrderRelationshipView.as_view(), name='order-relationships' ) diff --git a/example/tests/test_views.py b/example/tests/test_views.py index 71e2e2db..d976ed56 100644 --- a/example/tests/test_views.py +++ b/example/tests/test_views.py @@ -1,7 +1,7 @@ import json from datetime import datetime -from django.test import RequestFactory +from django.test import RequestFactory, override_settings from django.utils import timezone from rest_framework import status from rest_framework.decorators import action @@ -92,6 +92,18 @@ def test_get_blog_relationship_entry_set(self): assert response.data == expected_data + @override_settings(JSON_API_FORMAT_RELATED_LINKS="dasherize") + def test_get_blog_relationship_entry_set_with_formatted_link(self): + response = self.client.get( + "/blogs/{}/relationships/entry-set".format(self.blog.id) + ) + expected_data = [ + {"type": format_resource_type("Entry"), "id": str(self.first_entry.id)}, + {"type": format_resource_type("Entry"), "id": str(self.second_entry.id)}, + ] + + assert response.data == expected_data + def test_put_entry_relationship_blog_returns_405(self): url = "/entries/{}/relationships/blog".format(self.first_entry.id) response = self.client.put(url, data={}) @@ -507,6 +519,17 @@ def test_retrieve_related_None(self): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.json(), {"data": None}) + @override_settings(JSON_API_FORMAT_RELATED_LINKS="dasherize") + def test_retrieve_related_with_formatted_link(self): + first_entry = EntryFactory(authors=(self.author,)) + + kwargs = {"pk": self.author.pk, "related_field": "first-entry"} + url = reverse("author-related", kwargs=kwargs) + resp = self.client.get(url) + + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.json()["data"]["id"], str(first_entry.id)) + class TestValidationErrorResponses(TestBase): def test_if_returns_error_on_empty_post(self): diff --git a/example/urls_test.py b/example/urls_test.py index 7e875936..0219ac51 100644 --- a/example/urls_test.py +++ b/example/urls_test.py @@ -78,17 +78,17 @@ name="entry-featured", ), re_path( - r"^authors/(?P[^/.]+)/(?P\w+)/$", + r"^authors/(?P[^/.]+)/(?P[-\w]+)/$", AuthorViewSet.as_view({"get": "retrieve_related"}), name="author-related", ), re_path( - r"^entries/(?P[^/.]+)/relationships/(?P\w+)$", + r"^entries/(?P[^/.]+)/relationships/(?P[\-\w]+)$", EntryRelationshipView.as_view(), name="entry-relationships", ), re_path( - r"^blogs/(?P[^/.]+)/relationships/(?P\w+)$", + r"^blogs/(?P[^/.]+)/relationships/(?P[^/.]+)$", BlogRelationshipView.as_view(), name="blog-relationships", ), diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index de1c2f25..84ec509e 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -157,7 +157,7 @@ def get_related_serializer_class(self): parent_serializer_class = self.get_serializer_class() if "related_field" in self.kwargs: - field_name = self.kwargs["related_field"] + field_name = self.get_related_field_name() # Try get the class from related_serializers if hasattr(parent_serializer_class, "related_serializers"): @@ -402,6 +402,8 @@ def get_related_instance(self): def get_related_field_name(self): field_name = self.kwargs["related_field"] + field_name = undo_format_link_segment(field_name) + if field_name in self.field_name_mapping: return self.field_name_mapping[field_name] return field_name