【问题标题】:How to programatically call a class based view from another class based view in drf while modifying the request data before call?在调用前修改请求数据时,如何以编程方式从 drf 中的另一个基于类的视图调用基于类的视图?
【发布时间】:2021-08-01 11:52:59
【问题描述】:

我正在尝试从 Model2 的另一个基于类的视图中为 Model1 调用现有的基于类的视图。我从前端收到的 JSON 数据包含 Model1 和 Model 2 的数据,格式如下:

request.data 格式:

{
    "model1": {
        "key1": "val1"
    },
    "model2": {
        "key2": "val2"
    }
}

在我的 Model1View 中,我正在做一些额外的事情,每当我必须将一些新数据保存到 Model1 时,我都想做这些事情。所以我打算使用相同的视图。(这些额外的事情包括创建其他模型实例等)。

目的:在调用Model2View时,我想将Model1的请求数据分离出来,先调用Model1View。一旦我从这个视图中成功获得响应,我想使用 model1_id(Model1 的主键)来添加请求数据并保存 Model2。

问题:使用此视图进行测试时,出现错误:

django.http.request.RawPostDataException: You cannot access body after reading from request's data stream

型号:

from django.db import models

class Model1(models.Model):
    model1_id = models.AutoField(primary_key=True)
    key1 = models.CharField(max_length=20, blank=False, null=False)

class Model2(models.Model):
    model2_id = models.AutoField(primary_key=True)
    key2 = models.CharField(max_length=20, blank=False, null=False)
    model1_id = models.ForeignKey('Model1', blank=False, null=False, on_delete=models.PROTECT)

序列化器:

from rest_framework import serializers
from .models import *

class Model1Serializer(serializers.ModelSerializer):
    class Meta:
        model = Model1
        fields = '__all__'

class Model2Serializer(serializers.ModelSerializer):
    class Meta:
        model = Model2
        fields = '__all__'

观看次数:

from django.shortcuts import render

from rest_framework import generics
from rest_framework.response import Response
from rest_framework import status

from .models import *
from .serializers import *

class Model1View(generics.ListCreateAPIView):
    queryset = Model1.objects.all()
    serializer_class = Model1Serializer

    def post(self, request, *args, **kwargs):
        if ('data' in kwargs):
            request.data.update(kwargs.get('data'))
        # Call super to save the data in Model1
        response = super().post(request, *args, **kwargs)
        # Do some additional things
        return Response(response.data, status.HTTP_201_CREATED)

class Model2View(generics.ListCreateAPIView):
    queryset = Model2.objects.all()
    serializer_class = Model2Serializer

    def post(self, request, *args, **kwargs):
        # Get Model1 data and Model2 data from request
        model1_data = request.data.pop("model1")
        model2_data = request.data.pop("model2")

        # Call Model1View to create Model1 Instance
        model1_response = Model1View.as_view()(request._request, data=model1_data)
        # Save Model1 Instance ID to data and then save Model2 Instance
        model2_data["model1_id"] = model1_response['model1_id']
        model2_serializer = Model2Serializer(data=model2_data)
        if model2_serializer.is_valid():
            model2_serializer.save()
            return Response(model2_serializer.data, status.HTTP_201_CREATED)
        return Response(model2_serializer.errors, status.HTTP_400_BAD_REQUEST)

一些试验:删除request.data.pop 语句可以解决错误,但是,如何从request 中提取数据?如果我在调用 Model1View 之前与 request 对象交互,无论是通过复制它还是更新数据等,我最终都会遇到同样的错误。

【问题讨论】:

    标签: django django-rest-framework django-views


    【解决方案1】:

    对于这个问题陈述,您需要一个视图,然后在自定义序列化程序中分别序列化模型 1 和模型 2。在 model1 和 model2 序列化程序中,您可以执行您打算执行的自定义。像这样的

    class Model1Serializer(serializers.ModelSerializer):
        class Meta:
            model = Model1
            fields = '__all__'
    
    class Model2Serializer(serializers.ModelSerializer):
        class Meta:
            model = Model2
            fields = '__all__'
    
    class ModelSerializer(serializers.ModelSerializer):
        model1 = Model1Serializer(many=False, required=True)
        model2 = Model2Serializer(many=False, required=True)
    

    在视图类中使用 ModelSerializer 来序列化 model1 和 model2 实例。

    【讨论】:

    • 感谢您的意见。单个序列化器将通过单个视图完成将请求数据发布到 2 个不同模型的问题。但是,我也打算在 Model1View 中使用“做一些额外的事情”代码,而不必重复代码。我怎样才能做到这一点?
    • 在您的代码中,我看到您在 createApiView 发布函数之后编写了“做一些额外的事情”。您可以使用 post save 信号调度程序来执行相同的操作。如果在将对象保存到数据库之前可以完成“其他事情”,您可以通过序列化程序中的验证功能或模型中的预保存信号调度程序。希望清除它。关于这一点,在 django 中拥有胖模型和瘦视图始终是最佳实践。所以尽量减少视图类中的操作数。
    • 所有@lazycoderboi 都是正确的。这显然是序列化器的工作,或者这些操作可以包含在模型的逻辑中。
    • @VasanthS 再次感谢您的意见。我之前不知道信号调度器。我现在正在了解它们。另外,您能否帮助我解决与上述问题相关的另一个疑问。如果我为 2 个模型组合序列化程序,那么在发布新请求时,Model2 将如何获取 model1_id。由于我在单个 JSON 请求中传递 Model1 和 Model2 的数据,有没有办法创建 Model1 实例,然后使用 model1_id 使用单个序列化器方法创建 Model2 实例。如果这很明显,我很抱歉,但我对 drf 和一般编码不是很有经验。
    • 如果您覆盖 ModelSerializer 中的 create() 函数,您将可以访问 Model1 和 Model2 的值。换句话说,您可以在主序列化程序 create() 函数中创建 model1 和 model2 数据库条目。
    猜你喜欢
    • 2013-02-04
    • 2022-11-20
    • 1970-01-01
    • 1970-01-01
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多