diff --git a/example/factories.py b/example/factories.py index 012fb85e..563ba30b 100644 --- a/example/factories.py +++ b/example/factories.py @@ -7,6 +7,7 @@ ArtProject, Author, AuthorBio, + AuthorType, Blog, Comment, Company, @@ -26,6 +27,13 @@ class Meta: name = factory.LazyAttribute(lambda x: faker.name()) +class AuthorTypeFactory(factory.django.DjangoModelFactory): + class Meta: + model = AuthorType + + name = factory.LazyAttribute(lambda x: faker.name()) + + class AuthorFactory(factory.django.DjangoModelFactory): class Meta: model = Author @@ -34,6 +42,7 @@ class Meta: email = factory.LazyAttribute(lambda x: faker.email()) bio = factory.RelatedFactory('example.factories.AuthorBioFactory', 'author') + type = factory.SubFactory(AuthorTypeFactory) class AuthorBioFactory(factory.django.DjangoModelFactory): diff --git a/example/migrations/0004_auto_20171011_0631.py b/example/migrations/0004_auto_20171011_0631.py new file mode 100644 index 00000000..96df2aa7 --- /dev/null +++ b/example/migrations/0004_auto_20171011_0631.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-10-11 06:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('example', '0003_polymorphics'), + ] + + operations = [ + migrations.CreateModel( + name='AuthorType', + 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)), + ('name', models.CharField(max_length=50)), + ], + options={ + 'ordering': ('id',), + }, + ), + migrations.AlterModelOptions( + name='author', + options={'ordering': ('id',)}, + ), + migrations.AlterModelOptions( + name='authorbio', + options={'ordering': ('id',)}, + ), + migrations.AlterModelOptions( + name='blog', + options={'ordering': ('id',)}, + ), + migrations.AlterModelOptions( + name='comment', + options={'ordering': ('id',)}, + ), + migrations.AlterModelOptions( + name='entry', + options={'ordering': ('id',)}, + ), + migrations.AlterModelOptions( + name='taggeditem', + options={'ordering': ('id',)}, + ), + migrations.AlterField( + model_name='entry', + name='authors', + field=models.ManyToManyField(related_name='entries', to='example.Author'), + ), + migrations.AddField( + model_name='author', + name='type', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='example.AuthorType'), + ), + ] diff --git a/example/models.py b/example/models.py index 5cc70b47..5395607f 100644 --- a/example/models.py +++ b/example/models.py @@ -45,10 +45,22 @@ class Meta: ordering = ('id',) +@python_2_unicode_compatible +class AuthorType(BaseModel): + name = models.CharField(max_length=50) + + def __str__(self): + return self.name + + class Meta: + ordering = ('id',) + + @python_2_unicode_compatible class Author(BaseModel): name = models.CharField(max_length=50) email = models.EmailField() + type = models.ForeignKey(AuthorType, null=True) def __str__(self): return self.name diff --git a/example/serializers.py b/example/serializers.py index accd345c..da491e7f 100644 --- a/example/serializers.py +++ b/example/serializers.py @@ -9,6 +9,7 @@ ArtProject, Author, AuthorBio, + AuthorType, Blog, Comment, Company, @@ -101,6 +102,12 @@ class JSONAPIMeta: included_resources = ['comments'] +class AuthorTypeSerializer(serializers.ModelSerializer): + class Meta: + model = AuthorType + fields = ('name', ) + + class AuthorBioSerializer(serializers.ModelSerializer): class Meta: model = AuthorBio @@ -109,12 +116,13 @@ class Meta: class AuthorSerializer(serializers.ModelSerializer): included_serializers = { - 'bio': AuthorBioSerializer + 'bio': AuthorBioSerializer, + 'type': AuthorTypeSerializer } class Meta: model = Author - fields = ('name', 'email', 'bio', 'entries') + fields = ('name', 'email', 'bio', 'entries', 'type') class WriterSerializer(serializers.ModelSerializer): diff --git a/example/tests/conftest.py b/example/tests/conftest.py index 47879630..84de7732 100644 --- a/example/tests/conftest.py +++ b/example/tests/conftest.py @@ -5,6 +5,7 @@ ArtProjectFactory, AuthorBioFactory, AuthorFactory, + AuthorTypeFactory, BlogFactory, CommentFactory, CompanyFactory, @@ -16,6 +17,7 @@ register(BlogFactory) register(AuthorFactory) register(AuthorBioFactory) +register(AuthorTypeFactory) register(EntryFactory) register(CommentFactory) register(TaggedItemFactory) diff --git a/example/tests/test_model_viewsets.py b/example/tests/test_model_viewsets.py index a3835146..36949fe1 100644 --- a/example/tests/test_model_viewsets.py +++ b/example/tests/test_model_viewsets.py @@ -1,3 +1,4 @@ +import pytest from django.conf import settings from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse @@ -226,3 +227,31 @@ def test_key_in_post(self): self.assertEqual( get_user_model().objects.get(pk=self.miles.pk).email, 'miles@trumpet.org') + + +@pytest.mark.django_db +def test_patch_allow_field_type(author, author_type_factory, client): + """ + Verify that type field may be updated. + """ + author_type = author_type_factory() + url = reverse('author-detail', args=[author.id]) + + data = { + 'data': { + 'id': author.id, + 'type': 'authors', + 'relationships': { + 'data': { + 'id': author_type.id, + 'type': 'author-type' + } + } + } + } + + response = client.patch(url, + content_type='application/vnd.api+json', + data=dump_json(data)) + + assert response.status_code == 200 diff --git a/example/tests/test_performance.py b/example/tests/test_performance.py index 3ec2f676..d9ae1db9 100644 --- a/example/tests/test_performance.py +++ b/example/tests/test_performance.py @@ -50,8 +50,9 @@ def test_query_count_include_author(self): 1. Primary resource COUNT query 2. Primary resource SELECT 3. Authors prefetched - 3. Entries prefetched + 4. Author types prefetched + 5. Entries prefetched """ - with self.assertNumQueries(4): + with self.assertNumQueries(5): response = self.client.get('/comments?include=author&page_size=25') self.assertEqual(len(response.data['results']), 25) diff --git a/example/views.py b/example/views.py index 0e1f8bb4..a08bbf61 100644 --- a/example/views.py +++ b/example/views.py @@ -78,7 +78,7 @@ class CommentViewSet(ModelViewSet): serializer_class = CommentSerializer prefetch_for_includes = { '__all__': [], - 'author': ['author', 'author__bio', 'author__entries'], + 'author': ['author', 'author__bio', 'author__entries', 'author__type'], 'entry': ['author', 'author__bio', 'author__entries'] } diff --git a/rest_framework_json_api/parsers.py b/rest_framework_json_api/parsers.py index 753c381b..86a61e74 100644 --- a/rest_framework_json_api/parsers.py +++ b/rest_framework_json_api/parsers.py @@ -6,7 +6,7 @@ from rest_framework import parsers from rest_framework.exceptions import ParseError -from . import exceptions, renderers, utils +from . import exceptions, renderers, serializers, utils class JSONParser(parsers.JSONParser): @@ -83,9 +83,10 @@ def parse(self, stream, media_type=None, parser_context=None): raise ParseError('Received document does not contain primary data') data = result.get('data') + view = parser_context['view'] from rest_framework_json_api.views import RelationshipView - if isinstance(parser_context['view'], RelationshipView): + if isinstance(view, RelationshipView): # We skip parsing the object as JSONAPI Resource Identifier Object and not a regular # Resource Object if isinstance(data, list): @@ -129,8 +130,12 @@ def parse(self, stream, media_type=None, parser_context=None): raise ParseError("The resource identifier object must contain an 'id' member") # Construct the return data + serializer_class = getattr(view, 'serializer_class', None) parsed_data = {'id': data.get('id')} if 'id' in data else {} - parsed_data['type'] = data.get('type') + # `type` field needs to be allowed in none polymorphic serializers + if serializer_class is not None: + if issubclass(serializer_class, serializers.PolymorphicModelSerializer): + parsed_data['type'] = data.get('type') parsed_data.update(self.parse_attributes(data)) parsed_data.update(self.parse_relationships(data)) parsed_data.update(self.parse_metadata(result)) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy