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 875a600

Browse files
Merge pull request #104 from HackSoftware/nested_serializers_support
Nested serializers support
2 parents a001d9f + ec5784a commit 875a600

File tree

12 files changed

+36437
-44
lines changed

12 files changed

+36437
-44
lines changed

‎.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@ drfdocs.egg-info/
1111

1212
site/
1313

14+
demo/node_modules/
15+
rest_framework_docs/static/rest_framework_docs/js/dist.min.js
16+
1417
rest_framework_docs/static/node_modules/
1518
rest_framework_docs/static/rest_framework_docs/js/dist.min.js.map

‎demo/project/organisations/serializers.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
from project.accounts.serializers import UserProfileSerializer
44

55

6+
class MembershipSerializer(serializers.ModelSerializer):
7+
class Meta:
8+
model = Membership
9+
fields = ('joined', 'is_owner', 'role')
10+
11+
612
class CreateOrganisationSerializer(serializers.ModelSerializer):
13+
membership_set = MembershipSerializer(many=True)
714

815
class Meta:
916
model = Organisation
10-
fields = ('name', 'slug',)
17+
fields = ('name', 'slug','membership_set')
1118

1219

1320
class OrganisationMembersSerializer(serializers.ModelSerializer):
@@ -27,3 +34,11 @@ class OrganisationDetailSerializer(serializers.ModelSerializer):
2734
class Meta:
2835
model = Organisation
2936
fields = ('name', 'slug', 'is_active')
37+
38+
39+
class RetrieveOrganisationSerializer(serializers.ModelSerializer):
40+
membership_set = MembershipSerializer()
41+
42+
class Meta:
43+
model = Organisation
44+
fields = ('name', 'slug', 'is_active', 'membership_set')

‎demo/project/organisations/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
urlpatterns = [
66

77
url(r'^create/$', view=views.CreateOrganisationView.as_view(), name="create"),
8+
url(r'^(?P<slug>[\w-]+)/$', view=views.RetrieveOrganisationView.as_view(), name="organisation"),
89
url(r'^(?P<slug>[\w-]+)/members/$', view=views.OrganisationMembersView.as_view(), name="members"),
910
url(r'^(?P<slug>[\w-]+)/leave/$', view=views.LeaveOrganisationView.as_view(), name="leave")
1011

‎demo/project/organisations/views.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
from rest_framework.response import Response
33
from project.organisations.models import Organisation, Membership
44
from project.organisations.serializers import (
5-
CreateOrganisationSerializer, OrganisationMembersSerializer
5+
CreateOrganisationSerializer, OrganisationMembersSerializer, RetrieveOrganisationSerializer
66
)
77

88

9+
class RetrieveOrganisationView(generics.RetrieveAPIView):
10+
11+
serializer_class = RetrieveOrganisationSerializer
12+
13+
914
class CreateOrganisationView(generics.CreateAPIView):
1015

1116
serializer_class = CreateOrganisationSerializer

‎rest_framework_docs/api_endpoint.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import inspect
33
from django.contrib.admindocs.views import simplify_regex
44
from django.utils.encoding import force_str
5+
from rest_framework.serializers import BaseSerializer
56

67

78
class ApiEndpoint(object):
@@ -17,8 +18,12 @@ def __init__(self, pattern, parent_pattern=None, drf_router=None):
1718
self.allowed_methods = self.__get_allowed_methods__()
1819
# self.view_name = pattern.callback.__name__
1920
self.errors = None
20-
self.fields = self.__get_serializer_fields__()
21-
self.fields_json = self.__get_serializer_fields_json__()
21+
self.serializer_class = self.__get_serializer_class__()
22+
if self.serializer_class:
23+
self.serializer = self.__get_serializer__()
24+
self.fields = self.__get_serializer_fields__(self.serializer)
25+
self.fields_json = self.__get_serializer_fields_json__()
26+
2227
self.permissions = self.__get_permissions_class__()
2328

2429
def __get_path__(self, parent_pattern):
@@ -68,27 +73,39 @@ def __get_permissions_class__(self):
6873
for perm_class in self.pattern.callback.cls.permission_classes:
6974
return perm_class.__name__
7075

71-
def __get_serializer_fields__(self):
72-
fields = []
73-
serializer = None
76+
def __get_serializer__(self):
77+
try:
78+
return self.serializer_class()
79+
except KeyError as e:
80+
self.errors = e
7481

82+
def __get_serializer_class__(self):
7583
if hasattr(self.callback.cls, 'serializer_class'):
76-
serializer= self.callback.cls.serializer_class
84+
return self.callback.cls.serializer_class
7785

78-
elif hasattr(self.callback.cls, 'get_serializer_class'):
79-
serializer = self.callback.cls.get_serializer_class(self.pattern.callback.cls())
86+
if hasattr(self.callback.cls, 'get_serializer_class'):
87+
return self.callback.cls.get_serializer_class(self.pattern.callback.cls())
88+
89+
def __get_serializer_fields__(self, serializer):
90+
fields = []
8091

8192
if hasattr(serializer, 'get_fields'):
82-
try:
83-
fields = [{
93+
for key, field in serializer.get_fields().items():
94+
to_many_relation = True if hasattr(field, 'many') else False
95+
sub_fields = []
96+
97+
if to_many_relation:
98+
sub_fields = self.__get_serializer_fields__(field.child) if isinstance(field, BaseSerializer) else None
99+
else:
100+
sub_fields = self.__get_serializer_fields__(field) if isinstance(field, BaseSerializer) else None
101+
102+
fields.append({
84103
"name": key,
85104
"type": str(field.__class__.__name__),
86-
"required": field.required
87-
} for key, field in serializer().get_fields().items()]
88-
except KeyError as e:
89-
self.errors = e
90-
fields = []
91-
105+
"sub_fields": sub_fields,
106+
"required": field.required,
107+
"to_many_relation": to_many_relation
108+
})
92109
# FIXME:
93110
# Show more attibutes of `field`?
94111

‎rest_framework_docs/static/rest_framework_docs/js/dist.min.js

Lines changed: 36326 additions & 12 deletions
Large diffs are not rendered by default.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<ul class="list fields">
2+
{% for field in fields %}
3+
<li class="field">
4+
{{ field.name }}: {{ field.type }}
5+
{% if field.required %}
6+
<span class="label label-primary label-required" title="Required">R</span>
7+
{% endif %}
8+
9+
{% if field.to_many_relation %}
10+
<span class="label label-primary label-required" title="Support sending several objects of this type in a request ({{obj}, {obj}, ... })">Array of objects</span>
11+
{% endif %}
12+
13+
{% if field.sub_fields %}
14+
{%include "rest_framework_docs/components/fields_list.html" with fields=field.sub_fields %}
15+
{% endif %}
16+
</li>
17+
{% endfor %}
18+
</ul>

‎rest_framework_docs/templates/rest_framework_docs/home.html

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,7 @@ <h4 class="panel-title title">
6565

6666
{% if endpoint.fields %}
6767
<p class="fields-desc">Fields:</p>
68-
<ul class="list fields">
69-
{% for field in endpoint.fields %}
70-
<li class="field">{{ field.name }}: {{ field.type }} {% if field.required %}<span class="label label-primary label-required" title="Required">R</span>{% endif %}</li>
71-
{% endfor %}
72-
</ul>
68+
{%include "rest_framework_docs/components/fields_list.html" with fields=endpoint.fields %}
7369
{% elif not endpoint.errors %}
7470
<p>No fields.</p>
7571
{% endif %}

‎tests/serializers.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,26 @@ class Meta:
3434
extra_kwargs = {'password': {'write_only': True}}
3535

3636

37+
class MembershipSerializer(serializers.ModelSerializer):
38+
class Meta:
39+
model = Membership
40+
fields = ('joined', 'is_owner', 'role')
41+
42+
3743
class CreateOrganisationSerializer(serializers.ModelSerializer):
44+
membership_set = MembershipSerializer(many=True)
45+
46+
class Meta:
47+
model = Organisation
48+
fields = ('name', 'slug', 'membership_set')
49+
50+
51+
class RetrieveOrganisationSerializer(serializers.ModelSerializer):
52+
membership_set = MembershipSerializer()
3853

3954
class Meta:
4055
model = Organisation
41-
fields = ('name', 'slug',)
56+
fields = ('name', 'slug','is_active', 'membership_set')
4257

4358

4459
class OrganisationMembersSerializer(serializers.ModelSerializer):

‎tests/tests.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_index_view_with_endpoints(self):
2727
response = self.client.get(reverse('drfdocs'))
2828

2929
self.assertEqual(response.status_code, 200)
30-
self.assertEqual(len(response.context["endpoints"]), 14)
30+
self.assertEqual(len(response.context["endpoints"]), 15)
3131

3232
# Test the login view
3333
self.assertEqual(response.context["endpoints"][0].name_parent, "accounts")
@@ -72,9 +72,12 @@ def test_model_viewset(self):
7272
response = self.client.get(reverse('drfdocs'))
7373

7474
self.assertEqual(response.status_code, 200)
75-
self.assertEqual(response.context["endpoints"][10].path, '/organisation-model-viewsets/')
76-
self.assertEqual(response.context["endpoints"][11].path, '/organisation-model-viewsets/<pk>/')
77-
self.assertEqual(response.context["endpoints"][10].allowed_methods, ['GET', 'POST', 'OPTIONS'])
78-
self.assertEqual(response.context["endpoints"][11].allowed_methods, ['GET', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'])
79-
self.assertEqual(response.context["endpoints"][12].allowed_methods, ['POST', 'OPTIONS'])
80-
self.assertEqual(response.context["endpoints"][12].docstring, 'This is a test.')
75+
76+
self.assertEqual(response.context["endpoints"][10].path, '/organisations/<slug>/')
77+
self.assertEqual(response.context['endpoints'][6].fields[2]['to_many_relation'], True)
78+
self.assertEqual(response.context["endpoints"][11].path, '/organisation-model-viewsets/')
79+
self.assertEqual(response.context["endpoints"][12].path, '/organisation-model-viewsets/<pk>/')
80+
self.assertEqual(response.context["endpoints"][11].allowed_methods, ['GET', 'POST', 'OPTIONS'])
81+
self.assertEqual(response.context["endpoints"][12].allowed_methods, ['GET', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'])
82+
self.assertEqual(response.context["endpoints"][13].allowed_methods, ['POST', 'OPTIONS'])
83+
self.assertEqual(response.context["endpoints"][13].docstring, 'This is a test.')

0 commit comments

Comments
(0)

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