Kiedy używać funkcji create () Serializera i create () perform_create () modelu ModelViewset

103

Chcę wyjaśnić podaną dokumentację django-rest-frameworkdotyczącą tworzenia obiektu modelowego. Jak dotąd odkryłem, że istnieją 3 podejścia do radzenia sobie z takimi zdarzeniami.

  1. Metoda serializatora create(). Oto dokumentacja

    class CommentSerializer(serializers.Serializer):
    
        def create(self, validated_data):
            return Comment.objects.create(**validated_data)
    
  2. Metoda ModelViewset create(). Dokumentacja

    class AccountViewSet(viewsets.ModelViewSet):
    
        queryset = Account.objects.all()
        serializer_class = AccountSerializer
        permission_classes = [IsAccountAdminOrReadOnly]
    
  3. Metoda ModelViewset perform_create(). Dokumentacja

    class SnippetViewSet(viewsets.ModelViewSet):
    
        def perform_create(self, serializer):
            serializer.save(owner=self.request.user)
    

Te trzy podejścia są ważne w zależności od środowiska aplikacji.

Ale KIEDY musimy użyć każdej create() / perform_create()funkcji ??. Z drugiej strony znalazłem konto, na którym zostały wywołane dwie metody tworzenia dla pojedynczego żądania wpisu modelviewset create()i serializera create().

Mam nadzieję, że ktoś podzieli się swoją wiedzą, aby wyjaśnić, co z pewnością będzie bardzo pomocne w moim procesie rozwoju.

Roel
źródło

Odpowiedzi:

132
  1. Możesz użyć, create(self, validated_data)aby dodać dodatkowe szczegóły do ​​obiektu przed zapisaniem wartości AND „prod” w każdej zmiennej modelu, tak jak to się **validated_datadzieje. Idealnie mówiąc, chcesz wykonać tę formę „szturchania” tylko w JEDNYM miejscu, aby createmetoda w Twoim przypadku CommentSerializerbyła najlepsza. Oprócz tego możesz również zadzwonić do zewnętrznych interfejsów API, aby utworzyć konta użytkowników po ich stronie tuż przed zapisaniem kont we własnej bazie danych. Tej createfunkcji należy używać w połączeniu z ModelViewSet. Zawsze myśl - „Cienkie widoki, grube serializatory”.

Przykład:

def create(self, validated_data):
    email = validated_data.get("email", None)
    validated.pop("email") 
    # Now you have a clean valid email string 
    # You might want to call an external API or modify another table
    # (eg. keep track of number of accounts registered.) or even
    # make changes to the email format.

    # Once you are done, create the instance with the validated data
    return models.YourModel.objects.create(email=email, **validated_data)
  1. create(self, request, *args, **kwargs)Funkcję w ModelViewSetzdefiniowano w CreateModelMixinklasy, który jest dominującym ModelViewSet. CreateModelMixinjego główne funkcje są następujące:

    from rest_framework import status
    from rest_framework.response import Response
    
    
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
    def perform_create(self, serializer):
        serializer.save()
    

Jak widać, powyższa createfunkcja dba o wywołanie walidacji na twoim serializatorze i wygenerowanie poprawnej odpowiedzi. Piękno kryjące się za tym polega na tym, że możesz teraz wyizolować logikę aplikacji i NIE martwić się o przyziemne i powtarzające się wywołania walidacyjne i obsługę wyników odpowiedzi :). Działa to całkiem dobrze w połączeniu ze create(self, validated_data)znalezionym w serializatorze (gdzie może znajdować się określona logika aplikacji).

  1. Teraz możesz zapytać, dlaczego mamy oddzielną perform_create(self, serializer)funkcję z tylko jedną linią kodu!?!? Cóż, głównym powodem tego jest umożliwienie dostosowywania podczas wywoływania savefunkcji. Możesz chcieć dostarczyć dodatkowe dane przed wywołaniem save (tak jakserializer.save(owner=self.request.user) i gdybyśmy nie mieli perform_create(self, serializer), musiałbyś zastąpić to, create(self, request, *args, **kwargs)a to po prostu przeczy celowi, aby mixiny wykonywały ciężką i nudną pracę.

Mam nadzieję że to pomoże!

Apoorv Kansal
źródło
Cześć! Dziękujemy za podzielenie się wiedzą! Co do create(self, validated_data)serializatora, to znaczy, że skupia się na logice walidacji danych? a co więcej, może pomóc przywrócić dane danego serializatora z powrotem do odpowiedzi, prawda?
Roel
1
Nie, więc w tym momencie przeszedłeś już całą weryfikację. Mówię o tym, jak możesz chcieć dostosować zweryfikowane dane tuż przed zapisaniem ich w bazie danych. Podam przykład w mojej odpowiedzi.
Apoorv Kansal
1
Bez obaw - dodałem tylko przykład, aby nadać szerszy kontekst.
Apoorv Kansal
1
Tak, to jest ostatnia linia, która zapisze twój obiekt w bazie danych
Apoorv Kansal
1
Zatem createfunkcja w samym serializatorze jest wywoływana tylko wtedy, gdy to zrobisz serializer.save(). W swojej create(self, request)funkcji wewnętrznej ( AccountViewSet), jesteś nie dzwoni serializer.save()w ogóle, a zatem jedynym tworzenie instancji dzieje się z tej rozmowy: Account.objects.create_user(**serializer.validated_data).
Apoorv Kansal