Par exemple, lorsque vous souhaitez obtenir une liste d'éléments appartenant à une catégorie spécifique, vous voulez que l'URL ressemble à / categories / <category> / items /
drf-nested-routers. alanjds / drf-nested-routers) J'ai essayé de l'utiliser.
Tout en regardant README, implémentez une URL imbriquée afin que l'élément relève d'une catégorie spécifique comme l'URL ci-dessous.
/categories
/categories/{pk}
/categories/{category_pk}/items
/categories/{category_pk}/items/{pk}
models.py Tout d'abord, implémentez la catégorie et le modèle d'élément.
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='Ordre d'affichage')
serializers.py Il implémente également des sérialiseurs de catégories et d'articles.
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
Comme cela sera décrit plus loin, bien que le routeur soit utilisé pour générer l'URL, il ne peut pas être spécifié de sorte que «pk» et «category_pk» soient des nombres.
Donc, vous devez faire attention que si vous entrez par inadvertance quelque chose comme / category / hoge / items
ou / category / 1 / items / hoge
, vous obtiendrez une Value Error
et une Internal Server Error.
Pour éviter ValueError
, retrieve () ʻutilise
rest_framework.generics.get_object_or_404au lieu de
django.shortcuts.get_object_or_404. Cependant, malheureusement,
list () n'existe pas dans
rest_framework.generics.get_list_or_404, donc si TypeError et ValuError se produisent après
rest_framework.generics.get_object_or_404`, augmentez 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 à l'aide de 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)),
]
L'URL enregistrée dans l'implémentation ci-dessus est la suivante.
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']
En regardant README, il n'y avait pas d'exemple d'implémentation autre que list ()
et retrieve ()
dans le ViewSet du côté enfant. Alors j'ai essayé.
create Il y a deux points à prendre en compte. Le premier point est d'ignorer l'existence de `` catégorie '' dans les données de requête reçues du client et d'utiliser category_pk (ou jouer avec une erreur). Le deuxième point est de ne pas oublier de vérifier l'existence d'une catégorie. Cela ressemble à ceci une fois mis en œuvre en y prêtant attention.
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 En gros, soyez prudent comme avec create. Cependant, le traitement lorsque la catégorie est spécifiée dans les données reçues du client est gênant, mais il se joue par validation.
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
Je pense que ce serait bien de pouvoir changer la catégorie, mais je me demande si le code d'état dans ce cas devrait être autour de status.HTTP_204_NO_CONTENT
.
La ressource n'existe pas dans l'URL lorsque PUT est terminé.
destroy
Fondamentalement, détruire équivaut à créer.
Cependant, comme il n'est pas nécessaire d'obtenir la catégorie, implémentez-la pour que l'élément soit obtenu avec get_object_or_404
.
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)
Il est facile de trier les résultats de l'accès à / categories / {category_pk} / items
.
Modifiez simplement le jeu de requêtes ViewSet.
class ItemViewSet(viewsets.ModelViewSet):
serializer_class = ItemSerializer
queryset = Item.objects.all().order_by('display_order')
Cependant, avec la méthode ci-dessus, il n'est pas possible de mettre des éléments dans le résultat de / categories / {category_pk}
et de les trier.
Dans ce cas, il ne semble pas y avoir d'autre moyen que de le spécifier dans l'ordre du modèle.
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='Ordre d'affichage')
Si vous voulez utiliser categories / some-slug / items
au lieu de categories / 1 / items
, spécifiez simplement lookup_field dans ViewSet.
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
lookup_field = 'slug'
L'URL enregistrée dans ce cas est la suivante.
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']
Puisque l'argument passé à ItemViewSet est category_slug, les arguments de diverses méthodes doivent y correspondre.
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 peut également être utilisé normalement.
Par exemple, si vous souhaitez trier les éléments dans l'ordre spécifié, implémentez une méthode pour mettre à jour display_order dans categories / 1 / items / sort
.
class ItemViewSet(viewsets.ModelViewSet):
(...réduction)
@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()
Si vous en avez envie, générez l'exemple de code quelque part.
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