Ich habe die Elemente, die ich persönlich als Memo aufbewahren möchte, in den Inhalten zusammengefasst, die ich unter Verwendung des REST-Frameworks von Django untersucht habe. Wenn Sie nachschlagen, finden Sie die alltäglichen Inhalte und die Elemente, die Sie selbst implementiert haben. Wenn Sie im Inhalt dieses Artikels Vorschläge haben, dass es einen besseren Weg gibt oder dass es falsch ist, sich einem solchen Problem überhaupt zu stellen, hinterlassen Sie bitte einen Kommentar.
Es ist einfach, eine solche Rest-API mit dem Restframework-Router zu erstellen.
/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
Es ist jedoch schwierig, eine API mit verschachtelten URLs mit dem Rest-Framework-Router zu erstellen, wie unten gezeigt.
/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
Die Lösung besteht darin, drf-verschachtelte Router zu verwenden. drf-nested-routers ist eine Bibliothek, mit der Sie verschachtelte URLs einfach im Rest-Framework implementieren können.
Führen Sie eine Pip-Installation durch.
$ 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)),
]
Sie können jeden Primärschlüssel mit dem folgenden Argument abrufen. Der Schlüsselwortname des Arguments ist der in urls.py angegebene Suchname + _pk.
# 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)
Tatsächlich können Sie mit der Methode create () von standard views.ModelViewSet nicht mehrere Modelle gleichzeitig erstellen. Wenn Sie mehrere Modelle erstellen möchten, müssen Sie die API entsprechend drücken.
Daher habe ich einen Dekorateur erstellt, der ein einzelnes Modell oder mehrere Modelle mit views.ModelViewSet erstellen kann.
Kopieren Sie den folgenden Code und speichern Sie ihn in einer geeigneten Datei.
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
Importieren Sie den multi_create-Dekorator aus der zuvor gespeicherten Datei und hängen Sie ihn wie unten gezeigt an die create () -Methode von ViewSet an. Das Argument ist die Serializer-Klasse, die dem Modell entspricht, das Sie erstellen möchten.
# 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
Sie müssen lediglich die folgenden JSON-Daten im Listenformat POSTEN.
[
{"name": "hoge"},
{"name": "fuga"}
]
Die folgende Antwort wird zurückgegeben.
[
{
"id": 1,
"name": "hoge"
},
{
"id": 2,
"name": "fuga"
}
]
Möglicherweise möchten Sie die Feldwerte für den Serializer dynamisch ermitteln.
Dieses Mal werden wir serializers.SerializerMethodField () verwenden. Mit serializers.SerializerMethodField () können Sie den Wert des Felds anhand des Ergebnisses der Methode ermitteln.
Angenommen, Sie haben eine Modellklasse wie die folgende und eine hoge () -Methode, die name + _hoge zurückgibt.
# modles.py
class MyModel(models.Model):
name = models.CharField(max_length=100)
def hoge(self):
return "{}_hoge".format(self.name)
In Serializer wird der Wert des Wertefelds dynamisch bestimmt, indem Serializer angegeben werden. SerializerMethodField () wie unten gezeigt. Der angewendete Methodenname lautet get_ + Feldname. Diesmal ist der Rückgabewert der Methode get_value () der Wert. Es ist auch möglich, den angewendeten Methodennamen mit dem Argument method_name von SerializerMethodField () anzugeben.
# serializer.py
class MySerializer(serializers.ModelSerializer):
value = serializers.SerializerMethodField()
class Meta:
model = MyModel
def get_value(self, obj):
return obj.hoge()
Angenommen, die API wird getroffen und die create () -Methode des ViewSet wird aufgerufen. Wie kann ich zu diesem Zeitpunkt eine Fehlerantwort geben, wenn in der save () -Methode der Model-Klasse ein Fehler auftritt (siehe unten)? Es gibt keine Methode, die ich in der MyViewSet-Klasse für die Fehlerbehandlung mit try außer implementiert habe, und die save () -Methode von MyModel wird vollständig in der Black Box aufgerufen.
# 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():
(...)
Eine Lösung besteht darin, die create () -Methode für die Fehlerbehandlung wie unten gezeigt zu überschreiben.
# 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)
Bei dieser Methode ist es erforderlich, Fehler beim Erstellen und Aktualisieren auf die gleiche Weise zu behandeln.
Eine andere Lösung besteht darin, die handle_exception () -Methode zu überschreiben. handle_exception ist eine Standardmethode für das Rest-Framework, die Fehler behandelt. Wenn Sie beispielsweise eine nicht autorisierte HTTP-Methode treffen, wird eine Antwort zurückgegeben, die der folgenden ähnelt:
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
Content-Length: 42
{"detail": "Method 'DELETE' not allowed."}
Bei dieser Methode werden Fehler, die von handler_exception nicht ausgeschlossen werden, am Überschreibungsziel ausgeschlossen.
# 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)
Mit dieser Methode können Sie alle Fehler behandeln, die in diesem MyViewSet auftreten. Übrigens gibt es kein Problem mit der Methode zur Bestimmung des exc-Typs anhand der Instanz anstelle von try Except.
Die dritte Methode ist die Verwendung von custom_exception_handler ().
Beschreiben des Pfads von custom_exception_handler, der in settings.py implementiert werden soll.
# settings.py
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
Implementieren Sie custom_exception_handler () in der zuvor durch path angegebenen Datei.
# 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
Das Merkmal dieser Methode ist, dass die Fehler, die in allen Ansichten auftreten, in diesem custom_exception_handler zusammengefasst werden.
Jede dieser Methoden hat einen anderen Anwendungsbereich, daher möchte ich sie je nach Situation richtig anwenden.
Wenn Sie über die Lösung nachdenken, können Sie sie selbstverständlich einfach an den Serializer-Konstruktor (init) übergeben. In diesem Beispiel wird es an das Schlüsselwortargument user_data übergeben.
# 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)
Auf der empfangenden Seite wird init überschrieben und vom Schlüsselwortargument empfangen.
# 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)
Ich glaube nicht, dass es üblich ist, View-Werte an Serializer zu übergeben, aber Sie können sie verwenden, wenn Sie Serializer verwenden.SerializersMethodFiels () usw.
das ist alles.