2013-06-27 2 views
18

Я не смог найти какую-либо информацию о том, как этого достичь в учебнике на веб-сайте Django REST Framework, и у меня нет удалось найти его в документации, хотя я уверен, что он где-то там.Как реализовать иерархию ресурсов (например,/родители/<id>/дети) в 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?

+0

Я добавил 'def get_queryset (self): issue_id = self.kwargs ['issue_id'] return Page.objects.filter (issue_id = issue_id)' to 'PageList', и теперь GET работает для' issue//pages' , Теперь мне просто нужно выяснить, как публиковать сообщения. –

+1

Я добавил 'def pre_save (self, obj): obj.issue_id = self.kwargs ['issue_id']' to 'PageList', и теперь работает POST. Запрос страниц из проблемы, которая не существует, возвращает пустой результат, а не 404, хотя и не найден. Если кто-то знает, как лучше это сделать, мне очень интересно услышать об этом. –

ответ

9

Вот еще один способ, которым я сделал это:

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.ру

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/маршрутизаторах, но это дает гораздо больший контроль над тем, что происходит.

Здесь я решил выставить только заказы как дети клиента. Поскольку они отделены друг от друга, вы можете использовать разные классы сериализатора для списка с деталями.

+0

Если вы добавите pre_save в OrdersByCustomer, подобно тому, как я написал его в ответе на мой оригинальный вопрос, я сделаю это принятым ответом. –

+0

жаль, что это заняло так много времени! –

8

Вот как я достиг этого с помощью нового 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 

URL .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 

Теперь «заказать/ID/линии» будет возвращать список сериализованными ордерных линий, которые имеют отношения с приказом определены этим идентификатором.

Любой метод на ViewSet, украшенном @link или @action, будет иметь URL-адрес при регистрации представления с маршрутизатором.

+0

Спасибо за чистый пример. Тем не менее, я также хочу, чтобы POST для дочернего ресурса находился под родителями//детьми, и я хочу, чтобы дети были доступны только у их родителей, поэтому дети/не должны быть даже допустимым путем. Я предполагаю, что публикация, вероятно, может быть исправлена ​​с помощью метода, украшенного @action, но тогда это не похоже на большую часть преимуществ над представлениями, основанными на классе. Мне действительно нравится идея маршрутизаторов. –

+0

После изучения этого, я думаю, что использование ViewSets и Routers является удобным и легким, но для этого требуется много настроек, чтобы заставить их делать что-то вроде родительских отношений с дочерними элементами. Я напишу еще один более ясный способ, который я делал в другом ответе. –

+0

При использовании этой иерархической методики ресурсов с помощью ViewSet и @link вы нашли способ изменить docstring, отображаемую в верхней части браузера с возможностью просмотра, когда вы следуете @link? Как бы то ни было, он, похоже, отображает помощь родителя (Заказ), а не дочернего (OrderLine). – Gary

1

Я добавил Защиту get_queryset (Self): ISSUE_ID = self.kwargs [ 'ISSUE_ID'] вернуться Page.objects.filter (ISSUE_ID = ISSUE_ID) в PageList и теперь 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) 

Я знаю, что это старый пост, но, возможно, это может помочь другим людям, имеющим такой же или какой-то подобной проблемой.

 Смежные вопросы

  • Нет связанных вопросов^_^