【问题标题】:how to override django save method to update some field dynamically?如何覆盖 django save 方法以动态更新某些字段?
【发布时间】:2019-10-13 18:07:24
【问题描述】:

这里我有两个模型。在这些模型中,我想在Ledger 模型中使amount_to_pay 的值动态化。例如,这两个模型有两种不同的形式,如果用户选择payment_option 来自 ledger 模型并为 amount_to_pay 字段提供一些值,那么如果只有 ledger.idexpense.payment_option_id 相同,那么分类帐模型中 amount_to_pay 的值应替换为该值。如何我可以吗?

models.py

     class Expense(models.Model):
        pay_from = models.CharField(max_length=200)
        payment_option = models.ForeignKey('Ledger', on_delete=models.CASCADE)
        amount_to_pay = models.IntegerField(default=0)
        expense_date = models.DateField(default=datetime.date.today)
        expense_type = models.ForeignKey(ExpenseType, on_delete=models.CASCADE)
        note = models.TextField()
        created = models.DateTimeField(auto_now_add=True)
        updated = models.DateTimeField(auto_now=True)
        slug = AutoSlugField(unique_with='id', populate_from='expense_type')

        def get_amount_to_pay(self):
            return self.amount_to_pay


    class Ledger(models.Model):
        name = models.CharField(max_length=200)
        account_number = models.CharField(max_length=250, unique=True)
        account_type = models.CharField(max_length=200)
        opening_balance = models.IntegerField(default=0)
        amount_to_pay = models.IntegerField(default=0, blank=True, null=True)
        current_balance = models.IntegerField(default=0, blank=True, null=True)
        created = models.DateTimeField(auto_now_add=True)
        updated = models.DateTimeField(auto_now=True)
        slug = AutoSlugField(unique_with='id', populate_from='name')

        def save(self, *args, **kwargs):
             self.amount_to_pay = Expense.get_amount_to_pay(self)

# here how can i save the amount_to_pay from expense form if the ledger.id and expense.payment_option.id matches??
#i got stuck here.           
 self.current_balance = self.opening_balance - self.amount_to_pay

            super(Ledger, self).save(*args, **kwargs)

【问题讨论】:

    标签: python django orm


    【解决方案1】:

    覆盖保存方法并在 django 中动态更新某些字段

    对于我的情况,当我对答案发表评论时,我想动态更新答案模型中的完成列,它应该返回 true 而不是 False。因此,在我的示例中,我正在检查已完成的布尔列是否为 False,如果为 False,我使用 Comment 中的答案对象将其状态更改为 True,然后保存答案对象,请参阅有关更新与插入字段 @987654321 的更多信息@

    class Answer(models.Model):
        user = models.ForeignKey("User", on_delete=models.CASCADE, blank=True, null=True)
        username = models.CharField(max_length=250)
        codeAnswer = models.CharField(max_length=250)
        girAnswer = models.CharField(max_length=250)
        correct = models.BooleanField(default=False)
        firebaseToken = models.CharField(max_length=250)
        done = models.BooleanField(default=False)
        created_at = models.DateTimeField()
        updated_at = models.DateTimeField(auto_now=True)
    
        def __unicode__(self):
            return "{0}: {1}".format(self.username, self.value)
    
        def __str__(self):
            return "%s" % (self.codeAnswer)
    
    # So here i override the save method in the Comment model to do what i want as below.
    
    class Comment(models.Model):
        answer = models.ForeignKey("Answer", on_delete=models.CASCADE, blank=False , related_name="comments")
        writerName = models.CharField(max_length=250)
        content = models.CharField(max_length=250)
        deleted = models.BooleanField(default=False)
        created_at = models.DateTimeField(auto_now_add=True)
        updated_at = models.DateTimeField(auto_now=True)
    
        def __str__(self):
            return "%s" % (self.writerName)
    
        def save(self, *args, **kwargs):
            if self.answer.done is False:
                self.answer.done = True
                self.answer.save()
            super(Comment, self).save(*args, **kwargs)
    

    希望它能回答上面的问题,谢谢

    【讨论】:

    • 如果您有新问题,请点击 按钮提出问题。如果有助于提供上下文,请包含指向此问题的链接。 - From Review
    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    【解决方案2】:

    检查 Ledger 是否有孩子,然后更新它:

    class Expense(models.Model):    
            def save(self, *args, **kwargs):
                    self.payment_option.amount_to_pay = self.payment_option.amount_to_pay + self.amount_to_pay
                    self.payment_option.save()
                super(Expense, self).save(*args, **kwargs)
    

    【讨论】:

    • self.ledger_payment_option.amount_to_pay = 1000 它有什么作用??我想将它保存为动态的。如果只有用户从费用模型表单中给出值并且 payment_option 和 ledger 的 id 匹配,那么 ledger 表中的 amount_to_pay 的值应该替换为用户输入的数据
    • 如果你想从 Expense 模型中更新 Ledger.amount_to_pay 你有答案。
    【解决方案3】:

    解决方案一:

    我认为与其更改Ledger 模型,不如更改Expense 模型,如下所示:

    class Expense(models.Model):
        ...
        def save(self, *args, **kwargs):
           self.payment_option.amount_to_pay = self.payment_option.amount_to_pay + self.amount_to_pay
           self.payment_option.save()
           super(Expense, self).save(*args, **kwargs)
    

    方案二:

    但老实说,解决方案一对我来说似乎并不好。原因是您将相同的数据保存在 2 个地方(费用和分类帐中)。相反,它应该是一次,然后应该动态计算Ledger 中的amount_to_pay 值。像这样:

    from django.db.models import Sum
    
    class Ledger(...):
    
         @property
         def amount_to_pay(self):
             # I am using a property method to show the amount_to_pay value.
             # FYI: in this way, you need to remove amount_to_pay field from Ledger model
             return self.opening_balance - self.expense_set.all().aggregate(a_sum=Sum('amount_to_pay')).get('a_sum', 0)
    

    这样,对于每个分类帐,amount_to_pay 的值将在运行时动态计算。例如:

     for l in Ledger.objects.all():
         l.amount_to_pay
    

    解决方案三:

    如果您对使用先前解决方案中的每个l.amount_to_pay(因为它从 DB 动态计算 amount_to_pay)产生 DB 命中持谨慎态度,那么您始终可以使用 annotate 值。像这样:

    对于此解决方案,您需要更改 Expense 模型并添加 related_name

    class Expense(models.Model):
        pay_from = models.CharField(max_length=200)
        payment_option = models.ForeignKey('Ledger', on_delete=models.CASCADE, related_name='expenses')

    然后像这样在查询中使用related_name(仅供参考:对于以下使用示例,您不能在 Ledger 模型中保留 def amount_to_pay(...) 方法):

    from django.db.models import Sum, F, ExpressionWrapper, IntegerField
    
    ledgers = Ledger.objects.all().annotate(expense_sum=Sum('expenses__amount_to_pay')).annotate(amount_to_pay=ExpressionWrapper(F('opening_balance') - F('expense_sum'), output_field=IntegerField()))
    
    # usage one
    for l in ledgers:
       l.amount_to_pay
    
    # usage two
    ledgers.values('amount_to_pay')

    【讨论】:

      【解决方案4】:

      最好的办法是你用任何你有foreginkey的方式覆盖save方法中的调用。

          def save(self, *args, **kwargs):
              #self.date_created = timezone.now()
              # YOUR LOGIC HERE
              super(YOUR_OVERRIDING_MODEL , self).save(*args, **kwargs
      

      【讨论】:

      • amount_to_pay 和 current_balance 是必填项
      • amount_to_pay = Expense.objects.get(id=form.cleaned_data['amount_to_pay_id']) 打印这个值
      • amount_to_pay_idamount_to_pay
      猜你喜欢
      • 2013-07-20
      • 1970-01-01
      • 2018-02-05
      • 2018-10-12
      • 1970-01-01
      • 2021-11-21
      • 1970-01-01
      • 2020-05-21
      • 2012-04-29
      相关资源
      最近更新 更多