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.
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
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.
Effectuez une installation de pip.
$ pip install drf-nested-routers
# 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)
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.
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
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"
}
]
Vous souhaiterez peut-être déterminer dynamiquement les valeurs de champ pour le sérialiseur.
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()
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():
(...)
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.
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.
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.
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.
Recommended Posts