Django Rest Framework POST objetos nesteds

Estoy enfrentando un pequeño problema en este momento con Django Rest Framework . Estoy tratando de publicar un objeto con objetos nesteds en él.

Aquí están mis serializers.py :

 class ClassSerializer(serializers.ModelSerializer): class Meta: model = Class fields = ('number', 'letter') class SubjectSerializer(serializers.ModelSerializer): class Meta: model = Subject fields = ('title',) class ExamSerializer(serializers.ModelSerializer): subject = SubjectSerializer() clazz = ClassSerializer() class Meta: model = Exam fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details') depth = 1 def create(self, validated_data): return Exam.objects.create(**validated_data) def update(self, instance, validated_data): instance.__dict__.update(**validated_data) instance.save() return instance 

Y create() desde views.py :

 def create(self, request): serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) return Response(serializer.validated_data, status=status.HTTP_201_CREATED) 

Y aquí está la respuesta de Postman : Respuesta del cartero

He leído algunas publicaciones aquí sobre este problema, pero todavía estoy atascado con él. He intentado arreglarlo de varias maneras, pero sigue apareciendo "This field is required." .

Usted está tratando con el problema de la serialización anidada . Por favor, lea la documentación vinculada antes de continuar.

Su pregunta se relaciona con un área compleja de problemas en DRF y, por lo tanto, requiere una explicación y discusión para comprender cómo funcionan los serializadores y los conjuntos de vistas.

Discutiré el problema de representar sus datos de Subject y Class través del mismo punto final utilizando una representación diferente de datos para diferentes métodos HTTP, porque este suele ser el problema cuando las personas desean representar sus datos en formatos nesteds; desean proporcionar a sus interfaces de usuario información suficiente para un uso limpio, por ejemplo, a través de los selectores desplegables.

Por defecto, el marco de REST (DRF) de Django y Django se refiere a los objetos relacionados (su Subject y Class ) por sus claves principales . Estas, de manera predeterminada, son claves enteras de incremento automático con Django. Si desea referirse a ellos por otras formas, tiene que escribir anulaciones para esto. Hay algunas opciones diferentes.

  1. La primera opción es especializar su creación y actualizar la lógica: consulte su clase a través de algunos otros atributos y escriba manualmente las búsquedas de creación, o establezca la clave a la que se refiere como la clave principal de su clase. Puede configurar el nombre de su clase, UUID o cualquier otro atributo como la clave principal de la base de datos, siempre y cuando sea un campo único y único (la razón por la que menciono esto es porque, en este momento, está buscando sus modelos de Class con una búsqueda compuesta que consiste en un término de búsqueda compuesto (número, letra). Puede anular las búsquedas de objetos relacionados en su método de create vista (para POST), por ejemplo, pero también tendrá que manejar búsquedas similares en su método de vista de update (para PUT y PATCH).
  2. En segundo lugar, en mi opinión, la opción preferible es especializar sus representaciones de objetos: consulte sus clases normalmente a través de la clave principal y cree un serializador para leer el objeto y otro para crearlo y actualizarlo . Esto puede lograrse fácilmente mediante la herencia de clase de serializador y anulando sus representaciones. Use la clave principal en sus solicitudes POST, PUT, PATCH, etc. para actualizar sus referencias de clase y claves externas.

Opción 1: Look Class y Subject up con un atributo arbitrario en crear y actualizar:

Establezca sus serializadores de clase nesteds como de solo lectura:

 class ExamSerializer(serializers.ModelSerializer): subject = SubjectSerializer(read_only=True) clazz = ClassSerializer(read_only=True) 

Anule la creación de su vista para buscar las clases relacionadas en los atributos de forma libre. Además, vea cómo DRF implementa esto con los mixins . También deberá anular su método de update para manejar estos correctamente, y tener en cuenta el soporte PATCH (actualización parcial) además de PUT (actualización) si toma esta ruta:

 def create(self, request): # Look up objects by arbitrary attributes. # You can check here if your students are participating # the classes and have taken the subjects they sign up for. subject = get_object_or_404(Subject, title=request.data.get('subject')) clazz = get_object_or_404( Class, number=request.data.get('clazz_number') letter=request.data.get('clazz_letter') ) serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save(clazz=clazz, subject=subject) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) 

Opción 2: especialice sus serializadores para leer y escribir y use claves primarias; Este es el enfoque idiomático:

Primero, defina un ModelSerializer predeterminado que desee usar para las operaciones normales (POST, PUT, PATCH):

 class ExamSerializer(serializers.ModelSerializer) class Meta: model = Exam fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details') 

Luego, anule los campos necesarios con el tipo de representación que desea darles para leer los datos (GET):

 class ExamReadSerializer(ExamSerializer): subject = SubjectSerializer(read_only=True) clazz = ClassSerializer(read_only=True) 

Luego especifique el serializador que desea usar para diferentes operaciones para su ViewSet. Aquí devolvemos los datos nesteds del Sujeto y la Clase para las operaciones de lectura, pero solo usamos sus claves principales para las operaciones de actualización (mucho más simples):

 class ExamViewSet(viewsets.ModelViewSet): queryset = Exam.objects.all() def get_serializer_class(self): # Define your HTTP method-to-serializer mapping freely. # This also works with CoreAPI and Swagger documentation, # which produces clean and readable API documentation, # so I have chosen to believe this is the way the # Django REST Framework author intended things to work: if self.request.method in ['GET']: # Since the ReadSerializer does nested lookups # in multiple tables, only use it when necessary return ExamReadSerializer return ExamSerializer 

Como puede ver, la opción 2 parece bastante menos compleja y propensa a errores, ya que contiene solo 3 líneas de código manuscrito en la parte superior de DRF (la implementación de get_serializer_class). Simplemente deje que la lógica del marco resuelva las representaciones, la creación y las actualizaciones de objetos para usted.

He visto muchos otros enfoques, pero hasta ahora estos han sido los que han producido el menor código para mantenerme y aprovechar el diseño de DRF de manera limpia.

Un enfoque más fácil sin hacer clases adicionales es tomar la serialización en ti mismo:

 class ExamSerializer(serializers.ModelSerializer): class Meta: model = Exam fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details') def to_representation(self, instance): data = super().to_representation(instance) data['subject'] = SubjectSerializer( Subject.objects.get(pk=data['subject'])).data data['clazz'] = ClassSerializer( Class.objects.get(pk=data['clazz'])).data return data 

Tuve el mismo problema al intentar publicar un objeto JSON nested en DRF (Django Rest Framework).

Una vez que haya configurado correctamente la escritura de serializadores nesteds (consulte la documentación en serializadores nesteds grabables ), puede probar que funciona utilizando la API navegable y la publicación / colocación de datos allí. Si eso funciona, y aún recibe errores de ” Este campo es obligatorio ” en sus modelos nesteds al publicar / colocar objetos JSON, es posible que deba establecer el tipo de contenido de su solicitud.

Esta respuesta proporcionó la solución que necesitaba, y se resume a continuación.

 $.ajax ({ // Other parameters eg url, type data: JSON.stringify(data), dataType: "json", contentType: "application/json; charset=utf-8", }); 

Necesitaba establecer el “tipo de contenido”, así como “alinear” mi objeto js.