【问题标题】:DRF: Right way to use ListCreateAPIView with nested serializerDRF:使用带有嵌套序列化程序的 ListCreateAPIView 的正确方法
【发布时间】:2020-03-30 13:45:16
【问题描述】:

我创建了一个页面,列出了所有现有供应商和适用于每个供应商的模块。在这里我需要更改模块的状态(活动或非活动),如果模块不存在,但需要使其处于活动状态然后创建它。它看起来大致是这样的。

Vendor1   module1/false  module2/true   module3/true .....
Vendor2   module1/false  module2/true   module3/true .....
.....
.....

models.py

class RfiParticipation(models.Model):
    vendor = models.ForeignKey('Vendors', models.DO_NOTHING, related_name='to_vendor')
    m = models.ForeignKey('Modules', models.DO_NOTHING, related_name='to_modules')
    active = models.BooleanField(default=False)
    user_id = models.IntegerField()
    rfi = models.ForeignKey('Rfis', models.DO_NOTHING, related_name='to_rfi', blank=True, null=True)
    timestamp = models.DateTimeField(auto_now=True)

为了显示它,我使用 ListCreateAPIView() 类和嵌套的序列化器

serializer.py

class VendorModulesListManagementSerializer(serializers.ModelSerializer):
    to_vendor = RfiParticipationSerializer(many=True)

    class Meta:
        model = Vendors
        fields = ('vendorid', 'vendor_name', 'to_vendor',)
        read_only_fields = ('vendorid', 'vendor_name', )

    def create(self, validated_data):
        validated_data = validated_data.pop('to_vendor')
        for validated_data in validated_data:
            module, created = RfiParticipation.objects.update_or_create(
                rfi=validated_data.get('rfi', None),
                vendor=validated_data.get('vendor', None),
                m=validated_data.get('m', None),
                defaults={'active': validated_data.get('active', False)})
        return module


class RfiParticipationSerializer(serializers.ModelSerializer):

    class Meta:
        model = RfiParticipation
        fields = ('pk', 'active', 'm', 'rfi', 'vendor', 'timestamp')
        read_only_fields = ('timestamp', )

views.py

class AssociateModulesWithVendorView(generics.ListCreateAPIView):
    """
    RFI: List of vendors with participated modules and modules status
    """
    permission_classes = [permissions.AllowAny, ]
    serializer_class = VendorModulesListManagementSerializer
    queryset = Vendors.objects.all()

我有一个关于在发送 POST 请求时使用 create serializer 方法的问题。

现在输入格式是这样的

{
    "to_vendor": [
            {
                "active": false,
                "m": 1,
                "rfi": "20R1",
                "vendor": 15

            }]
}

即当前代码实现的字典键是一个字典的列表。如果我从 dict 值中删除“ [] ”,我得到了

{
    "to_vendor": {
        "non_field_errors": [
            "Expected a list of items but got type \"dict\"."
        ]
    }
}

这就是为什么我需要在 create 方法中添加一个 for 循环以仅使用一个元素遍历列表的原因。我已经怀疑我在做正确的事。也许我选择了错误的实现方式?

但现在的问题是为什么我会出错?

AttributeError: Got AttributeError when attempting to get a value for field `to_vendor` on serializer `VendorModulesListManagementSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `RfiParticipation` instance.
Original exception text was: 'RfiParticipation' object has no attribute 'to_vendor'.

非常感谢您的帮助和建议!

更新

获取请求格式数据:

[
    {
        "vendorid": 15,
        "vendor_name": "Forest Gamp",
        "to_vendor": [
            {
                "pk": 35,
                "active": true,
                "m": "Sourcing",
                "rfi": "1",
                "vendor": 15,
                "timestamp": "2020-03-29T08:15:41.638427"
            },
            {
                "pk": 39,
                "active": false,
                "m": "CLM",
                "rfi": "20R1",
                "vendor": 15,
                "timestamp": "2020-03-29T09:09:03.431111"
            }
        ]
    },
    {
        "vendorid": 16,
        "vendor_name": "Test21fd2",
        "to_vendor": [
            {
                "pk": 41,
                "active": false,
                "m": "SA",
                "rfi": "20R1",
                "vendor": 16,
                "timestamp": "2020-03-30T11:05:16.106412"
            },
            {
                "pk": 40,
                "active": false,
                "m": "CLM",
                "rfi": "20R1",
                "vendor": 16,
                "timestamp": "2020-03-30T10:40:52.799763"
            }
        ]
    }
]

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    它尝试访问您的模型RfiParticipation 上的to_vendor,并抱怨此属性不存在。 related_name 指的是您的 Vendors 模型上的反向关系。这样您就可以执行Vendors.to_vendor.all() 之类的操作并获取所有RfiParticipation 实例。

    当它尝试验证您的输入数据时会发生这种情况,甚至在它到达您的序列化程序的创建函数之前。

    如果您尝试创建新的RfiParticipation,为什么要使用定义queryset = Vendors.objects.all() 的视图?

    而是定义一个负责创建RfiParticipation 的视图,因为您似乎已经引用了Vendors。如果我理解正确,您要做的基本上是批量创建RfiParticipation,因此请创建一个指向这些的视图。

    【讨论】:

    • 奇怪的是,当我发出GET 请求时,到_vendor 的连接有效。一旦我发送POST,就会发生错误。我使用 Vendor 是因为我需要添加到问题更新中的某个嵌套结构。如果我使用RfiParticipation 对象,我无法得到它,或者我只是不知道该怎么做。 Bulc_upload 并不是我所需要的。每次单击复选框时都会更新或创建模块。也就是说,没有设置多个复选框,然后一个请求发送所有复选框。一个复选框是对数据库的一个请求。
    【解决方案2】:

    我已经完成了你的实现,我想你可能在views.py中使用了错误的泛型类继承

    你应该尝试替换

    class AssociateModulesWithVendorView(generics.ListCreateAPIView):
        permission_classes = [permissions.AllowAny, ]
        serializer_class = VendorModulesListManagementSerializer
        queryset = Vendors.objects.all()
    

    class AssociateModulesWithVendorView(generics.CreateAPIView, generics.ListAPIView):
        permission_classes = [permissions.AllowAny, ]
        serializer_class = VendorModulesListManagementSerializer
        queryset = Vendors.objects.all()
    

    您可以查看以下链接以供参考: 创建APIView:https://www.django-rest-framework.org/api-guide/generic-views/#createmodelmixin ListCreateAPIView : https://www.django-rest-framework.org/api-guide/generic-views/#listcreateapiview

    【讨论】:

    • 我认为使用你的版本和我的没有什么不同。这是相同的功能。我为 POST 和 GET 方法使用不同的序列化程序解决了我的问题。
    猜你喜欢
    • 1970-01-01
    • 2022-07-11
    • 2019-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    • 2021-05-21
    相关资源
    最近更新 更多