我现在正面临 Django Rest Framework的 一个小问题。我正在尝试发布一个带有嵌套对象的对象。
这是我的serializers.py:
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
而create()来自views.py:
create()
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)
这是 邮递员 的回复:
我在这里阅读了一些有关此问题的文章,但我仍然坚持下去。我已尝试通过多种方式对其进行修复,但仍在恢复中"This field is required."。
"This field is required."
您正在处理 嵌套序列化 的问题。在继续之前,请阅读链接的文档。
您的问题涉及DRF中的复杂问题,因此需要进行一些解释和讨论,以了解序列化程序和视图集的工作方式。
我将讨论代表您的问题Subject,并Class通过使用不同的HTTP方法数据的不同表示通过相同的端点数据,因为这通常是当人们希望代表自己的嵌套格式的数据的问题; 他们希望为用户界面提供足够的信息以供清洁使用,例如通过下拉选择器。
Subject
Class
默认情况下,Django和Django REST Framework(DRF )通过其 主键 引用相关对象(您Subject和Class)。默认情况下,这些是使用Django自动递增的整数键。如果要通过其他方式引用它们,则必须为此编写重写。有几种不同的选择。 __
create
update
选项1:在创建和更新中使用任意属性查找类和主题:
将嵌套的类序列化器设置为只读:
class ExamSerializer(serializers.ModelSerializer): subject = SubjectSerializer(read_only=True) clazz = ClassSerializer(read_only=True)
覆盖视图的创建,以在自由格式属性上查找相关类。此外,请检查 DRF如何通过mixins实现此功能 。您还必须重写update方法以正确处理这些问题,并且如果采取以下方法PATCH,除了PUT(更新)之外,还应考虑(部分更新)支持:
PATCH
PUT
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)
选项2:专用于序列化器进行读写和使用主键; 这是惯用的方法:
首先定义一个您希望用于常规操作(POST,PUT,PATCH)的默认ModelSerializer:
class ExamSerializer(serializers.ModelSerializer) class Meta: model = Exam fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details')
然后,使用您要提供给它们的表示形式来覆盖必填字段,以读取数据(GET):
class ExamReadSerializer(ExamSerializer): subject = SubjectSerializer(read_only=True) clazz = ClassSerializer(read_only=True)
然后为ViewSet 指定要用于不同操作的序列化 器。在这里,我们返回嵌套的Subject和Class数据以进行读取操作,但仅将其主键用于更新操作(更简单):
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
如您所见,选项2似乎不太复杂且不易出错,在DRF(get_serializer_class实现)上仅包含3行手写代码。只需让框架的逻辑为您弄清楚对象的表示形式以及对象的创建和更新。
我看到了许多其他方法,但是到目前为止,这些方法为我生成的代码最少,并且可以以干净的方式利用DRF的设计。