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.
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']
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)
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')
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()
Wenn Sie Lust dazu haben, erhöhen Sie den Beispielcode irgendwo.
Python==3.6 Django==1.10.6 djangorestframework==3.6.2 drf-nested-routers==0.90.0
https://github.com/alanjds/drf-nested-routers
Recommended Posts