2013-08-19 23 views
13

In Django REST-Framework, was ist bei der Erstellung einer flachen, Read-Write-Serializer-Darstellung beteiligt? Die Dokumente beziehen sich auf eine "flache Darstellung" (Ende des Abschnitts http://django-rest-framework.org/api-guide/serializers.html#dealing-with-nested-objects), bieten jedoch keine Beispiele oder irgendetwas anderes als einen Vorschlag, eine RelatedField Unterklasse zu verwenden.Django-REST-Framework flach, Read-Write-Serializer

Zum Beispiel, wie eine flache Darstellung der User und UserProfile Beziehung unten zur Verfügung stellen?

# Model 
class UserProfile(models.Model): 
    user = models.OneToOneField(User) 
    favourite_number = models.IntegerField() 

# Serializer 
class UserProfileSerializer(serializers.ModelSerializer): 
    email = serialisers.EmailField(source='user.email') 
    class Meta: 
     model = UserProfile 
     fields = ['id', 'favourite_number', 'email',] 

die oben UserProfileSerializer dürfen nicht zu dem email Feld zu schreiben, aber ich hoffe, dass es die Absicht ausreichend gut zum Ausdruck bringt. Also, wie sollte ein 'flacher' Lese-Schreib-Serialisierer konstruiert werden, um ein beschreibbares email Attribut auf dem UserProfileSerializer zu erlauben? Ist dies überhaupt möglich, wenn ModelSerializer abgeleitet wird?

Danke.

Antwort

6

Mit Blick auf die Django REST Framework (DRF) Quelle habe ich mich darauf geeinigt, dass ein DRF Serializer stark an ein begleitendes Model für unserialisierende Zwecke gebunden ist. Field 's source param machen dies weniger für Serialisierung Zwecke.

, die mit im Auge und sehen Serializer als Validierung einkapseln und Verhalten speichern (zusätzlich zu ihren (un) Serialisierung Verhalten) verwendete ich zwei Serializer: eine für jede der User und Userprofile Modelle:

class UserSerializer(serializer.ModelSerializer): 
    class Meta: 
     model = User 
     fields = ['email',] 

class UserProfileSerializer(serializer.ModelSerializer): 
    email = serializers.EmailField(source='user.email') 
    class Meta: 
     model = UserProfile 
     fields = ['id', 'favourite_number', 'email',] 

Der source Parameter auf der EmailField behandelt den Serialisierungsfall angemessen (z. B. bei der Bearbeitung von GET-Anfragen). Für Deserialisierens (zB bei serivicing PUT-Anfragen) ist es notwendig, eine wenig Arbeit in der Ansicht zu tun, die Validierung kombiniert und das Verhalten der beiden Serializer sparen:

class UserProfileRetrieveUpdate(generics.GenericAPIView): 
    def get(self, request, *args, **kwargs): 
     # Only UserProfileSerializer is required to serialize data since 
     # email is populated by the 'source' param on EmailField. 
     serializer = UserProfileSerializer(
       instance=request.user.get_profile()) 
     return Response(serializer.data) 

    def put(self, request, *args, **kwargs): 
     # Both UserProfileSerializer and UserProfileSerializer are required 
     # in order to validate and save data on their associated models. 
     user_profile_serializer = UserProfileSerializer(
       instance=request.user.get_profile(), 
       data=request.DATA) 
     user_serializer = UserSerializer(
       instance=request.user, 
       data=request.DATA) 
     if user_profile_serializer.is_valid() and user_serializer.is_valid(): 
      user_profile_serializer.save() 
      user_serializer.save() 
      return Response(
        user_profile_serializer.data, status=status.HTTP_200_OK) 
     # Combine errors from both serializers. 
     errors = dict() 
     errors.update(user_profile_serializer.errors) 
     errors.update(user_serializer.errors) 
     return Response(errors, status=status.HTTP_400_BAD_REQUEST) 
+0

Paul, ist Ihre Anfrage.DATA hier ein einzelnes verschachteltes JSON-Array oder haben Sie eines für jedes der Modelle in der POST-Anfrage? (Ich versuche, etwas Ähnliches zu erreichen) – jvc26

+0

@ jvc26, mein Beispiel oben würde ein einzelnes Root-JSON-Objekt in 'request.DATA' verwenden. 'request.DATA' würde in etwa so aussehen: ' {'id': '1', 'Favoritennummer': '2', 'E-Mail': '[email protected]'} ' Soweit der Client ist Bedenken Sie, dass das JSON-Objekt eine einzelne Modellinstanz darstellt und dass es keine Kenntnis über die beiden Modelle ("User" und "UserProfile") hat, auf die es tatsächlich auf dem Server aufgeteilt ist. 'UserSerializer' und' UserProfileSerializer' werden verwendet, um Inhalte aus request.DATA für ihre zugeordneten Modelle zu extrahieren, zu validieren und zu speichern. –

2

Erstens: eine bessere Behandlung verschachtelter Schreibvorgänge ist auf dem Weg.

Zweitens: Die Serializer Relations docs sagen wir sowohl PrimaryKeyRelatedField und SlugRelatedField dass „Standardmäßig dieses Feld read-write ist ...“ - also, wenn Ihr E-Mail-Feld einzigartig war (ist?), Es könnte sein, Sie die SlugRelatedField nutzen könnten und es würde einfach funktionieren - das habe ich bisher noch nicht ausprobiert.

Drittens: Stattdessen habe ich eine einfache Field Unterklasse verwendet, die source="*" technique verwendet, um das gesamte Objekt zu akzeptieren. Von dort ziehe ich das zugehörige Feld manuell in to_native und gebe das zurück - das ist schreibgeschützt. Um zu schreiben, habe ich request.DATA in post_save überprüft und das zugehörige Objekt dort aktualisiert - Dies ist nicht automatisch, aber es funktioniert. So

, Vierter: Ein Blick auf, was Sie bereits haben, mein Ansatz (siehe oben) beträgt Ihr email Feld als schreibgeschützt markiert und dann post_save Implementierung für einen email Wert zu überprüfen und die Aktualisierung entsprechend auszuführen.

+0

Danke, @ Karton-gibson. (1) Ich freue mich auf leichtere Schreibvorgänge auf verschachtelten Darstellungen. (2) Mein Beispiel ist zur Veranschaulichung vereinfacht, aber Ihr Vorschlag ist interessant. (3 & 4) Es ist mir passiert, dass ich ein post_save() Fiedeln gemacht habe, und ich habe das in anderen Situationen benutzt, aber die Verwendung von zwei Serialisierern (meine eigene Antwort) scheint sauberer und robuster zu sein. –

0

Obwohl dies streng nicht die Frage beantworten - ich glaube, es wird dein Bedürfnis lösen. Das Problem könnte mehr in der Aufteilung von zwei Modellen liegen, um eine Entität darzustellen, als in einem Problem mit DRF.

Seit Django 1.5, können Sie einen benutzerdefinierten Benutzer machen, wenn alles, was Sie wollen einige Verfahren und zusätzlichen Felder, aber abgesehen davon, dass Sie mit dem Benutzer Django zufrieden sind, dann alles, was Sie tun müssen, ist:

class MyUser(AbstractBaseUser): favourite_number = models.IntegerField()

und in Einstellungen: AUTH_USER_MODEL = 'myapp.myuser'

(Und natürlich eine db-migration, die mit der Option db_table sehr einfach gemacht werden könnte, um auf Ihre vorhandene Benutzertabelle zu zeigen und einfach die neuen Spalten dort hinzuzufügen).

Danach haben Sie den gängigen Fall, der DRF übertrifft.

Verwandte Themen