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

查看:56
本文介绍了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)

这是来自邮递员的回复:

我已经在这里阅读了一些关于这个问题的帖子,但我仍然被它困住了.我已经尝试通过多种方式修复它,但它仍然返回 "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 或任何其他属性设置为主数据库键,只要它是唯一的,单个字段(我提到这一点的原因是因为您目前正在通过复合搜索查找您的 Class 模型,其中包含复合(数字、字母)搜索词).例如,您可以在 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 如何实现这一点与混合.您还必须覆盖您的 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 似乎不那么复杂且容易出错,它仅包含 3 行基于 DRF(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天全站免登陆