Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 53469f3

Browse files
authored
Re-enabled overwriting of URL field (#1237)
Enabled overwriting of URL field URL_FIELD_NAME is usually used as self-link in links. However it should be allowed to be overwritten as long as not HyperlinkedIdentifyField has been used.
1 parent 3f1ea67 commit 53469f3

File tree

8 files changed

+89
-8
lines changed

8 files changed

+89
-8
lines changed

‎CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
Note that in line with [Django REST framework policy](https://www.django-rest-framework.org/topics/release-notes/),
99
any parts of the framework not mentioned in the documentation should generally be considered private API, and may be subject to change.
1010

11+
## [Unreleased]
12+
13+
### Fixed
14+
15+
* Re-enabled overwriting of url field (regression since 7.0.0)
16+
1117
## [7.0.1] - 2024年06月06日
1218

1319
### Added

‎rest_framework_json_api/renderers.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,11 @@ def extract_attributes(cls, fields, resource):
7575
and relationships are not returned.
7676
"""
7777

78-
invalid_fields = {"id", api_settings.URL_FIELD_NAME}
79-
8078
return {
8179
format_field_name(field_name): value
8280
for field_name, value in resource.items()
8381
if field_name in fields
84-
and field_name notininvalid_fields
82+
and field_name !="id"
8583
and not is_relationship_field(fields[field_name])
8684
}
8785

@@ -449,7 +447,10 @@ def _filter_sparse_fields(cls, serializer, fields, resource_name):
449447
if field.field_name in sparse_fields
450448
# URL field is not considered a field in JSON:API spec
451449
# but a link so need to keep it
452-
or field.field_name == api_settings.URL_FIELD_NAME
450+
or (
451+
field.field_name == api_settings.URL_FIELD_NAME
452+
and isinstance(field, relations.HyperlinkedIdentityField)
453+
)
453454
}
454455

455456
return fields
@@ -486,7 +487,7 @@ def build_json_resource_obj(
486487
resource_data["relationships"] = relationships
487488
# Add 'self' link if field is present and valid
488489
if api_settings.URL_FIELD_NAME in resource and isinstance(
489-
fields[api_settings.URL_FIELD_NAME], relations.RelatedField
490+
fields[api_settings.URL_FIELD_NAME], relations.HyperlinkedIdentityField
490491
):
491492
resource_data["links"] = {"self": resource[api_settings.URL_FIELD_NAME]}
492493

‎rest_framework_json_api/serializers.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.utils.module_loading import import_string as import_class_from_dotted_path
77
from django.utils.translation import gettext_lazy as _
88
from rest_framework.exceptions import ParseError
9+
from rest_framework.relations import HyperlinkedIdentityField
910

1011
# star import defined so `rest_framework_json_api.serializers` can be
1112
# a simple drop in for `rest_framework.serializers`
@@ -94,9 +95,12 @@ def _readable_fields(self):
9495
field
9596
for field in readable_fields
9697
if field.field_name in sparse_fields
97-
# URL field is not considered a field in JSON:API spec
98-
# but a link so need to keep it
99-
or field.field_name == api_settings.URL_FIELD_NAME
98+
# URL_FIELD_NAME is the field used as self-link to resource
99+
# however only when it is a HyperlinkedIdentityField
100+
or (
101+
field.field_name == api_settings.URL_FIELD_NAME
102+
and isinstance(field, HyperlinkedIdentityField)
103+
)
100104
# ID is a required field which might have been overwritten
101105
# so need to keep it
102106
or field.field_name == "id"

‎tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ManyToManySource,
99
ManyToManyTarget,
1010
NestedRelatedSource,
11+
URLModel,
1112
)
1213

1314

@@ -36,6 +37,11 @@ def model(db):
3637
return BasicModel.objects.create(text="Model")
3738

3839

40+
@pytest.fixture
41+
def url_instance(db):
42+
return URLModel.objects.create(text="Url", url="https://example.com")
43+
44+
3945
@pytest.fixture
4046
def foreign_key_target(db):
4147
return ForeignKeyTarget.objects.create(name="Target")

‎tests/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ class Meta:
1818
ordering = ("id",)
1919

2020

21+
class URLModel(DJAModel):
22+
url = models.URLField()
23+
text = models.CharField(max_length=100)
24+
25+
class Meta:
26+
ordering = ("id",)
27+
28+
2129
# Models for relations tests
2230
# ManyToMany
2331
class ManyToManyTarget(DJAModel):

‎tests/serializers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ManyToManySource,
99
ManyToManyTarget,
1010
NestedRelatedSource,
11+
URLModel,
1112
)
1213

1314

@@ -17,6 +18,15 @@ class Meta:
1718
model = BasicModel
1819

1920

21+
class URLModelSerializer(serializers.ModelSerializer):
22+
class Meta:
23+
fields = (
24+
"text",
25+
"url",
26+
)
27+
model = URLModel
28+
29+
2030
class ForeignKeyTargetSerializer(serializers.ModelSerializer):
2131
class Meta:
2232
fields = ("name",)

‎tests/test_views.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
ForeignKeyTargetViewSet,
2222
ManyToManySourceViewSet,
2323
NestedRelatedSourceViewSet,
24+
URLModelViewSet,
2425
)
2526

2627

@@ -182,6 +183,42 @@ def test_list_with_default_included_resources(self, client, foreign_key_source):
182183
}
183184
] == result["included"]
184185

186+
@pytest.mark.urls(__name__)
187+
def test_list_allow_overwriting_url_field(self, client, url_instance):
188+
"""
189+
Test overwriting of url is possible.
190+
191+
URL_FIELD_NAME which is set to 'url' per default is used as self in links.
192+
However if field is overwritten and not a HyperlinkedIdentityField it should be allowed
193+
to use as a attribute as well.
194+
"""
195+
196+
url = reverse("urlmodel-list")
197+
response = client.get(url)
198+
assert response.status_code == status.HTTP_200_OK
199+
data = response.json()["data"]
200+
assert data == [
201+
{
202+
"type": "URLModel",
203+
"id": str(url_instance.pk),
204+
"attributes": {"text": "Url", "url": "https://example.com"},
205+
}
206+
]
207+
208+
@pytest.mark.urls(__name__)
209+
def test_list_allow_overwiritng_url_with_sparse_fields(self, client, url_instance):
210+
url = reverse("urlmodel-list")
211+
response = client.get(url, data={"fields[URLModel]": "text"})
212+
assert response.status_code == status.HTTP_200_OK
213+
data = response.json()["data"]
214+
assert data == [
215+
{
216+
"type": "URLModel",
217+
"id": str(url_instance.pk),
218+
"attributes": {"text": "Url"},
219+
}
220+
]
221+
185222
@pytest.mark.urls(__name__)
186223
def test_retrieve(self, client, model):
187224
url = reverse("basic-model-detail", kwargs={"pk": model.pk})
@@ -495,6 +532,7 @@ def patch(self, request, *args, **kwargs):
495532
# configuration in general
496533
router = SimpleRouter()
497534
router.register(r"basic_models", BasicModelViewSet, basename="basic-model")
535+
router.register(r"url_models", URLModelViewSet)
498536
router.register(r"foreign_key_sources", ForeignKeySourceViewSet)
499537
router.register(r"foreign_key_targets", ForeignKeyTargetViewSet)
500538
router.register(

‎tests/views.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
ForeignKeyTarget,
66
ManyToManySource,
77
NestedRelatedSource,
8+
URLModel,
89
)
910
from tests.serializers import (
1011
BasicModelSerializer,
@@ -13,6 +14,7 @@
1314
ForeignKeyTargetSerializer,
1415
ManyToManySourceSerializer,
1516
NestedRelatedSourceSerializer,
17+
URLModelSerializer,
1618
)
1719

1820

@@ -22,6 +24,12 @@ class BasicModelViewSet(ModelViewSet):
2224
ordering = ["text"]
2325

2426

27+
class URLModelViewSet(ModelViewSet):
28+
serializer_class = URLModelSerializer
29+
queryset = URLModel.objects.all()
30+
ordering = ["url"]
31+
32+
2533
class ForeignKeySourceViewSet(ModelViewSet):
2634
serializer_class = ForeignKeySourceSerializer
2735
queryset = ForeignKeySource.objects.all()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /