【问题标题】:Using DRF serializer to add custom details to multiple models使用 DRF 序列化器将自定义详细信息添加到多个模型
【发布时间】:2021-01-17 17:31:15
【问题描述】:

编辑(添加实际模型和细节)

**

好的,所以这个问题会有点冗长,实际上有四个模型。每当我创建发票时,我需要添加来自我使用循环的前端的发票详细信息并将所有这些添加到我的数据库中。下一部分是在 Ledger 表和 ProductQuantity 表中创建一条记录,为此我必须重复我在 InvoiceCreateAndGetByIdOnly中编写的代码>。我有许多更新多个模型的视图,并且我重复了很多代码。如您所见,serializers.py 非常薄,而 views.py 非常厚。就像 InvoiceCreate 视图一样,我还有其他视图可以更新 LedgerProductQuantity 模型,所以我必须重做我的 views.py 一遍又一遍


models.py

​​>
class Invoice(models.Model):

    invoice_id = models.AutoField(primary_key=True, editable=False)
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    date = models.DateField(default=datetime.date(datetime.now()))
    builty_num = models.CharField(max_length=100, blank=True)
    total_amount = models.FloatField(validators=[MinValueValidator(0.0)])
    discount = models.FloatField(default=0.0, validators=[MinValueValidator(0.0)])
    overflow = models.FloatField(default=0.0, validators=[MinValueValidator(0.0)])


class InvoiceDetail(models.Model):

    invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
    rate = models.FloatField(validators=[MinValueValidator(0.0)])
    num_thaan = models.PositiveIntegerField(validators=[MinValueValidator(1)])
    gazaana_per_thaan = models.FloatField(validators=[MinValueValidator(0.0)])


class Ledger(models.Model):
    TYPE = (
    ('D','Debit'),
    ('C','Credit'),
    )

    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    invoice = models.ForeignKey(Invoice, null=True, on_delete=models.CASCADE)
    return_invoice = models.ForeignKey(ReturnInvoice, null=True, on_delete=models.CASCADE)
    cash = models.ForeignKey(CashDetail, null=True, on_delete=models.CASCADE)
    cheque = models.ForeignKey(ChequeDetail, null=True, on_delete=models.CASCADE)
    cheque_transfer = models.ForeignKey(ChequeTransfer, null=True, on_delete=models.CASCADE)
    bank = models.ForeignKey(BankTransfer, null=True, on_delete=models.CASCADE)

    date = models.DateField(default=datetime.date(datetime.now()))
    detail = models.TextField()
    transaction_type = models.CharField(max_length=1, choices=TYPE)
    amount = models.FloatField(validators=[MinValueValidator(0.0)])
    balance = models.FloatField(validators=[MinValueValidator(0.0)])
    balance_status = models.CharField(max_length=6)


class ProductQuantity(models.Model):

    TYPE = (
    ('+','Add'),
    ('-','Subtract'),
    )    

    invoice_id_add = models.ForeignKey(StockInvoice, on_delete=models.CASCADE, null=True)
    invoice_id_sub = models.ForeignKey(Invoice, on_delete=models.CASCADE, null=True)
    invoice_id_return = models.ForeignKey(ReturnInvoice, on_delete=models.CASCADE, null=True)
    invoice_id_stock_return = models.ForeignKey(StockReturnInvoice, on_delete=models.CASCADE, null=True)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
    date = models.DateField(default=datetime.date(datetime.now()))
    num_thaan = models.PositiveIntegerField()
    rate = models.FloatField(validators=[MinValueValidator(0.0)])
    gazaana_per_thaan = models.FloatField(validators=[MinValueValidator(0.0)])
    quantity = models.FloatField(validators=[MinValueValidator(0.0)])
    transaction_type = models.CharField(max_length=1, choices=TYPE)

views.py

​​>
class InvoiceCreateAndGetByIdOnly(APIView):
    """
    only handle post/get request when the invoice is created, 
    update ledger accordingly and updates quantity in ProductQuantity
    """

    permission_classes = [canEditInvoice]

    def get(self, request):
        if 'invoice_id' in request.query_params:
            query = Invoice.objects.filter(
                invoice_id = request.query_params['invoice_id']
                )
            return Response(InvoiceSerializer(query,many=True).data)
        query = Invoice.objects.all()
        return Response(InvoiceSerializer(query, many=True).data)


    def post(self, request):
        allData = request.data
        data = request.data
        invoice_detail = data.pop('invoice_set')
        serializer_invoice = InvoiceSerializer(data=request.data)

        if serializer_invoice.is_valid():

            invoice = serializer_invoice.create(
                validated_data=serializer_invoice.validated_data
                )
           
            invoice_detail_array = []
            """adding invoice_id to the invoice_detail_set"""
            for detail in invoice_detail:
                isValid = False
                detail['invoice'] = invoice.invoice_id
                invoice_detail_array.append(detail)
                serializer_new = InvoiceDetailSerializer(data=detail) 

                if serializer_new.is_valid(): 
                    isValid = True
                else: 
                    Invoice.objects.filter(invoice_id=invoice.invoice_id).delete()
                    return Response(
                        serializer_new.errors, 
                        status=status.HTTP_400_BAD_REQUEST
                        )

            for detail in invoice_detail_array:
                quantity , thaan = ProductQuantity.get_quantity(
                    product=detail['product'], 
                    warehouse=detail['warehouse'],
                    gazaana=detail['gazaana_per_thaan']
                    )
                if(thaan < detail['num_thaan']):
                    Invoice.objects.filter(invoice_id=invoice.invoice_id).delete()
                    return Response(
                        {'error':'less thaan in stock'}, 
                        status=status.HTTP_400_BAD_REQUEST
                        )
                if(quantity < (detail['num_thaan'] * detail['gazaana_per_thaan'])):
                    Invoice.objects.filter(invoice_id=invoice.invoice_id).delete()
                    return Response(
                        {'error':'less quantity in stock'}, 
                        status=status.HTTP_400_BAD_REQUEST
                        )

                quantity = {
                    'invoice_id_sub' : invoice.invoice_id,
                    'product': detail['product'],
                    'warehouse': detail['warehouse'],
                    'date': invoice.date,
                    'num_thaan': detail['num_thaan'],
                    'rate': detail['rate'],
                    'quantity': detail['num_thaan'] * detail['gazaana_per_thaan'],
                    'gazaana_per_thaan': detail['gazaana_per_thaan'],
                    'transaction_type': '-'
                }    

                serializer_new = InvoiceDetailSerializer(data=detail)
                serializer_product_quantity = ProductQuantitySerializer(data=quantity)
                if(serializer_new.is_valid()):
                    invoice_detail = serializer_new.create(
                        validated_data=serializer_new.validated_data
                        )
                    if(serializer_product_quantity.is_valid()):
                        product_quantity_detail = serializer_product_quantity.create(
                            validated_data=serializer_product_quantity.validated_data)
         
            
            invoice_db = Invoice.objects.get(invoice_id=invoice.invoice_id)
            invoice_detail_db = InvoiceDetail.objects.all().filter(invoice=invoice_db.invoice_id)
            detail = " "
            for obj in invoice_detail_db:
                detail += (
                    str(obj.num_thaan) + " thaan " + 
                    str(obj.gazaana_per_thaan) + " gaz " + 
                    str(obj.product) + " @ PKR " + 
                    str(obj.rate) + "\n"
                    )

            balance = Ledger.get_balance(invoice_db.customer.customer_id)
            new_balance = balance -  invoice_db.total_amount - invoice_db.overflow + invoice_db.discount
            status_ledger = ""
            if( new_balance == 0):
                status_ledger = "Nill"
            elif( new_balance > 0):
                status_ledger = "Credit"
            elif( new_balance < 0):
                status_ledger = "Debit"

            data_ledger = {
                'customer': invoice_db.customer.customer_id,
                'invoice' : invoice_db.invoice_id,
                'date' : invoice_db.date,
                'transaction_type' : 'D',
                'amount' : invoice_db.total_amount + invoice_db.overflow - invoice_db.discount ,
                'detail' : detail,
                'balance': abs(new_balance),
                'balance_status': status_ledger,
            }
            
            serializer_ledger = LedgerSerializer(data=data_ledger)
            if serializer_ledger.is_valid():
                ledger_instance = serializer_ledger.create(
                    validated_data=serializer_ledger.validated_data
                    )
            else:
                return Response(
                    serializer_ledger.errors, 
                    status=status.HTTP_400_BAD_REQUEST
                    )

            return Response({
                "message":"invoice successfully created",
                "data":allData
                }, status=status.HTTP_201_CREATED)

        return Response(
            serializer_invoice.errors, 
            status=status.HTTP_400_BAD_REQUEST
            )

serializers.py

​​>
class LedgerSerializer(serializers.ModelSerializer):

    class Meta:
        model = Ledger
        fields = '__all__'


class InvoiceSerializer(serializers.ModelSerializer):

    invoice_detail = serializers.SerializerMethodField('get_invoice_details')

    def get_invoice_details(self,obj):
        return InvoiceDetailSerializer(InvoiceDetail.objects.filter(invoice=obj),many=True).data

    class Meta:
        model = Invoice
        # fields = '__all__'
        fields = ['invoice_detail', 'customer', 'builty_num','date', 'total_amount', 'invoice_id', 'overflow', 'discount']


class InvoiceDetailSerializer(serializers.ModelSerializer):

    class Meta:
        model = InvoiceDetail
        fields = '__all__'


class ProductQuantitySerializer(serializers.ModelSerializer):

    class Meta:
        model = ProductQuantity
        fields = '__all__'

【问题讨论】:

  • 你能举个具体的例子吗?目前尚不清楚问题是什么以及您期望什么。
  • @BriseBalloches,我刚刚更新了问题并添加了所有详细信息,如果您需要更多详细信息,请告诉我。

标签: python django serialization django-rest-framework


【解决方案1】:

当A、B发生如下变化时,你应该使用signals来修改C

from django.db import models
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver

class A(models.Model):
    field1 = models.CharField(max_length=100)
    field2 = models.CharField(max_length=255)

class B(models.Model):
    field1 = models.ForeignKey(A, on_delete=models.CASCADE)
    field2 = models.FloatField()

class C(models.Model):
    field1 = models.CharField(max_length=200)
    field2 = models.BooleanField(default=False)


def modify_c(a, b):
    c = C.objects.get() # get instance of C through C.object.get() using a, b
    # update c through fields of a and b
    c.save()

@receiver([post_save, post_delete], sender=A)
@receiver([post_save, post_delete], sender=B)
def update_c_from_a_or_b(sender, instance, *args, **keywords):
    if sender == A:
        instance_of_a = instance
        instance_of_b = # get instance of B through B.objects.get()
    if sender == B:
        instance_of_b = instance
        instance_of_a =  instance.field1 # ForeignKey key to A
    modify_c(instance_of_a, instance_of_b)

【讨论】:

  • 嘿,我刚刚更新了我的问题。你现在可以再看一遍吗?我觉得之前不是很清楚。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2019-08-28
  • 1970-01-01
  • 2014-01-13
  • 2018-05-29
  • 1970-01-01
  • 2017-06-18
  • 2022-01-22
  • 1970-01-01
相关资源
最近更新 更多