Как реализовать иерархию ресурсов (например./Parents/<id>/children) в Django REST Framework
Я не мог найти никакой информации о том, как этого достичь в учебнике на веб-сайте Django REST Framework, и мне не удалось найти его в документации, хотя я уверен, что он где-то там.
Я хочу, чтобы issues
был родительским ресурсом, а pages
- дочерними, так что /issues/1/pages
возвращает все страницы с issue_id
из 1.
Есть ли хороший способ достичь этого, используя общие представления на основе класса?
Вот что я до сих пор.
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)
Как я могу реализовать такую взаимосвязь между issues
и pages
?
Ответы
Ответ 1
Вот еще один способ, которым я это сделал:
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'),
)
Существует больше кода, чем с Viewsets/Routers, но это дает вам больший контроль над тем, что происходит.
Здесь я выбрал только выставлять заказы как дети клиента. Поскольку они отделены друг от друга, вы можете использовать разные классы сериализатора для списка с деталями.
Ответ 2
Вот как я достиг этого, используя новый ViewSets и Routers из Rest-Framework версии 2.3:
views.py:
from rest_framework import viewsets
from rest_framework.response import Response
from models import Order, OrderLine
from serializers import OrderSerializer, OrderLineSerializer
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
@link()
def lines(self, request, pk=None):
queryset = OrderLine.objects.filter(order__pk=pk)
serializer = OrderLineSerializer(queryset,
context={'request':request},
many=True)
return Response(serializer.data)
class OrderLineViewSet(viewsets.ModelViewSet):
queryset = OrderLine.objects.all()
serializer_class = OrderLineSerializer
serializers.py
from rest_framework import serializers
from models import Order, OrderLine
class OrderSerializer(serializers.HyperlinkedModelSerializer):
lines = serializers.HyperlinkedIdentityField(view_name='order-lines')
class Meta:
model = Order
class OrderLineSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = OrderLine
urls.py
from views import OrderViewSet, OrderLineViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'order', OrderViewSet)
router.register(r'orderline', OrderLineViewSet)
urlpatterns = router.urls
Теперь 'order/id/lines' вернет список сериализованных строк заказа, которые имеют отношение к ордеру, идентифицированному этим идентификатором.
Любой метод в ViewSet, украшенном @link или @action, получит URL-адрес при регистрации представления с маршрутизатором.
Ответ 3
Я добавил def get_queryset (self): issue_id = self.kwargs ['issue_id'] return Page.objects.filter(issue_id = issue_id) в ListList и теперь GET работает для выпуска/ /страницы. Теперь мне просто нужно выяснить, как отправить сообщение.
Я добавил def pre_save (self, obj): obj.issue_id = self.kwargs ['issue_id'] в pageList, и теперь работает POST. Запрос страниц из проблемы, которая не существует, возвращает пустой результат, а не 404, хотя и не найден. Если кто-нибудь знает, как лучше это сделать, мне очень интересно услышать об этом.
Если ваш метод get_queryset (self) возвращает пустой список вместо 404 NOT FOUND, я бы предложил использовать функцию ярлыка get_list_or_404 из django.
Метод может выглядеть следующим образом:
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)
Я знаю, что это старый пост, но, возможно, это может помочь другим людям с одинаковой или какой-то аналогичной проблемой.