2013-06-27 6 views
18

Nie mogłem znaleźć żadnych informacji, jak to osiągnąć w samouczku na stronie Django REST Framework, a ja nie udało mi się znaleźć go w dokumentacji, choć jestem pewien, że jest gdzieś.Jak zaimplementować hierarchię zasobów (np./Parents/<id>/children) w Django REST Framework

Chcę issues być zasób rodzic i pages być dzieci, tak aby wszystkie strony /issues/1/pages zwrotów z issue_id z 1.

Czy jest dobrym sposobem osiągnięcia tego celu przy użyciu widoków opartych klasy rodzajowe?

Oto, co mam do tej pory.

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) 

Jak mogę wdrożyć tego rodzaju relacji między issues i pages?

+0

Dodałem 'def get_queryset (self): issu_id = self.kwargs ['issu_id'] return Strona.objects.filter (issu_id = numer wydania)' to 'PageList', a teraz GET działa dla' wydania//stron' . Teraz muszę po prostu wymyślić, jak publikować. –

+1

Dodałem 'def pre_save (self, obj): obj.issue_id = self.kwargs ['issu_id']' to 'PageList', a teraz POST też działa. Odpytanie stron od problemu, który nie istnieje, zwraca wynik pusty, a nie 404 nie znaleziono. Jeśli ktoś zna lepszy sposób, aby to zrobić, jestem bardzo zainteresowany, aby o tym usłyszeć. –

Odpowiedz

9

Oto kolejny sposób robiłem to:

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 

adresy URL.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'), 
    ) 

Jest bardziej zaangażowany niż kod z Viewsets/routerach ale to daje znacznie większą kontrolę nad tym co się dzieje.

Tutaj zdecydowałem się wystawiać zamówienia tylko jako dzieci klienta. Ponieważ są one rozdzielone, możesz użyć różnych klas serializatorów dla listy i szczegółów.

+0

Jeśli dodasz pre_save do OrdersByCustomer, podobnie jak napisałem to w odpowiedzi na moje oryginalne pytanie, zrobię to jako zaakceptowaną odpowiedź. –

+0

Przepraszam, że trwało to tak długo, zrobione! –

8

Oto, jak udało mi się osiągnąć to za pomocą nowego ViewSets i Routers od Rest-Framework w wersji 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 
Adresy 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 

Teraz „zamów/id/linie” zwróci listę odcinkach linii zamówień, które mają związek z Zakonem identyfikowany przez tę id.

Każda metoda na ViewSet ozdobiona @link lub @action otrzyma adres URL podczas rejestracji widoku z routerem.

+0

Dzięki za czysty przykład. Jednak chcę również, aby POST dla zasobów podrzędnych był pod rodzicami//dzieci i chcę, aby dzieci były dostępne tylko pod ich rodzicami, więc dzieci/nie powinny nawet być prawidłową ścieżką. Domyślam się, że wysyłanie postów może zostać naprawione za pomocą metody ozdobionej przez @action, ale wtedy nie wydaje się, że ma większą przewagę nad widokami opartymi na klasach. Bardzo podoba mi się pomysł ruterów. –

+0

Po głębszym zbadaniu, myślę, że korzystanie z ViewSets i routerów jest wygodne i łatwe, ale nadal wymaga wielu dostosowań, aby zachęcić ich do zrobienia czegoś w rodzaju relacji z rodzicem. Opublikuję jeszcze bardziej wyraźny sposób, w jaki robię to w innej odpowiedzi. –

+0

Podczas korzystania z tej hierarchicznej techniki zasobów z ViewSet i @link, czy znalazłeś sposób na zmianę docstringu wyświetlanego u góry interfejsu API do przeglądania, gdy podążasz za @ linkiem? W tej sytuacji wydaje się, że wyświetla pomoc dla rodzica (Zamówienie), a nie dla dziecka (OrderLine). – Gary

1

dodałem def get_queryset (self): issue_id = self.kwargs [ 'issue_id'] powrót Page.objects.filter (issue_id = issue_id) do PageList i teraz dostać prace dla wydania/ /pages. Teraz muszę po prostu wymyślić, jak publikować.

Dodałem def pre_save (self, obj): obj.issue_id = self.kwargs ['issu_id'] do PageList i teraz POST też działa. Odpytanie stron od problemu, który nie istnieje, zwraca wynik pusty, a nie 404 nie znaleziono. Jeśli ktoś zna lepszy sposób, aby to zrobić, jestem bardzo zainteresowany, aby o tym usłyszeć.

Jeśli twoja metoda get_queryset (self) zwraca pustą listę zamiast 404 NOT FOUND, proponuję użyć funkcji skrótu get_list_or_404 z django. Metoda mogłaby wyglądać następująco:

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) 

Wiem, że to stary post, ale może to może pomóc innym ludziom o tym samym lub jakiś podobny problem.

Powiązane problemy