Django REST Framework POST嵌套对象 [英] Django REST Framework POST nested objects

查看:127
本文介绍了Django REST Framework POST嵌套对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在使用 Django Rest Framework 遇到一个小问题.我正在尝试发布其中包含嵌套对象的对象.

I'm facing a little problem right now with Django Rest Framework. I'm trying to post an object with nested objects in it.

这是我的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:

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)

这是 Postman 的回复:

我已经在这里阅读了有关此问题的一些帖子,但我仍然坚持使用它.我试图以几种方式修复它,但它仍然返回"This field is required.".

I've read some posts here about this problem but I'm still stuck with it. I've tried to fix it in several ways but it is still returning "This field is required.".

推荐答案

您正在处理

You are dealing with the problem of nested serialization. Please read the linked documentation before proceeding.

您的问题涉及DRF中的复杂问题,因此需要进行一些解释和讨论,以了解序列化程序和视图集的工作方式.

Your question relates to a complex area of problems in DRF and hence requires some explanation and discussion for understanding how serializers and viewsets work.

我将讨论通过对不同的HTTP方法使用不同的数据表示方式来通过同一端点表示您的SubjectClass数据的问题,因为当人们希望以嵌套方式表示其数据时,这通常是一个问题.格式;他们希望为用户界面提供足够的信息以供清洁使用,例如通过下拉选择器.

I will discuss the problem of representing your Subject and Class data via the same endpoint by using a different representation of data for different HTTP methods, because this is commonly the problem when people wish to represent their data in nested formats; they wish to provide their user interfaces enough information for clean use, e.g. through the dropdown selectors.

默认情况下,Django和Django REST Framework(DRF)通过其主键引用相关对象(您的SubjectClass).默认情况下,这些是使用Django自动递增的整数键.如果要通过其他方式引用它们,则必须为此编写重写.有几种不同的选择.

By default Django and Django REST Framework (DRF) refer to related objects (your Subject and Class) by their primary keys. These, by default, are auto-incrementing integer keys with Django. If you want to refer to them by other ways you have to write overrides for this. There are a few different options.

  1. 第一个选择是专业化您的创建和更新逻辑:通过其他一些属性来引用您的课程,并手动编写用于自己创建的查找,或将您要引用的键设置为 主键 .您可以将类的名称,UUID或任何其他属性设置为主数据库键,只要它是唯一的 单个字段 (我之所以这样说是因为您目前正在使用包含复合词(数字,字母)搜索词).例如,您可以在create视图方法中覆盖相关的对象查找(对于POST),但是随后您也必须在update视图方法中处理类似的查找(对于PUT和PATCH).
  2. 第二个,我认为更可取的选择是专业化对象的表示形式:通常通过主键引用您的类,并创建一个用于读取对象的序列化器 一个用于创建和更新它的.这可以通过序列化器类的继承和覆盖您的表示来轻松实现.在您的POST,PUT,PATCH等请求中使用主键来更新您的类引用和外键.
  1. First option is to specialize your creation and update logic: Refer to your class via some other attribute(s) and manually write the lookups for creation yourself, or set the key you are referring through as the primary key of your class. You can set your class' name, UUID or any other attribute as the primary database key, as long as it is a unique, single field (the reason I am mentioning this is because you are, at the moment, looking your Class models up with a composite search that consists of a composite (number, letter) search term). You can override related object lookups in your create view method (for POST), for example, but then you will have to handle similar lookups in your update view method as well (for PUT and PATCH).
  2. Second, in my opinion the preferable option, is to specialize your object representations: Refer to your classes normally via primary key and create one serializer for reading the object and one for creating and updating it. This can be easily achieved by serializer class inheritance and overriding your representations. Use the primary key in your POST, PUT, PATCH, etc. requests to update your class references and foreign keys.


选项1:在创建和更新时使用任意属性查找类和主题:

将嵌套的类序列化器设置为只读:

Set your nested class serializers as read-only:

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

覆盖视图的创建,以在自由格式属性上查找相关类.另外,请查看 DRF如何实现此目标使用mixins .您还必须重写update方法以正确处理这些问题,并且如果采用以下方法,除了PUT(更新)之外,还应考虑到PATCH(部分更新)的支持:

Override your view's create to look up the related classes on free-form attributes. Also, check out how DRF implements this with mixins. You will also have to override your update method to handle these correctly, and take into account PATCH (partial update) support in addition to PUT (update) if you take this route:

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:

First define a default ModelSerializer you wish to use for normal operations (POST, PUT, PATCH):

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

然后用您想要给它们的表示形式覆盖必填字段,以读取数据(GET):

Then override the necessary fields with the kind of representation you want to give to them for reading the data (GET):

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

然后 指定序列化程序希望对ViewSet使用不同的操作 .在这里,我们返回嵌套的Subject和Class数据以进行读取操作,但仅将其主键用于更新操作(更简单):

Then specify the serializer you wish to use for different operations for your ViewSet. Here we return the nested Subject and Class data for read operations, but only use their primary keys for update operations (far simpler):

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之上仅包含3行手写代码(get_serializer_class实现).只需让框架的逻辑为您找出对象的表示形式以及对象的创建和更新即可.

As you can see, option 2 seems fairly less complex and error-prone, containing only 3 lines of hand-written code on top of DRF (the get_serializer_class implementation). Just let the framework's logic figure out the representations and creation and updates of objects for you.

我看到了许多其他方法,但是到目前为止,这些方法产生的代码最少,可以为我维护,并以干净的方式利用DRF的设计.

I have seen many other approaches, but so far these have been the ones that have produced the least code to maintain for me and take advantage of the design of DRF in a clean manner.

这篇关于Django REST Framework POST嵌套对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆