-
Notifications
You must be signed in to change notification settings - Fork 300
Support polymorphic models #211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 25 commits
dd4d4ec
8c73d95
681b5aa
96cbab0
5c63425
fddb06b
22829d1
d565334
e840438
19b0238
0ddf5ca
b8bf612
8fd4617
275793c
a26df13
2278976
8563b65
4aaeac2
030f6c8
ca23885
ae759e5
37c5ae6
f36821b
4eec4aa
bc12e0f
6b4f45b
36f3b6a
05cdb51
c1afe35
8ff5465
35c90d4
c5599c0
8d94efb
89ad607
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,8 @@ pip-delete-this-directory.txt | |
|
||
# Tox | ||
.tox/ | ||
.cache/ | ||
.python-version | ||
|
||
# VirtualEnv | ||
.venv/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -425,6 +425,50 @@ field_name_mapping = { | |
``` | ||
|
||
|
||
### Working with polymorphic resources | ||
|
||
#### Writing polymorphic resources | ||
|
||
A polymorphic endpoint can be setup if associated with a polymorphic serializer. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "setup" (a noun) should be "set up" (a verb phrase) here. As a native English speaker, I apologize for the stupidity and craziness of the English language. 😄 |
||
A polymorphic serializer take care of (de)serializing the correct instances types and can be defined like this: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "take" should be "takes" |
||
|
||
```python | ||
class ProjectSerializer(serializers.PolymorphicModelSerializer): | ||
polymorphic_serializers = [ArtProjectSerializer, ResearchProjectSerializer] | ||
|
||
class Meta: | ||
model = models.Project | ||
``` | ||
|
||
It must inherit from `serializers.PolymorphicModelSerializer` and define the `polymorphic_serializers` list. | ||
This attribute defines the accepted resource types. | ||
|
||
|
||
Polymorphic relations can also be handled with `relations.PolymorphicResourceRelatedField` like this: | ||
|
||
```python | ||
class CompanySerializer(serializers.ModelSerializer): | ||
current_project = relations.PolymorphicResourceRelatedField( | ||
ProjectSerializer, queryset=models.Project.objects.all()) | ||
future_projects = relations.PolymorphicResourceRelatedField( | ||
ProjectSerializer, queryset=models.Project.objects.all(), many=True) | ||
|
||
class Meta: | ||
model = models.Company | ||
``` | ||
|
||
They must be explicitely declared with the `polymorphic_serializer` (first positional argument) correctly defined. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "explicitely" should be "explicitly" |
||
It must be a subclass of `serializers.PolymorphicModelSerializer`. | ||
|
||
<div class="warning"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sigh... the downside of using Markdown for Sphinx, no directive support. Nothing to do here, just lamenting. 😢 |
||
<strong>Note:</strong> | ||
Polymorphic resources are not compatible with | ||
<code class="docutils literal"> | ||
<span class="pre">resource_name</span> | ||
</code> | ||
defined on the view. | ||
</div> | ||
|
||
### Meta | ||
|
||
You may add metadata to the rendered json in two different ways: `meta_fields` and `get_root_meta`. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.11.1 on 2017-05-17 14:49 | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('contenttypes', '0002_remove_content_type_name'), | ||
('example', '0002_taggeditem'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Company', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('name', models.CharField(max_length=100)), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='Project', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('topic', models.CharField(max_length=30)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
migrations.AlterField( | ||
model_name='comment', | ||
name='entry', | ||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='example.Entry'), | ||
), | ||
migrations.CreateModel( | ||
name='ArtProject', | ||
fields=[ | ||
('project_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='example.Project')), | ||
('artist', models.CharField(max_length=30)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
bases=('example.project',), | ||
), | ||
migrations.CreateModel( | ||
name='ResearchProject', | ||
fields=[ | ||
('project_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='example.Project')), | ||
('supervisor', models.CharField(max_length=30)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
bases=('example.project',), | ||
), | ||
migrations.AddField( | ||
model_name='project', | ||
name='polymorphic_ctype', | ||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_example.project_set+', to='contenttypes.ContentType'), | ||
), | ||
migrations.AddField( | ||
model_name='company', | ||
name='current_project', | ||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='companies', to='example.Project'), | ||
), | ||
migrations.AddField( | ||
model_name='company', | ||
name='future_projects', | ||
field=models.ManyToManyField(to='example.Project'), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
from django.contrib.contenttypes.fields import GenericRelation | ||
from django.db import models | ||
from django.utils.encoding import python_2_unicode_compatible | ||
from polymorphic.models import PolymorphicModel | ||
|
||
|
||
class BaseModel(models.Model): | ||
|
@@ -86,3 +87,25 @@ class Comment(BaseModel): | |
|
||
def __str__(self): | ||
return self.body | ||
|
||
|
||
class Project(PolymorphicModel): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice use of the |
||
topic = models.CharField(max_length=30) | ||
|
||
|
||
class ArtProject(Project): | ||
artist = models.CharField(max_length=30) | ||
|
||
|
||
class ResearchProject(Project): | ||
supervisor = models.CharField(max_length=30) | ||
|
||
|
||
@python_2_unicode_compatible | ||
class Company(models.Model): | ||
name = models.CharField(max_length=100) | ||
current_project = models.ForeignKey(Project, related_name='companies') | ||
future_projects = models.ManyToManyField(Project) | ||
|
||
def __str__(self): | ||
return self.name |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,12 @@ | ||
from datetime import datetime | ||
from packaging import version | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, for the record, I've considered After all that code could be dropped as soon as we drop support of DRF 3.1 and 3.2. |
||
|
||
import rest_framework | ||
from rest_framework_json_api import serializers, relations | ||
from example.models import Blog, Entry, Author, AuthorBio, Comment, TaggedItem | ||
from example.models import ( | ||
Blog, Entry, Author, AuthorBio, Comment, TaggedItem, Project, ArtProject, ResearchProject, | ||
Company, | ||
) | ||
|
||
|
||
class TaggedItemSerializer(serializers.ModelSerializer): | ||
|
@@ -115,3 +121,40 @@ class Meta: | |
model = Comment | ||
exclude = ('created_at', 'modified_at',) | ||
# fields = ('entry', 'body', 'author',) | ||
|
||
|
||
class ArtProjectSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = ArtProject | ||
exclude = ('polymorphic_ctype',) | ||
|
||
|
||
class ResearchProjectSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = ResearchProject | ||
exclude = ('polymorphic_ctype',) | ||
|
||
|
||
class ProjectSerializer(serializers.PolymorphicModelSerializer): | ||
polymorphic_serializers = [ArtProjectSerializer, ResearchProjectSerializer] | ||
|
||
class Meta: | ||
model = Project | ||
exclude = ('polymorphic_ctype',) | ||
|
||
|
||
class CompanySerializer(serializers.ModelSerializer): | ||
current_project = relations.PolymorphicResourceRelatedField( | ||
ProjectSerializer, queryset=Project.objects.all()) | ||
future_projects = relations.PolymorphicResourceRelatedField( | ||
ProjectSerializer, queryset=Project.objects.all(), many=True) | ||
|
||
included_serializers = { | ||
'current_project': ProjectSerializer, | ||
'future_projects': ProjectSerializer, | ||
} | ||
|
||
class Meta: | ||
model = Company | ||
if version.parse(rest_framework.VERSION) >= version.parse('3.3'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting... I had no idea it was possible to use an |
||
fields = '__all__' |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
'django.contrib.auth', | ||
'django.contrib.admin', | ||
'rest_framework', | ||
'polymorphic', | ||
'example', | ||
] | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This jumps straight into how to use polymorphic resources. While that is appropriate for a Usage page, if this is the only place in the documentation that describes polymorphic resources, I think an introductory paragraph providing context and why a user would need this would be beneficial. Also, are there going to be related packages that are needed for this work? If so, can we link to them? I'm thinking about
django-polymorphic
here.