2013-06-27 8 views
18

Ich konnte keine Informationen finden, wie dies im Tutorial auf der Django REST Framework-Website zu erreichen, und ich habe nicht habe es in der Dokumentation gefunden, obwohl ich sicher bin, dass es irgendwo da ist.Wie man eine Hierarchie von Ressourcen implementiert (zB ./elterns/<id>/Children) in Django REST Framework

I issues wollen, dass die übergeordnete Ressource und pages werden, um die Kinder zu sein, so dass /issues/1/pages gibt alle Seiten mit issue_id von 1.

Gibt es ein guter Weg, dies mit generische Klasse basierte Ansichten zu erreichen?

Hier ist, was ich bisher habe.

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) 

Wie kann ich diese Art von Beziehung zwischen issues implementieren und pages?

+0

Ich habe 'def get_queryset (self) hinzugefügt: issue_id = self.kwargs ['issue_id'] return Page.objects.filter (issue_id = issue_id)' zu 'PageList' und jetzt funktioniert GET für' issue//pages' . Jetzt muss ich nur noch herausfinden, wie man schreibt. –

+1

Ich habe 'def pre_save (self, obj): obj.issue_id = self.kwargs [' issue_id '] 'zu' PageList' hinzugefügt und jetzt funktioniert POST auch. Das Abfragen von Seiten aus einem Problem, das nicht vorhanden ist, gibt ein leeres Ergebnis zurück, obwohl 404 nicht gefunden wurde. Wenn jemand einen besseren Weg kennt, interessiert mich das sehr. –

Antwort

9

ist hier eine andere Art, wie ich dies getan haben:

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'), 
    ) 

Es ist mehr Code beteiligt als bei Viewset/Router aber das gibt Ihnen viel mehr Kontrolle darüber, was los ist.

Hier habe ich gewählt, nur Bestellungen als Kinder eines Kunden auszusetzen. Da sie getrennt sind, können Sie verschiedene Serialisierungsklassen für Liste oder Detail verwenden.

+0

Wenn Sie pre_save zu OrdersByCustomer ähnlich hinzufügen, wie ich es in einer Antwort auf meine ursprüngliche Frage geschrieben habe, werde ich dies die akzeptierte Antwort machen. –

+0

Entschuldigung das dauerte so lange, fertig! –

8

Hier ist, wie ich erreicht habe diese Version die neue ViewSets und Routers von 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 

Jetzt ‚bestellen/id/Linien‘ wird eine Liste von serialisierten Auftragspositionen zurückzukehren, die eine Beziehung mit dem Orden der durch die ID identifiziert haben.

Jede Methode auf einem ViewSet, die mit @link oder @action versehen ist, erhält eine URL, wenn Sie die Ansicht beim Router registrieren.

+0

Danke für das saubere Beispiel. Ich möchte aber auch, dass POST für die untergeordnete Ressource unter Eltern//children liegt und ich möchte, dass Kinder nur unter ihren Eltern zugänglich sind, also sollte children/nicht einmal ein gültiger Pfad sein. Ich denke, das Posting könnte wahrscheinlich mit einer Methode korrigiert werden, die mit @action versehen ist, aber dann scheint es nicht so viel von Vorteil gegenüber klassenbasierten Views zu sein. Ich mag die Idee von Routern allerdings sehr. –

+0

Nachdem ich das weiter erkundet habe, denke ich, dass die Verwendung von ViewSets und Routern bequem und einfach ist, aber immer noch viel Anpassung erfordert, damit sie etwas wie die Eltern-Kind-Beziehung tun können. Ich werde einen anderen, expliziteren Weg aufzeigen, dass ich dies in einer anderen Antwort getan habe. –

+0

Haben Sie bei Verwendung dieser hierarchischen Ressourcentechnik mit ViewSet und @link eine Möglichkeit gefunden, den Docstring, der oben auf der browserfähigen API angezeigt wird, zu ändern, wenn Sie dem @link folgen? So scheint es, dass die Hilfe für das übergeordnete Element (Order) und nicht für das untergeordnete Element (OrderLine) angezeigt wird. – Gary

1

Ich habe def get_queryset (Selbst-): ISSUE_ID = self.kwargs [ 'ISSUE_ID'] zurückzukehren Page.objects.filter (ISSUE_ID = ISSUE_ID) zu Pagelist und jetzt GET Werke für Ausgabe/ /pages. Jetzt muss ich nur noch herausfinden, wie man schreibt.

Ich habe def pre_save (self, obj) hinzugefügt: obj.issue_id = self.kwargs ['issue_id'] zu PageList und jetzt funktioniert auch POST. Das Abfragen von Seiten aus einem Problem, das nicht vorhanden ist, gibt ein leeres Ergebnis zurück, obwohl 404 nicht gefunden wurde. Wenn jemand einen besseren Weg kennt, interessiert mich das sehr.

Wenn Ihre Methode get_queryset (Selbst-) eine leere Liste zurückgibt statt 404 NOT FOUND, würde ich vorschlagen, die Abkürzung Funktion get_list_or_404 von django zu verwenden. Das Verfahren könnte wie folgt aussehen:

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) 

Ich weiß, dass dies eine alte Post, aber vielleicht könnte dies anderen Menschen helfen, das gleiche oder ein ähnliches Problem.