【问题标题】:Writable Double-nested serializers with Django Rest Framework带有 Django Rest 框架的可写双嵌套序列化器
【发布时间】:2016-09-03 22:30:55
【问题描述】:

我有 1 个或多个 product_invoice 的发票,有些 product_invoice 可以有 1 个 product_order(因此在 ProductOrder 中,productinvoiceid 应该是 OneToOne relashionship,但它对我的问题并不重要)。

我可以获取2层的数据,但是无法创建,出现这个错误:

TypeError: 'orderinfo' 是此函数的无效关键字参数。

但如果我在ProductInvoiceSerializer 中删除orderinfo,我就可以创建发票和相关的Product_Invoice

我做错了什么?

django:1.9.3,DRF:3.3.3

[
{
    "invoiceid": 43,
    "stocklocationid": 1,
    "invoicecode": "OBR00040",
    "invoicedate": "2016-05-07",
    "totalamount": 500000,
    "buypricetotal": 125000,
    "discount": 0,
    "ppnamount": 50000,
    "ispaid": true,
    "deposit": 0,
    "emailaddress": "",
    "customername": "",
    "invoicenote": "",
    "isorder": false,
    "deliverydate": null,
    "products": [
        {
            "productinvoiceid": 48,
            "productid": 1,
            "quantity": 1,
            "buyingprice": 200000,
            "saleprice": 100000,
            "orderinfo": [
                {
                    "productorderid": 2,
                    "note": "",
                    "isstock": false,
                    "stocklocationid": 1,
                    "ispickedup": 0
                }
            ]
        }
    ]
},

我的模特:

class Invoice(models.Model):
    invoiceid = models.AutoField(db_column='InvoiceID', primary_key=True)
    invoicecode = models.CharField(db_column='InvoiceCode', unique=True, max_length=8)

    class Meta:
        managed = False
        db_table = 'invoice'

class ProductInvoice(models.Model):
    productinvoiceid = models.AutoField(db_column='ProductInvoiceID', primary_key=True)
    productid = models.ForeignKey(Product, models.DO_NOTHING, db_column='ProductID')
    invoiceid = models.ForeignKey(Invoice, models.DO_NOTHING, db_column='InvoiceID', related_name='products')
    quantity = models.IntegerField(db_column='Quantity', verbose_name='Quantity')

class Meta:
    managed = False
    db_table = 'product_invoice'

class ProductOrder(models.Model):
    productorderid = models.AutoField(db_column='ProductOrderID', primary_key=True)
    productinvoiceid = models.ForeignKey(ProductInvoice, models.DO_NOTHING, db_column='ProductInvoiceID', related_name='orderinfo')
    isstock = models.BooleanField(db_column='Stock', verbose_name = 'Is stock ?')
    isreadytopick = models.IntegerField(db_column='ReadyToPick')
    ispickedup = models.IntegerField(db_column='PickedUp', verbose_name = 'Already picked-up ?')

class Meta:
    managed = False
    db_table = 'product_order'

我的序列化器:

class ProductOrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProductOrder
        fields = ('productorderid','note','isstock','stocklocationid','ispickedup')

class ProductInvoiceSerializer(serializers.ModelSerializer):
    orderinfo = ProductOrderSerializer(many=True)

    class Meta:
        model = ProductInvoice
        fields = ('productinvoiceid', 'productid', 'quantity', 'buyingprice', 'saleprice', 'orderinfo')
    #fields = ('productinvoiceid', 'productid', 'quantity', 'buyingprice', 'saleprice')

    def create(self, validated_data):
        ordersinfo_data = validated_data.pop('orderinfo')
        product_invoice = ProductInvoice.objects.create(**validated_data)
        for orderinfo_data in ordersinfo_data:
            ProductOrder.objects.create(productinvoiceid=product_invoice, **orderinfo_data)
        return product_invoice

class InvoiceSerializer(serializers.ModelSerializer):
    products = ProductInvoiceSerializer(many=True)

    class Meta:
        model = Invoice
        fields = ('invoiceid', 'stocklocationid', 'invoicecode','invoicedate','totalamount','buypricetotal','discount','ppnamount','ispaid','deposit','emailaddress','customername','invoicenote','isorder','deliverydate','products')

    def create(self, validated_data):
        products_data = validated_data.pop('products')
        invoice = Invoice.objects.create(**validated_data)
        for product_data in products_data:
            #product_data.invoiceid = invoice.invoiceid
            ProductInvoice.objects.create(invoiceid=invoice, **product_data)
        return invoice

2016 年 5 月 11 日更新

view.py

@api_view(['GET', 'POST'])
def invoice_list(request):

if request.method == 'GET':
    invoices = Invoice.objects.all()
    serializer = InvoiceSerializer(invoices, many=True)
    return Response(serializer.data)

elif request.method == 'POST':
    serializer = InvoiceSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return JSONResponse(serializer.data, status=status.HTTP_201_CREATED, )
    return JSONResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

【问题讨论】:

    标签: django serialization nested django-rest-framework writable


    【解决方案1】:

    由于您在ProductInvoice.objects.create(invoiceid=invoice, **product_data)ProductInvoice 手动创建ProductInvoice 对象,因此模型没有任何名为orderinfo 的字段。

    既然你正在使用

    orderinfo = ProductOrderSerializer(many=True)
    

    ProductInvoice 需要有一个ManyToMany 字段来存储orderinfo

    因此你的模型应该是这样的:

    class ProductInvoice(models.Model):
        productinvoiceid = models.AutoField(db_column='ProductInvoiceID', primary_key=True)
        productid = models.ForeignKey(Product, models.DO_NOTHING, db_column='ProductID')
        invoiceid = models.ForeignKey(Invoice, models.DO_NOTHING, db_column='InvoiceID', related_name='products')
        quantity = models.IntegerField(db_column='Quantity', verbose_name='Quantity')
        orderinfo = models.ManyToManyField(ProductOrder)
    

    您应该使用ProductInvoiceSerializer 创建ProductInvoice 对象:

    def create(self, validated_data):
            products_data = validated_data.pop('products')
            invoice = Invoice.objects.create(**validated_data)
            for product_data in products_data:
                #product_data.invoiceid = invoice.invoiceid
                data = product_data
                data['invoiceid'] = invoice
                serializer = ProductInvoiceSerializer(data=data)
                if serializer.is_valid(raise_exception=True):
                    serializer.save()
            return invoice
    

    【讨论】:

    • 不确定我是否同意您的回复。在 InvoiceSerializer 中,我使用的是 products = ProductInvoiceSerializer(many=True),但这并不意味着我的 Invoice 模型中需要 products 字段。
    • @ALaplante 因为您正试图在ProductInvoice.objects.create(invoiceid=invoice, **product_data) 此处创建ProductInvoice 对象,而您无论如何都没有使用ProductInvoiceSerializer。这就是为什么您需要在ProductInvoice 模型中使用orderinfo 或使用ProductInvoiceSerializer。无论如何尝试更新的答案。
    • 我确实喜欢您更新后的答案,但我收到此错误消息:AssertionError: You cannot call '.save()' after accessing 'serializer.data'.If you need to access data before committing to the database then inspect 'serializer.validated_data' instead.。当我刚拥有 InvoiceSerializer 和 ProductInvoiceSerializer 时,我也有同样的情况,但我按照 DRF API Nested 指令修复了它。我不确定 DRF 是否允许在另一个序列化程序中调用序列化程序。关于你的回答,我只需要data['productid'] = product_data['productid'].pk
    • @ALaplante,你能发布你是如何做到这一点的完整代码吗?因为我看不到我们在哪里访问'serializer.data'
    • 尝试将对象创建部分从序列化程序移动到视图。
    猜你喜欢
    • 2019-10-13
    • 2018-12-13
    • 2021-06-23
    • 2014-07-27
    • 2015-03-20
    • 1970-01-01
    • 2023-01-10
    • 2016-08-01
    • 2014-07-26
    相关资源
    最近更新 更多