diff --git a/example/factories/__init__.py b/example/factories/__init__.py index d77d42c4..129ddf98 100644 --- a/example/factories/__init__.py +++ b/example/factories/__init__.py @@ -3,7 +3,7 @@ import factory from faker import Factory as FakerFactory -from example.models import Blog, Author, Entry, Comment +from example.models import Blog, Author, AuthorBio, Entry, Comment faker = FakerFactory.create() faker.seed(983843) @@ -23,6 +23,14 @@ class Meta: email = factory.LazyAttribute(lambda x: faker.email()) +class AuthorBioFactory(factory.django.DjangoModelFactory): + class Meta: + model = AuthorBio + + author = factory.SubFactory(AuthorFactory) + body = factory.LazyAttribute(lambda x: faker.text()) + + class EntryFactory(factory.django.DjangoModelFactory): class Meta: model = Entry diff --git a/example/models.py b/example/models.py index 64291393..7895722a 100644 --- a/example/models.py +++ b/example/models.py @@ -34,6 +34,15 @@ def __str__(self): return self.name +@python_2_unicode_compatible +class AuthorBio(BaseModel): + author = models.OneToOneField(Author, related_name='bio') + body = models.TextField() + + def __str__(self): + return self.author.name + + @python_2_unicode_compatible class Entry(BaseModel): blog = models.ForeignKey(Blog) diff --git a/example/serializers.py b/example/serializers.py index c6b243a1..398bd71c 100644 --- a/example/serializers.py +++ b/example/serializers.py @@ -1,5 +1,5 @@ from rest_framework_json_api import serializers, relations -from example.models import Blog, Entry, Author, Comment +from example.models import Blog, Entry, Author, AuthorBio, Comment class BlogSerializer(serializers.ModelSerializer): @@ -38,11 +38,21 @@ class Meta: 'authors', 'comments', 'suggested',) +class AuthorBioSerializer(serializers.ModelSerializer): + + class Meta: + model = AuthorBio + fields = ('author', 'body',) + + class AuthorSerializer(serializers.ModelSerializer): + included_serializers = { + 'bio': AuthorBioSerializer + } class Meta: model = Author - fields = ('name', 'email',) + fields = ('name', 'email', 'bio') class CommentSerializer(serializers.ModelSerializer): diff --git a/example/tests/conftest.py b/example/tests/conftest.py index 517b502b..8a96cfdb 100644 --- a/example/tests/conftest.py +++ b/example/tests/conftest.py @@ -1,10 +1,11 @@ import pytest from pytest_factoryboy import register -from example.factories import BlogFactory, AuthorFactory, EntryFactory, CommentFactory +from example.factories import BlogFactory, AuthorFactory, AuthorBioFactory, EntryFactory, CommentFactory register(BlogFactory) register(AuthorFactory) +register(AuthorBioFactory) register(EntryFactory) register(CommentFactory) diff --git a/example/tests/integration/test_includes.py b/example/tests/integration/test_includes.py index 3643a25b..4e8c79ce 100644 --- a/example/tests/integration/test_includes.py +++ b/example/tests/integration/test_includes.py @@ -36,3 +36,17 @@ def test_dynamic_related_data_is_included(single_entry, entry_factory, client): assert [x.get('type') for x in included] == ['entries'], 'Dynamic included types are incorrect' assert len(included) == 1, 'The dynamically included blog entries are of an incorrect count' + +def test_missing_field_not_included(author_bio_factory, author_factory, client): + # First author does not have a bio + author = author_factory() + response = client.get(reverse('author-detail', args=[author.pk])+'?include=bio') + data = load_json(response.content) + assert 'included' not in data + # Second author does + bio = author_bio_factory() + response = client.get(reverse('author-detail', args=[bio.author.pk])+'?include=bio') + data = load_json(response.content) + assert 'included' in data + assert len(data['included']) == 1 + assert data['included'][0]['attributes']['body'] == bio.body diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py index 23833bc7..566329a5 100644 --- a/rest_framework_json_api/renderers.py +++ b/rest_framework_json_api/renderers.py @@ -257,6 +257,8 @@ def extract_included(fields, resource, resource_instance, included_resources): # For ManyRelatedFields if `related_name` is not set we need to access `foo_set` from `source` relation_instance_or_manager = getattr(resource_instance, field.child_relation.source) except AttributeError: + if not hasattr(current_serializer, field.source): + continue serializer_method = getattr(current_serializer, field.source) relation_instance_or_manager = serializer_method(resource_instance)