Skip to content

ResourceRelatedField now accepts serializer methods which return querysets. #151

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion example/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,19 @@ def __init__(self, *args, **kwargs):
suggested = relations.ResourceRelatedField(
source='get_suggested', model=Entry, read_only=True)

all_comments = relations.ResourceRelatedField(source='get_all_comments',
many=True, model=Comment, read_only=True)

def get_all_comments(self, obj):
return Comment.objects.all()

def get_suggested(self, obj):
return Entry.objects.exclude(pk=obj.pk).first()

class Meta:
model = Entry
fields = ('blog', 'headline', 'body_text', 'pub_date', 'mod_date',
'authors', 'comments', 'suggested',)
'authors', 'comments', 'suggested', 'all_comments',)


class AuthorSerializer(serializers.ModelSerializer):
Expand Down
10 changes: 10 additions & 0 deletions example/tests/integration/test_non_paginated_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ def test_multiple_entries_no_pagination(multiple_entries, rf):
},
"relationships":
{
'allComments': {
'meta': {'count': 2},
'data': [{'id': '1','type': 'comments'},
{'id': '2','type': 'comments'}]
},
"blog": {
"data": {"type": "blogs", "id": "1"}
},
Expand All @@ -53,6 +58,11 @@ def test_multiple_entries_no_pagination(multiple_entries, rf):
},
"relationships":
{
'allComments': {
'meta': {'count': 2},
'data': [{'id': '1','type': 'comments'},
{'id': '2','type': 'comments'}]
},
"blog": {
"data": {"type": "blogs", "id": "2"}
},
Expand Down
10 changes: 7 additions & 3 deletions example/tests/integration/test_pagination.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from django.core.urlresolvers import reverse

import pytest
Expand All @@ -22,6 +23,10 @@ def test_pagination_with_single_entry(single_entry, client):
},
"relationships":
{
'allComments': {
'meta': {'count': 1},
'data': [{'id': '1','type': 'comments'}]
},
"blog": {
"data": {"type": "blogs", "id": "1"}
},
Expand Down Expand Up @@ -53,7 +58,6 @@ def test_pagination_with_single_entry(single_entry, client):
}

response = client.get(reverse("entry-list"))
content_dump = redump_json(response.content)
expected_dump = dump_json(expected)
content = json.loads(response.content.decode('utf-8'))

assert content_dump == expected_dump
assert content == expected
37 changes: 37 additions & 0 deletions rest_framework_json_api/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@
from rest_framework_json_api.utils import format_relation_name, Hyperlink, \
get_resource_type_from_queryset, get_resource_type_from_instance

import pdb

JSONAPI_MANY_RELATION_KWARGS = ('model', ) + MANY_RELATION_KWARGS

class ManyResourceRelatedField(ManyRelatedField):
"""
Allows us to use serializer method RelatedFields
with return querysets
"""
def __init__(self, child_relation=None, *args, **kwargs):
model = kwargs.pop('model', None)
if model:
self.model = model
super(ManyResourceRelatedField, self).__init__(child_relation, *args, **kwargs)

def get_attribute(self, instance):
if self.source and hasattr(self.parent, self.source):
serializer_method = getattr(self.parent, self.source)
if hasattr(serializer_method, '__call__'):
return serializer_method(instance)
return super(ManyResourceRelatedField, self).get_attribute(instance)


class ResourceRelatedField(PrimaryKeyRelatedField):
self_link_view_name = None
Expand All @@ -22,6 +44,21 @@ class ResourceRelatedField(PrimaryKeyRelatedField):
'no_match': _('Invalid hyperlink - No URL match.'),
}

def __new__(cls, *args, **kwargs):
# We override this because getting
# serializer methods fails when many is true
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
return super(ResourceRelatedField, cls).__new__(cls, *args, **kwargs)

@classmethod
def many_init(cls, *args, **kwargs):
list_kwargs = {'child_relation': cls(*args, **kwargs)}
for key in kwargs.keys():
if key in JSONAPI_MANY_RELATION_KWARGS:
list_kwargs[key] = kwargs[key]
return ManyResourceRelatedField(**list_kwargs)

def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwargs):
if self_link_view_name is not None:
self.self_link_view_name = self_link_view_name
Expand Down
2 changes: 1 addition & 1 deletion rest_framework_json_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ def extract_relationships(fields, resource, resource_instance):
relation_data = list()

serializer_data = resource.get(field_name)
resource_instance_queryset = list(relation_instance_or_manager.all())
resource_instance_queryset = relation_instance_or_manager.all()
if isinstance(serializer_data, list):
for position in range(len(serializer_data)):
nested_resource_instance = resource_instance_queryset[position]
Expand Down