From 2cc9ec00a2474967d264a3b51aacf71319f50309 Mon Sep 17 00:00:00 2001 From: santiavenda2 Date: Fri, 28 Dec 2018 10:55:14 -0300 Subject: [PATCH 1/4] Fix AutoPrefetchMixin for ReverseOneToOneDescriptor --- example/migrations/0006_auto_20181228_0752.py | 32 +++++++++++++++++++ example/models.py | 15 +++++++++ example/serializers.py | 14 ++++++-- example/tests/integration/test_includes.py | 12 +++++++ rest_framework_json_api/views.py | 6 +++- 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 example/migrations/0006_auto_20181228_0752.py diff --git a/example/migrations/0006_auto_20181228_0752.py b/example/migrations/0006_auto_20181228_0752.py new file mode 100644 index 00000000..2cfb0c29 --- /dev/null +++ b/example/migrations/0006_auto_20181228_0752.py @@ -0,0 +1,32 @@ +# Generated by Django 2.1.4 on 2018-12-28 07:52 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('example', '0005_auto_20180922_1508'), + ] + + operations = [ + migrations.CreateModel( + name='AuthorBioMetadata', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('modified_at', models.DateTimeField(auto_now=True)), + ('body', models.TextField()), + ('bio', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='metadata', to='example.AuthorBio')), + ], + options={ + 'ordering': ('id',), + }, + ), + migrations.AlterField( + model_name='comment', + name='author', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='example.Author'), + ), + ] diff --git a/example/models.py b/example/models.py index e280c827..179cbfe5 100644 --- a/example/models.py +++ b/example/models.py @@ -81,6 +81,21 @@ class Meta: ordering = ('id',) +@python_2_unicode_compatible +class AuthorBioMetadata(BaseModel): + """ + Just a class to have a relation with author bio + """ + bio = models.OneToOneField(AuthorBio, related_name='metadata', on_delete=models.CASCADE) + body = models.TextField() + + def __str__(self): + return self.bio.author.name + + class Meta: + ordering = ('id',) + + @python_2_unicode_compatible class Entry(BaseModel): blog = models.ForeignKey(Blog, on_delete=models.CASCADE) diff --git a/example/serializers.py b/example/serializers.py index 70699987..955a5a60 100644 --- a/example/serializers.py +++ b/example/serializers.py @@ -16,8 +16,8 @@ Project, ProjectType, ResearchProject, - TaggedItem -) + TaggedItem, + AuthorBioMetadata) class TaggedItemSerializer(serializers.ModelSerializer): @@ -199,6 +199,16 @@ class Meta: model = AuthorBio fields = ('author', 'body') + included_serializers = { + 'metadata': 'example.serializers.AuthorBioMetadataSerializer', + } + + +class AuthorBioMetadataSerializer(serializers.ModelSerializer): + class Meta: + model = AuthorBioMetadata + fields = ('body',) + class AuthorSerializer(serializers.ModelSerializer): bio = relations.ResourceRelatedField( diff --git a/example/tests/integration/test_includes.py b/example/tests/integration/test_includes.py index 694e20d1..698049b8 100644 --- a/example/tests/integration/test_includes.py +++ b/example/tests/integration/test_includes.py @@ -20,6 +20,18 @@ def test_included_data_on_list(multiple_entries, client): assert comment_count == expected_comment_count, 'List comment count is incorrect' +def test_included_data_on_list_with_one_to_one_relations(multiple_entries, client): + response = client.get(reverse("entry-list"), data={'include': 'authors.bio.metadata', 'page[size]': 5}) + included = response.json().get('included') + + assert len(response.json()['data']) == len(multiple_entries), ( + 'Incorrect entry count' + ) + assert [x.get('type') for x in included] == ['authorBios', 'authorBios', 'authors', 'authors'], ( + 'List included types are incorrect' + ) + + def test_default_included_data_on_detail(single_entry, client): return test_included_data_on_detail(single_entry=single_entry, client=client, query='') diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index 46f3a2bc..b692844b 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -92,7 +92,11 @@ def get_queryset(self, *args, **kwargs): if level == levels[-1]: included_model = field else: - model_field = field.field + + if issubclass(field_class, ReverseOneToOneDescriptor): + model_field = field.related.field + else: + model_field = field.field if is_forward_relation: level_model = model_field.related_model From 395056672a7ba2dcf76ab2f283474cbe393cf128 Mon Sep 17 00:00:00 2001 From: santiavenda2 Date: Fri, 28 Dec 2018 11:00:44 -0300 Subject: [PATCH 2/4] Updata changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4072276c..2b300d01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ any parts of the framework not mentioned in the documentation should generally b * Do not render `write_only` relations * Do not skip empty one-to-one relationships * Allow `HyperlinkRelatedField` to be used with [related urls](https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html?highlight=related%20links#related-urls) +* Fix AutoPrefetchMixin when includes have a one to one relation ## [2.6.0] - 2018-09-20 From d8ce701a6a659c7c8bc83665c75b5425563e2762 Mon Sep 17 00:00:00 2001 From: santiavenda2 Date: Fri, 28 Dec 2018 11:11:06 -0300 Subject: [PATCH 3/4] Fix line lenght --- example/tests/integration/test_includes.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/example/tests/integration/test_includes.py b/example/tests/integration/test_includes.py index 698049b8..54556de3 100644 --- a/example/tests/integration/test_includes.py +++ b/example/tests/integration/test_includes.py @@ -21,13 +21,16 @@ def test_included_data_on_list(multiple_entries, client): def test_included_data_on_list_with_one_to_one_relations(multiple_entries, client): - response = client.get(reverse("entry-list"), data={'include': 'authors.bio.metadata', 'page[size]': 5}) + response = client.get(reverse("entry-list"), + data={'include': 'authors.bio.metadata', 'page[size]': 5}) included = response.json().get('included') assert len(response.json()['data']) == len(multiple_entries), ( 'Incorrect entry count' ) - assert [x.get('type') for x in included] == ['authorBios', 'authorBios', 'authors', 'authors'], ( + expected_include_types = ['authorBios', 'authorBios', 'authors', 'authors'] + include_types = [x.get('type') for x in included] + assert include_types == expected_include_types, ( 'List included types are incorrect' ) From 14099540d23c29b831264754d74892300ffd558b Mon Sep 17 00:00:00 2001 From: Oliver Sauder Date: Fri, 4 Jan 2019 09:03:12 +0100 Subject: [PATCH 4/4] Clarify changelog and adjust fix id in test --- CHANGELOG.md | 2 +- example/tests/test_views.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc4dcdd5..4e4bc23c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ any parts of the framework not mentioned in the documentation should generally b * Do not skip empty one-to-one relationships * Allow `HyperlinkRelatedField` to be used with [related urls](https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html?highlight=related%20links#related-urls) * Fixed hardcoded year 2018 in tests ([#539](https://github.com/django-json-api/django-rest-framework-json-api/issues/539)) -* Fix AutoPrefetchMixin when includes have a one to one relation ([#537](https://github.com/django-json-api/django-rest-framework-json-api/issues/537)) +* Avoid exception in `AutoPrefetchMixin` when including a reverse one to one relation ([#537](https://github.com/django-json-api/django-rest-framework-json-api/issues/537)) ## [2.6.0] - 2018-09-20 diff --git a/example/tests/test_views.py b/example/tests/test_views.py index 85b706c1..95fc92a3 100644 --- a/example/tests/test_views.py +++ b/example/tests/test_views.py @@ -1,6 +1,6 @@ import json - from datetime import datetime + from django.test import RequestFactory from django.utils import timezone from rest_framework.exceptions import NotFound @@ -338,7 +338,7 @@ def test_retrieve_related_single_reverse_lookup(self): 'type': 'authorBios', 'id': str(self.author.bio.id), 'relationships': { 'author': {'data': {'type': 'authors', 'id': str(self.author.id)}}, - 'metadata': {'data': {'id': '1', + 'metadata': {'data': {'id': str(self.author.bio.metadata.id), 'type': 'authorBioMetadata'}} }, 'attributes': {