[PYTHON] Implementieren Sie hierarchische URLs mit drf-verschachtelten Routern im Django REST-Framework

Wenn ich beispielsweise eine Liste von Elementen abrufe, die zu einer bestimmten Kategorie gehören, möchte ich, dass die URL wie folgt aussieht: / category / <Kategorie> / items / drf-verschachtelte Router alanjds / drf-nested-routers) Ich habe versucht, es zu verwenden.

Versuchen Sie, mit drf-verschachtelten Routern zu implementieren

Implementieren Sie beim Betrachten von README eine verschachtelte URL, sodass das Element unter eine bestimmte Kategorie fällt, wie die folgende URL.

/categories
/categories/{pk}
/categories/{category_pk}/items
/categories/{category_pk}/items/{pk}

models.py Implementieren Sie zunächst das Kategorie- und Artikelmodell.

class Category(models.Model):
    name = models.CharField(max_length=30)
    slug = models.SlugField(unique=True)


class Item(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category)
    display_order = models.IntegerField(default=0, help_text='Bestellung anzeigen')

serializers.py Außerdem werden Serializer für Kategorien und Artikel implementiert.

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = (
            'pk',
            'name',
            'slug',
        )


class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = (
            'pk',
            'name',
            'display_order',
            'category',
        )

views.py Wie später beschrieben wird, kann der Router zwar zum Generieren einer URL verwendet werden, er kann jedoch nicht so angegeben werden, dass "pk" und "category_pk" Zahlen sind. Sie müssen also darauf achten, dass Sie bei einer versehentlichen Eingabe von "/ category / hoge / items" oder "/ category / 1 / items / hoge" einen "Value Error" und einen Internal Server Error erhalten.

Um "ValueError" zu vermeiden, verwendet "retrieve ()" "rest_framework.generics.get_object_or_404" anstelle von "django.shortcuts.get_object_or_404". Leider ist "list ()" in "rest_framework.generics.get_list_or_404" nicht vorhanden. Wenn also TypeError und ValuError nach "rest_framework.generics.get_object_or_404" auftreten, erhöhen Sie Http404.

from rest_framework.generics import get_object_or_404

class CategoryViewSet(viewsets.ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer


class ItemViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = ItemSerializer
    queryset = Item.objects.all()

    def retrieve(self, request, pk=None, category_pk=None):
        item = get_object_or_404(self.queryset, pk=pk, category__pk=category_pk)
        serializer = self.get_serializer(item)
        return Response(serializer.data)

    def list(self, request, category_pk=None):
        try:
            items = get_list_or_404(self.queryset, category__pk=category_pk)
        except (TypeError, ValueError):
            raise Http404
        else:
            serializer = self.get_serializer(items, many=True)
            return Response(serializer.data)

urls.py Route mit NestedSimpleRouter.

from rest_framework_nested import routers

router = routers.SimpleRouter(trailing_slash=False)
router.register(r'categories', CategoryViewSet)

categories_router = routers.NestedSimpleRouter(
    router, r'categories', lookup='category', trailing_slash=False)
categories_router.register(r'items', ItemViewSet)

urlpatterns = [
    url(r'^', include(router.urls)),
    url(r'^', include(categories_router.urls)),
]

Die in der obigen Implementierung registrierte URL lautet wie folgt.

categories$ [name='category-list']
categories/(?P<pk>[^/.]+)$ [name='category-detail']
categories/(?P<categoary_pk>[^/.]+)/items$ [name='item-list']
categories/(?P<categoary_pk>[^/.]+)/items/(?P<pk>[^/.]+)$ [name='item-detail']

Implementierung von Update-Methoden

Bei README gab es im ViewSet auf der untergeordneten Seite keine andere Beispielimplementierung als "list ()" und "retrieve ()". Also habe ich es versucht.

create Es gibt zwei Punkte zu beachten. Der erste Punkt besteht darin, das Vorhandensein von "Kategorie" in den vom Client empfangenen Anforderungsdaten zu ignorieren und category_pk zu verwenden (oder mit einem Fehler zu spielen). Der zweite Punkt ist, sich daran zu erinnern, die Existenz einer Kategorie zu überprüfen. Es sieht so aus, wenn es implementiert wird, während man auf sie achtet.

    def create(self, request, category_pk=None):
        category = get_object_or_404(Category.objects, pk=category_pk)
        request.data['category'] = category.pk
        return super(ItemViewSet, self).create(request)

update Seien Sie grundsätzlich vorsichtig wie beim Erstellen. Die Verarbeitung, wenn die Kategorie in den vom Client empfangenen Daten angegeben ist, ist jedoch ärgerlich, wird jedoch durch Validierung abgespielt.

views.py:

    def update(self, request, category_pk=None, *args, **kwargs):
        category = get_object_or_404(Category.objects, pk=category_pk)
        request.data['category'] = category.pk
        return super(ItemViewSet, self).update(request, *args, **kwargs)

serializers.py:

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = (
            'pk',
            'name',
            'display_order',
            'category',
        )

    def validate_category(self, category):
        if self.instance and self.instance.category_id != category.id:
            raise serializers.ValidationError("can not update to category.")

        return category

Ich denke, es wäre schön, die Kategorie ändern zu können, aber ich frage mich, ob der Statuscode in diesem Fall "status.HTTP_204_NO_CONTENT" sein sollte. Die Ressource ist in der URL nicht vorhanden, wenn der PUT abgeschlossen ist.

destroy Zerstören ist im Grunde dasselbe wie Erstellen. Da es jedoch nicht erforderlich ist, die Kategorie abzurufen, implementieren Sie sie so, dass das Element mit "get_object_or_404" erhalten wird.

    def destroy(self, request, pk=None, category_pk=None):
        item = get_object_or_404(self.queryset, pk=pk, category__pk=category_pk)
        self.perform_destroy(item)
        return Response(status=status.HTTP_204_NO_CONTENT)

Kinder sortieren

Es ist einfach, die Ergebnisse des Zugriffs auf "/ category / {category_pk} / items" zu sortieren. Ändern Sie einfach das ViewSet-Abfrageset.

class ItemViewSet(viewsets.ModelViewSet):
    serializer_class = ItemSerializer
    queryset = Item.objects.all().order_by('display_order')

Mit der obigen Methode ist es jedoch nicht möglich, Elemente in das Ergebnis von / category / {category_pk} einzufügen und zu sortieren. In diesem Fall scheint es keinen anderen Weg zu geben, als ihn in der Modellreihenfolge anzugeben.

serializers.py:

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = (
            'pk',
            'name',
            'slug',
            'item_set'
        )

    item_set = ItemSerializer(many=True, read_only=True)

models.py:

class Item(models.Model):
    class Meta(object):
        ordering = ('display_order', 'pk')

    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category)
    display_order = models.IntegerField(default=0, help_text='Bestellung anzeigen')

Verwenden Sie Slug für URL

Wenn Sie "Kategorien / einige Slugs / Elemente" anstelle von "Kategorien / 1 / Elemente" verwenden möchten, geben Sie einfach lookup_field in ViewSet an.

class CategoryViewSet(viewsets.ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    lookup_field = 'slug'

Die in diesem Fall registrierte URL lautet wie folgt.

categories$ [name='category-list']
categories/(?P<slug>[^/.]+)$ [name='category-detail']
categories/(?P<category_slug>[^/.]+)/items$ [name='item-list']
categories/(?P<category_slug>[^/.]+)/items/(?P<pk>[^/.]+)$ [name='item-detail']

Da das an ItemViewSet übergebene Argument category_slug ist, müssen die Argumente verschiedener Methoden übereinstimmen.

class ItemViewSet(viewsets.ModelViewSet):
    serializer_class = ItemSerializer
    queryset = Item.objects.all().order_by('display_order')

    def retrieve(self, request, pk=None, category_slug=None):
        item = get_object_or_404(self.queryset, pk=pk, category__slug=category_slug)
        serializer = self.get_serializer(item)
        return Response(serializer.data)

list_ruote list_route kann auch normal verwendet werden. Wenn Sie beispielsweise Elemente in der angegebenen Reihenfolge sortieren möchten, implementieren Sie eine Methode zum Aktualisieren der Anzeigereihenfolge in "Kategorien / 1 / Elemente / Sortieren".

class ItemViewSet(viewsets.ModelViewSet):
    (...Kürzung)

    @list_route(methods=['patch'])
    @transaction.atomic
    def sort(self, request, category_pk=None):
        items = self.queryset.filter(category__pk=category_pk)

        for i, pk in enumerate(request.data.get('item_pks', [])):
            items.filter(pk=pk).update(display_order=i + 1)

        return Response()

Quellcode

Wenn Sie Lust dazu haben, erhöhen Sie den Beispielcode irgendwo.

Verschiedene Versionen

Python==3.6 Django==1.10.6 djangorestframework==3.6.2 drf-nested-routers==0.90.0

Referenzseite

https://github.com/alanjds/drf-nested-routers

Recommended Posts

Implementieren Sie hierarchische URLs mit drf-verschachtelten Routern im Django REST-Framework
Implementieren Sie die JWT-Anmeldefunktion im Django REST-Framework
Implementierung der Authentifizierungsfunktion in Django REST Framework mit djoser
Django REST Framework mit Vue.js
Melden Sie sich mit dem Django Rest Framework an
[Django] Verwenden Sie MessagePack mit dem Django REST-Framework
Erstellen Sie eine RESTful-API mit dem Django Rest Framework
Logisches Löschen in Django, DRF (Django REST Framework)
CRUD GET mit Nuxt & Django REST Framework ②
CRUD POST mit Nuxt & Django REST Framework
CRUD GET mit Nuxt & Django REST Framework ①
Implementierung der benutzerdefinierten Authentifizierungsfunktion für Benutzermodelle in Django REST Framework mit djoser
Wie man mit verstümmelten Charakteren in json von Django REST Framework umgeht
CRUD PUT, DELETE mit Nuxt & Django REST Framework
Grundlagen des Django REST-Frameworks
Tipps zum Django Rest Framework
Erstellen Sie eine Todo-App mit Django REST Framework + Angular
Weitere neue Benutzerauthentifizierungsmethoden mit Django REST Framework
Lassen Sie uns eine Todo-App mit dem Django REST-Framework erstellen
Erstellen Sie eine API für die Benutzerauthentifizierung mit Django REST Framework
Wenn Sie mit dem Django REST-Framework filtern möchten
Listenmethode für verschachtelte Ressourcen im Django REST-Framework
Implementieren Sie die API mit explosiver Geschwindigkeit mithilfe des Django REST Framework
Implementieren Sie die Follow-Funktion in Django
Django REST Framework Stolperstein
So schreiben Sie eine benutzerdefinierte Validierung in Django REST Framework
Laden Sie die Django-Shell mit ipython neu
Implementierung der JWT-Authentifizierungsfunktion in Django REST Framework mit djoser
Implementieren Sie die REST-API schnell in Python
GraphQL-API mit graphene_django in Django
Erstellen Sie eine REST-API, um dynamodb mit dem Django REST Framework zu betreiben
Ein Verwaltungstool, das sofort mit dem REST-Framework ng-admin + Django erstellt werden kann
Verschiedene Hinweise zum Django REST-Framework
Überlegungen zum Design von Django REST Framework + Clean Architecture
Implementieren Sie ein benutzerdefiniertes Benutzermodell in Django
Ich möchte eine API erstellen, die ein Modell mit einer rekursiven Beziehung im Django REST Framework zurückgibt
So können Sie die Funktionen des Django Rest Frameworks in einer Datei ausprobieren
So implementieren Sie Rails-Helfer-ähnliche Funktionen in Django
Speichern Sie mehrere Modelle in einem Formular mit Django
Starten Sie Django in einer virtuellen Umgebung mit Pipenv
Erstellen Sie mit Vagrant in 5 Minuten eine Django-Umgebung
So implementieren Sie "named_scope" von RubyOnRails mit Django
Entfernen Sie zusätzliche Zeichenfolgen in der URL mit kanonischem Ausdruck
Django REST Framework Ein wenig nützlich zu wissen.
Konfigurieren Sie ein Modul mit mehreren Dateien in Django
So erstellen Sie eine Rest-API in Django
Manchmal möchten Sie mit DRF (Django REST Framework) auf View information from Serializer zugreifen.