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 9fa2e23

Browse files
slivercmblayman
authored andcommitted
Allow type field on none polymorphic serializers (#376)
1 parent a953a03 commit 9fa2e23

File tree

9 files changed

+136
-8
lines changed

9 files changed

+136
-8
lines changed

‎example/factories.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
ArtProject,
88
Author,
99
AuthorBio,
10+
AuthorType,
1011
Blog,
1112
Comment,
1213
Company,
@@ -26,6 +27,13 @@ class Meta:
2627
name = factory.LazyAttribute(lambda x: faker.name())
2728

2829

30+
class AuthorTypeFactory(factory.django.DjangoModelFactory):
31+
class Meta:
32+
model = AuthorType
33+
34+
name = factory.LazyAttribute(lambda x: faker.name())
35+
36+
2937
class AuthorFactory(factory.django.DjangoModelFactory):
3038
class Meta:
3139
model = Author
@@ -34,6 +42,7 @@ class Meta:
3442
email = factory.LazyAttribute(lambda x: faker.email())
3543

3644
bio = factory.RelatedFactory('example.factories.AuthorBioFactory', 'author')
45+
type = factory.SubFactory(AuthorTypeFactory)
3746

3847

3948
class AuthorBioFactory(factory.django.DjangoModelFactory):
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.6 on 2017年10月11日 06:31
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('example', '0003_polymorphics'),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name='AuthorType',
18+
fields=[
19+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20+
('created_at', models.DateTimeField(auto_now_add=True)),
21+
('modified_at', models.DateTimeField(auto_now=True)),
22+
('name', models.CharField(max_length=50)),
23+
],
24+
options={
25+
'ordering': ('id',),
26+
},
27+
),
28+
migrations.AlterModelOptions(
29+
name='author',
30+
options={'ordering': ('id',)},
31+
),
32+
migrations.AlterModelOptions(
33+
name='authorbio',
34+
options={'ordering': ('id',)},
35+
),
36+
migrations.AlterModelOptions(
37+
name='blog',
38+
options={'ordering': ('id',)},
39+
),
40+
migrations.AlterModelOptions(
41+
name='comment',
42+
options={'ordering': ('id',)},
43+
),
44+
migrations.AlterModelOptions(
45+
name='entry',
46+
options={'ordering': ('id',)},
47+
),
48+
migrations.AlterModelOptions(
49+
name='taggeditem',
50+
options={'ordering': ('id',)},
51+
),
52+
migrations.AlterField(
53+
model_name='entry',
54+
name='authors',
55+
field=models.ManyToManyField(related_name='entries', to='example.Author'),
56+
),
57+
migrations.AddField(
58+
model_name='author',
59+
name='type',
60+
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='example.AuthorType'),
61+
),
62+
]

‎example/models.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,22 @@ class Meta:
4545
ordering = ('id',)
4646

4747

48+
@python_2_unicode_compatible
49+
class AuthorType(BaseModel):
50+
name = models.CharField(max_length=50)
51+
52+
def __str__(self):
53+
return self.name
54+
55+
class Meta:
56+
ordering = ('id',)
57+
58+
4859
@python_2_unicode_compatible
4960
class Author(BaseModel):
5061
name = models.CharField(max_length=50)
5162
email = models.EmailField()
63+
type = models.ForeignKey(AuthorType, null=True)
5264

5365
def __str__(self):
5466
return self.name

‎example/serializers.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
ArtProject,
1010
Author,
1111
AuthorBio,
12+
AuthorType,
1213
Blog,
1314
Comment,
1415
Company,
@@ -101,6 +102,12 @@ class JSONAPIMeta:
101102
included_resources = ['comments']
102103

103104

105+
class AuthorTypeSerializer(serializers.ModelSerializer):
106+
class Meta:
107+
model = AuthorType
108+
fields = ('name', )
109+
110+
104111
class AuthorBioSerializer(serializers.ModelSerializer):
105112
class Meta:
106113
model = AuthorBio
@@ -109,12 +116,13 @@ class Meta:
109116

110117
class AuthorSerializer(serializers.ModelSerializer):
111118
included_serializers = {
112-
'bio': AuthorBioSerializer
119+
'bio': AuthorBioSerializer,
120+
'type': AuthorTypeSerializer
113121
}
114122

115123
class Meta:
116124
model = Author
117-
fields = ('name', 'email', 'bio', 'entries')
125+
fields = ('name', 'email', 'bio', 'entries', 'type')
118126

119127

120128
class WriterSerializer(serializers.ModelSerializer):

‎example/tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
ArtProjectFactory,
66
AuthorBioFactory,
77
AuthorFactory,
8+
AuthorTypeFactory,
89
BlogFactory,
910
CommentFactory,
1011
CompanyFactory,
@@ -16,6 +17,7 @@
1617
register(BlogFactory)
1718
register(AuthorFactory)
1819
register(AuthorBioFactory)
20+
register(AuthorTypeFactory)
1921
register(EntryFactory)
2022
register(CommentFactory)
2123
register(TaggedItemFactory)

‎example/tests/test_model_viewsets.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import pytest
12
from django.conf import settings
23
from django.contrib.auth import get_user_model
34
from django.core.urlresolvers import reverse
@@ -226,3 +227,31 @@ def test_key_in_post(self):
226227
self.assertEqual(
227228
get_user_model().objects.get(pk=self.miles.pk).email,
228229
'miles@trumpet.org')
230+
231+
232+
@pytest.mark.django_db
233+
def test_patch_allow_field_type(author, author_type_factory, client):
234+
"""
235+
Verify that type field may be updated.
236+
"""
237+
author_type = author_type_factory()
238+
url = reverse('author-detail', args=[author.id])
239+
240+
data = {
241+
'data': {
242+
'id': author.id,
243+
'type': 'authors',
244+
'relationships': {
245+
'data': {
246+
'id': author_type.id,
247+
'type': 'author-type'
248+
}
249+
}
250+
}
251+
}
252+
253+
response = client.patch(url,
254+
content_type='application/vnd.api+json',
255+
data=dump_json(data))
256+
257+
assert response.status_code == 200

‎example/tests/test_performance.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ def test_query_count_include_author(self):
5050
1. Primary resource COUNT query
5151
2. Primary resource SELECT
5252
3. Authors prefetched
53-
3. Entries prefetched
53+
4. Author types prefetched
54+
5. Entries prefetched
5455
"""
55-
with self.assertNumQueries(4):
56+
with self.assertNumQueries(5):
5657
response = self.client.get('/comments?include=author&page_size=25')
5758
self.assertEqual(len(response.data['results']), 25)

‎example/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class CommentViewSet(ModelViewSet):
7878
serializer_class = CommentSerializer
7979
prefetch_for_includes = {
8080
'__all__': [],
81-
'author': ['author', 'author__bio', 'author__entries'],
81+
'author': ['author', 'author__bio', 'author__entries', 'author__type'],
8282
'entry': ['author', 'author__bio', 'author__entries']
8383
}
8484

‎rest_framework_json_api/parsers.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from rest_framework import parsers
77
from rest_framework.exceptions import ParseError
88

9-
from . import exceptions, renderers, utils
9+
from . import exceptions, renderers, serializers, utils
1010

1111

1212
class JSONParser(parsers.JSONParser):
@@ -83,9 +83,10 @@ def parse(self, stream, media_type=None, parser_context=None):
8383
raise ParseError('Received document does not contain primary data')
8484

8585
data = result.get('data')
86+
view = parser_context['view']
8687

8788
from rest_framework_json_api.views import RelationshipView
88-
if isinstance(parser_context['view'], RelationshipView):
89+
if isinstance(view, RelationshipView):
8990
# We skip parsing the object as JSONAPI Resource Identifier Object and not a regular
9091
# Resource Object
9192
if isinstance(data, list):
@@ -129,8 +130,12 @@ def parse(self, stream, media_type=None, parser_context=None):
129130
raise ParseError("The resource identifier object must contain an 'id' member")
130131

131132
# Construct the return data
133+
serializer_class = getattr(view, 'serializer_class', None)
132134
parsed_data = {'id': data.get('id')} if 'id' in data else {}
133-
parsed_data['type'] = data.get('type')
135+
# `type` field needs to be allowed in none polymorphic serializers
136+
if serializer_class is not None:
137+
if issubclass(serializer_class, serializers.PolymorphicModelSerializer):
138+
parsed_data['type'] = data.get('type')
134139
parsed_data.update(self.parse_attributes(data))
135140
parsed_data.update(self.parse_relationships(data))
136141
parsed_data.update(self.parse_metadata(result))

0 commit comments

Comments
(0)

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