[PYTHON] Framework Django REST Un peu utile à savoir.

J'ai résumé les éléments que je souhaite personnellement conserver comme mémo dans le contenu que j'ai étudié en utilisant le framework REST de Django. Si vous le recherchez, vous trouverez les contenus qui sont courants et les éléments que vous avez vous-même mis en œuvre. Si vous avez des suggestions dans le contenu de cet article selon lesquelles il existe une meilleure façon de le faire, ou qu'il est faux de faire face à un tel problème en premier lieu, veuillez laisser un commentaire.

Je souhaite exprimer une URL imbriquée

Il est facile de créer une API de repos comme celle-ci en utilisant le routeur restframework.

/api/v1/groups/ GET POST
/api/v1/groups/1/ GET PUT PATCH DELETE
/api/v1/members/ GET POST
/api/v1/members/1/ GET PUT PATCH DELETE

Cependant, il est difficile de créer une API avec des URL imbriquées avec le routeur du framework rest comme indiqué ci-dessous.

/api/v1/groups/ GET POST
/api/v1/groups/1/ GET PUT PATCH DELETE
/api/v1/groups/1/members/ GET POST
/api/v1/groups/1/members/1/ GET PUT PATCH DELETE

Solution

La solution consiste à utiliser des routeurs imbriqués drf. drf-nested-routers est une bibliothèque qui vous permet d'implémenter facilement des URL imbriquées sur le framework rest.

Méthode d'introduction

Effectuez une installation de pip.

$ pip install drf-nested-routers

la mise en oeuvre

# urls.py

from rest_framework_nested import routers
from .views import *

router = routers.SimpleRouter()

router.register(r'groups', GroupViewSet)
groups_router = routers.NestedSimpleRouter(router, r'groups', lookup='group')
groups_router.register(r'members', MemberViewSet, base_name='groups-members')

urlpatterns = [
	url(r'^api/v1/', include(router.urls)),
	url(r'^api/v1/', include(groups_router.urls)),
]

Vous pouvez obtenir chaque clé primaire avec l'argument comme suit. Le nom du mot-clé de l'argument est le nom de recherche + _pk spécifié dans urls.py.

# views.py
class GroupViewSet(viewsets.ViewSet):
    def list(self, request):
        (...)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        group = self.queryset.get(pk=pk)
        (...)
        return Response(serializer.data)


class MemberViewSet(viewsets.ViewSet):
    def list(self, request, group_pk=None):
        members = self.queryset.filter(group=group_pk)
        (...)
        return Response(serializer.data)

    def retrieve(self, request, pk=None, group_pk=None):
        member = self.queryset.get(pk=pk, group=group_pk)
        (...)
        return Response(serializer.data)

Je souhaite créer plusieurs modèles à la fois avec l'API POST de REST implémentée par ModelViewSet

En fait, la méthode standard views.ModelViewSet create () ne vous permet pas de créer plusieurs modèles à la fois. Si vous souhaitez créer plusieurs modèles, vous devez accéder à l'API en conséquence.

Solution

Code créé

Par conséquent, j'ai créé un décorateur qui peut créer un seul modèle ou plusieurs modèles avec des vues.ModelViewSet.

Copiez le code ci-dessous et enregistrez-le dans un fichier approprié.

from rest_framework.response import Response
from rest_framework import status

def multi_create(serializer_class=None):
    def __multi_create(function):
        def __wrapper(self, request, *args, **kwargs):
            many = False
            if isinstance(request.data, list):
                many = True
            serializer = serializer_class(data=request.data, many=many)
            if serializer.is_valid():
                serializer.save()
                headers = self.get_success_headers(serializer.data)
                data = serializer.data
                result = function(self, request, *args, **kwargs)
                if result is not None:
                    return result
                if many:
                    data = list(data)
                return Response(data,
                                status=status.HTTP_201_CREATED,
                                headers=headers)
            else:
                return Response(serializer.errors,
                                status=status.HTTP_400_BAD_REQUEST)
        return __wrapper
    return __multi_create

Comment utiliser

Importez le décorateur multi_create à partir du fichier que vous avez enregistré précédemment et attachez-le à la méthode create () de ViewSet comme indiqué ci-dessous. L'argument est la classe Serializer qui correspond au modèle que vous souhaitez créer.

# views.py

from .decorators import multi_create

class MyViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MySerializer
  
	@multi_create(serializer_class=MySerializer)
    def create(self, request):
        pass

Tout ce que vous avez à faire est de POSTER les données JSON au format de liste suivant.

[
	{"name": "hoge"},
	{"name": "fuga"}
]

La réponse suivante sera renvoyée.

[
	{
		"id": 1,
		"name": "hoge"
	},
	{
		"id": 2,
		"name": "fuga"
	}
]

Je souhaite déterminer dynamiquement la valeur du champ de Serializer

Vous souhaiterez peut-être déterminer dynamiquement les valeurs de champ pour le sérialiseur.

Solution

Cette fois, nous utiliserons des sérialiseurs.SerializerMethodField (). En utilisant serializers.SerializerMethodField (), vous pouvez déterminer la valeur du champ en fonction du résultat de la méthode.

Supposons que vous ayez une classe Model comme celle ci-dessous et une méthode hoge () qui renvoie name + _hoge.

# modles.py
class MyModel(models.Model):
    name = models.CharField(max_length=100)
    
    def hoge(self):
        return "{}_hoge".format(self.name)

Dans Serializer, la valeur du champ de valeur est déterminée dynamiquement en spécifiant serializers.SerializerMethodField () comme indiqué ci-dessous. Le nom de la méthode appliquée est le nom du champ get_ +. Cette fois, la valeur de retour de la méthode get_value () sera la valeur de value. Il est également possible de spécifier le nom de la méthode appliquée avec l'argument nom_méthode de SerializerMethodField ().

# serializer.py
class MySerializer(serializers.ModelSerializer):
    value = serializers.SerializerMethodField()
    
    class Meta:
        model = MyModel
    
    def get_value(self, obj):
        return obj.hoge()

Je souhaite renvoyer une erreur survenue dans Model ou Selializer comme réponse

Supposons que l'API soit touchée et que la méthode create () de ViewSet soit appelée. À ce moment-là, si une erreur se produit dans la méthode save () de la classe Model comme indiqué ci-dessous, comment dois-je faire une réponse d'erreur? Il n'y a aucune méthode que j'ai implémentée dans la classe MyViewSet pour la gestion des erreurs avec try sauf, et la méthode save () de MyModel est appelée complètement à l'intérieur de la boîte noire.

# views.py
class MyViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MySerializer
# models.py
class MyModel(models.Model):
    name = models.CharField(max_length=100)

    def save(self, force_insert=False, force_update=False,
             using=None, update_fields=None):
        if self.hoge():
            raise HogeError('hoge error.')
        super(MyModel, self).save(*args, **kwargs)

    def hoge():
        (...)

Solution

Premier

Une solution consiste à remplacer la méthode create () pour la gestion des erreurs comme indiqué ci-dessous.

# views.py
class MyViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MySerializer

    def create(self, request):
        try:
           super(MyViewSet, self).create(*args, **kwargs) 
        except HogeError:
            (....)
            return Response(content, status=status.HTTP_400_BAD_REQUEST)

    def update(self, request):
        try:
           super(MyViewSet, self).update(*args, **kwargs) 
        except HogeError:
            (....)
            return Response(content, status=status.HTTP_400_BAD_REQUEST)

Avec cette méthode, il est nécessaire de gérer Error de la même manière lors de la création et de la mise à jour.

Seconde

Une autre solution consiste donc à remplacer la méthode handle_exception (). handle_exception est une méthode standard de cadre de repos qui gère les erreurs. Par exemple, frapper une méthode HTTP non autorisée renverra une réponse similaire à la suivante:

HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
Content-Length: 42

{"detail": "Method 'DELETE' not allowed."}

Avec cette méthode, les erreurs qui ne sont pas exclues par handler_exception sont exclues à la destination de remplacement.

# views.py
class MyViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MySerializer

    def handle_exception(self, exc):
        try:
            return super(MyViewSet, self).handle_exception(exc)
        except HogeError:
            content = {'detail': '{}'.format(exc.args)}
            return Response(content, status=status.HTTP_400_BAD_REQUEST)

En utilisant cette méthode, vous pouvez gérer toutes les erreurs qui se produisent dans ce MyViewSet. À propos, il n'y a aucun problème avec la méthode de détermination du type exc par is instance au lieu de try except.

Troisième

La troisième méthode consiste à utiliser custom_exception_handler ().

Décrivez le chemin de custom_exception_handler à implémenter dans settings.py.

# settings.py
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}

Implémentez custom_exception_handler () dans le fichier spécifié par le chemin précédemment.

# utils.py
from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):

    response = exception_handler(exc, context)
    if isinstance(exc, HogeError):
        content = {'detail': '{}'.format(exc.args)}
        return Response(content, status=status.HTTP_400_BAD_REQUEST)
    return response

La caractéristique de cette méthode est que les erreurs qui se produisent dans toutes les vues sont regroupées dans ce custom_exception_handler.

Chacune de ces méthodes a une portée différente, je voudrais donc les utiliser correctement en fonction de la situation.

Je souhaite transmettre la valeur de View au sérialiseur

Solution

Si vous pensez à la solution, vous pouvez simplement la passer au constructeur Serializer (init) comme une évidence. Dans cet exemple, il est passé à l'argument mot-clé user_data.

# views.py
class MyViewSet(views.ModelViewSet):
    def retrieve(self, request):
        user_data = request.GET['user_data']
        (...)
        serializer = MySerializer(My_list, many=True, user_data=user_data)

Du côté de la réception, init est remplacé et reçu à partir de l'argument mot-clé.

# serializer.py
class MySerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        self.user_data = kwargs.pop('user_data', '')
        super(MySerializer, self).__init__(*args, **kwargs)

Je ne pense pas qu'il soit courant de transmettre les valeurs de View aux sérialiseurs, mais vous pouvez les utiliser lorsque vous utilisez des sérialiseurs.

c'est tout.

référence

Recommended Posts

Framework Django REST Un peu utile à savoir.
Les itertools de Python sont un peu utiles à connaître
Créer une API REST pour faire fonctionner dynamodb avec le Framework Django REST
Bases du framework Django REST
Comment créer une API Rest dans Django
Astuces Django Rest Framework
Créer une application Todo avec Django REST Framework + Angular
Essayez de créer une application Todo avec le framework Django REST
Lorsque vous souhaitez filtrer avec le framework Django REST
Bloc d'achoppement du framework Django REST
Framework Django REST avec Vue.js
Connectez-vous avec Django Rest Framework
Comment écrire une validation personnalisée dans Django REST Framework
Comment réinitialiser le mot de passe via l'API à l'aide du framework Rest Django
[Django] Utiliser MessagePack avec le framework Django REST
Créer une API RESTful avec Django Rest Framework
Suppression logique dans Django, DRF (Django REST Framework)
Comprendre la commodité de Django Rest Framework
Un outil administratif qui peut être créé immédiatement avec le framework ng-admin + Django REST
CRUD GET avec Nuxt & Django REST Framework ②
CRUD POST avec Nuxt & Django REST Framework
CRUD GET avec Nuxt & Django REST Framework ①
Django REST Framework + Considération de conception d'architecture propre
Comment gérer les caractères déformés dans json de Django REST Framework
J'ai fait une webAPI! Construire un environnement à partir de Django Rest Framework 1 avec EC2
Je souhaite créer une API qui retourne un modèle avec une relation récursive dans Django REST Framework
Comment vous permettre d'essayer les fonctionnalités du framework django rest dans un seul fichier
La route pour lutter contre la connexion entre Nginx, Django et uwsgi et gagner un peu
Comment générer automatiquement un document API avec le framework Django REST et POST à partir de l'écran de document
L'histoire de Django créant une bibliothèque qui pourrait être un peu plus utile
CRUD PUT, DELETE avec Nuxt & Django REST Framework
Comment développer une application de panier avec Django
Implémenter la fonctionnalité de connexion JWT dans le framework Django REST
J'aimerais en savoir plus sur Django page nation
Pour moi en tant que débutant Django (3) - Bonjour tout le monde! ---
Implémentation de la fonction d'authentification dans Django REST Framework à l'aide de djoser
Je souhaite télécharger une application Django sur heroku
Parfois, vous souhaitez accéder aux informations de vue depuis Serializer avec DRF (Django REST Framework)
Une petite histoire à savoir comme un point addictif lors de l'écriture d'applications Twilio à l'aide de Python sur AWS Lambda
Un peu d'informations addictives sur Cliff, le framework CLI
Plus de nouvelles méthodes d'authentification des utilisateurs avec Django REST Framework
Python Ver. Présentation de WebPay avec un peu de code
(Python) Essayez de développer une application Web en utilisant Django
[CRUD] [Django] Créer un site CRUD en utilisant le framework Python Django ~ 1 ~
Comment déployer une application Django dans le cloud Alibaba
Créer une API autour de l'authentification des utilisateurs avec Django REST Framework
Comment créer un environnement Django (python) sur Docker
Étapes de l'installation de Python 3 à la création d'une application Django
[Django Rest Framework] Personnalisez la fonction de filtre à l'aide de Django-Filter
[CRUD] [Django] Créer un site CRUD en utilisant le framework Python Django ~ 2 ~
Transition vers l'écran de mise à jour avec le Django a tag
À moi-même en tant que débutant Django (1) -Création d'un projet / application-
Comment exécuter Django sur IIS sur un serveur Windows
Comment référencer des fichiers statiques dans un projet Django
À moi-même en tant que débutant Django (4) --Créer une application mémo-
[CRUD] [Django] Créer un site CRUD en utilisant le framework Python Django ~ 3 ~
[CRUD] [Django] Créer un site CRUD en utilisant le framework Python Django ~ 4 ~
[CRUD] [Django] Créer un site CRUD en utilisant le framework Python Django ~ 5 ~
Pour moi en tant que débutant Django (2) - Qu'est-ce que MTV?