I couldn't find any information on how to achieve this in the tutorial at the Django REST Framework website and I haven't managed to find it in the documentation, though I'm sure it's there somewhere.
I want issues
to be the parent resource and pages
to be the children so that /issues/1/pages
returns all pages with issue_id
of 1.
Is there a good way to achieve this using generic class based views?
Here's what I have so far.
restAPI/urls.py:
from django.conf.urls import patterns, url
from rest_framework.urlpatterns import format_suffix_patterns
from restAPI import views
urlpatterns = patterns('',
url(r'^issues/$', views.IssueList.as_view()),
url(r'^issues/(?P<pk>[0-9]+)/$', views.IssueDetail.as_view()),
url(r'^issues/(?P<issue_id>[0-9]+)/pages/$', views.PageList.as_view()),
url(r'^pages/(?P<pk>[0-9]+)/$', views.PageDetail.as_view()),
)
urlpatterns = format_suffix_patterns(urlpatterns)
restAPI/models.py:
from django.db import models
class Issue(models.Model):
created = models.DateTimeField(auto_now_add=True)
revision = models.IntegerField(default = 1)
issue_date = models.DateTimeField(auto_now_add=True)
issue_image_url = models.CharField(max_length=100)
class Page(models.Model):
created = models.DateTimeField(auto_now_add=True)
page_number = models.IntegerField()
standard_page_url = models.CharField(max_length=100, default='')
large_page_url = models.CharField(max_length=100, default='')
thumbnail_url = models.CharField(max_length=100, default='')
issue = models.ForeignKey(Issue, related_name="pages")
class Meta:
ordering = ('page_number',)
restAPI/serializers.py:
from rest_framework import serializers
from restAPI.models import Page, Issue
class IssueSerializer(serializers.ModelSerializer):
class Meta:
model = Issue
fields = ('id', 'created', 'revision', 'issue_date', 'issue_image_url')
class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page
fields = ('id', 'created', 'page_number', 'standard_page_url', 'large_page_url', 'thumbnail_url')
restAPI/views.py:
from restAPI.models import Page, Issue
from restAPI.serializers import PageSerializer, IssueSerializer
from rest_framework import mixins
from rest_framework import generics
class IssueList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Issue.objects.all()
serializer_class = IssueSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class IssueDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Issue.objects.all()
serializer_class = IssueSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class PageList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Page.objects.all()
serializer_class = PageSerializer
def get(self, request, *args, **kwargs):
print kwargs
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class PageDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Page.objects.all()
serializer_class = PageSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
How can I implement this sort of relationship between issues
and pages
?
Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
Django REST framework is an open source, flexible and fully-featured library with modular and customizable architecture that aims at building sophisticated web APIs and uses Python and Django.
Here is another way I've done this:
views.py
from models import Customer, Order from serializers import CustomerSerializer, OrderSerializer from rest_framework import generics class CustomerList(generics.ListCreateAPIView): queryset = Customer.objects.all() serializer_class = CustomerSerializer class CustomerDetail(generics.RetrieveUpdateDestroyAPIView) queryset = Customer.objects.all() serializer_class = CustomerSerializer class OrdersByCustomer(generics.ListCreateAPIView): queryset = Order.objects.all() serializer_class = OrderSerializer def get_queryset(self): customer_pk = self.kwargs['customer_pk'] return self.queryset.filter(customer__pk=customer_pk) def pre_save(self, obj): obj.customer_id = self.kwargs['customer_pk'] class OrderDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Order.objects.all() serializer_class = OrderSerializer
serializers.py
from models import Customer, Order from rest_framework import serializers from rest_framework.reverse import reverse class OrderSerializer(serializers.HyperlinkedModelSerializer) class Meta: model = Order class CustomerSerializer(serializers.HyperlinkedModelSerializer) orders = serializers.SerializerMethodField('get_customer_orders') def get_customer_orders(self, obj): return reverse('ordersbycustomer-list', args=[obj.pk], request=self.context['request']) class Meta: model = Customer
urls.py
from django.conf.urls import patterns, include, url from views import OrdersByCustomer, CustomerDetail, CustomerList urlpatterns = patterns("", url(r'^customers/(?P<customer_pk>.+)/orders/$', OrdersByCustomer.as_view(), name='ordersbycustomer-list'), url(r'^customers/(?P<pk>.+)/$', CustomerDetail.as_view(), name='customer-detail'), url(r'^customers/$', CustomerList.as_view(), name='customer-list'), )
There is more code involved than with Viewsets/Routers but this gives you much more control over what is going on.
Here I have chosen to only expose orders as children of a customer. Since they are separated, you can use different serializer classes for list vs detail.
I added def get_queryset(self): issue_id = self.kwargs['issue_id'] return Page.objects.filter(issue_id = issue_id) to PageList and now GET works for issue/ /pages. Now I just have to figure out how to post as well.
I added def pre_save(self, obj): obj.issue_id = self.kwargs['issue_id'] to PageList and now POST works too. Querying pages from an issue that doesn't exist returns an empty result rather than 404 not found though. If anyone knows of a better way to do this I'm very interested to hear about it.
If your method get_queryset(self) returns an empty list instead of 404 NOT FOUND, I would suggest to use the shortcut function get_list_or_404 from django. The method could look like this:
from django.shortcuts import get_list_or_404
def get_queryset(self):
filter = {}
filter['issue_id'] = self.kwargs['issue_id']
return get_list_or_404(self.queryset, **filter)
I know this is an old post, but maybe this could help other people having the same or some similar problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With