如何使Django模型实例与另一个模型实例相关联

时间:2018-01-22 23:32:55

标签: python django

我希望租金下的金额在“帐户”下的金额上增加。当用户在租金中输入金额时,它应该扣除或添加账户中的金额,如果已付款则应重置或在未付款时保持不变。

class Account(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=250)
    number = models.PositiveIntegerField(help_text="Account number", unique=True, blank=True, null=False)
    currency = models.CharField(max_length=3, choices=ALLOWED_CURRENCIES)
    creation_date = models.DateTimeField(auto_now_add=True)
    amount = models.DecimalField(max_digits=MONEY_MAX_DIGITS, decimal_places=MONEY_DECIMAL_PLACES, help_text="Latest"                                                                                                  "balance", default=0, blank=False, null=True)

class Rent(models.Model):
    source = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="Account holder.")
    amount = models.DecimalField(max_digits=MONEY_MAX_DIGITS, decimal_places=MONEY_DECIMAL_PLACES)
    date = models.DateTimeField(auto_now_add=True)

1 个答案:

答案 0 :(得分:1)

您可以使用以下三种策略之一:

  1. 动态计算帐户的金额。您可以使用这样的方法(或者可能是缓存属性),而不是使用amount字段:

    from django.db.models import Sum
    
    class Account(models.Model):
    
        # ...
    
        def amount(self):
            result = self.rent_set.all().aggregate(
                total_amount=Sum('amount'))
            return result['total_amount']
    

    这种方法的优势在于它始终准确:无论您如何创建和操作Account和Rent记录,此方法都将始终返回正确的数量。

    它的缺点是每次调用它时都会发出查询。这会对性能产生影响。

  2. 保存或删除租金时更新帐户金额字段:

    from django.db.models import Sum
    
    class Account(models.Model):
    
        # ...
    
        def update_amount(self):
            result = self.rent_set.all().aggregate(
                total_amount=Sum('amount'))
            self.amount = result['total_amount']
    
    class Rent(models.Model):
    
        # ...
    
        def save(self, *args, **kwargs):
            super().save(*args, **kwargs)
            self.account.update_amount()
            self.account.save()
    
        def delete(self, *args, **kwargs):
            account = self.account
            super().delete(*args, **kwargs)
            account.update_amount()
            account.save()
    

    此方法的优点是金额存储在帐户记录中,因此无需在每次需要时重新计算,只有在保存Rent对象时才会重新计算。

    缺点是你必须在Rent对象上调用save()delete()。例如,您不能使用Rent.objects.all().delete(...)Rent.objects.all().update(...)等查询集方法,因为您的自定义save()delete()将不会被调用。

    另一个问题是,如果您在数据库级别编辑/删除租借记录而不通过自定义代码,则不会更新金额。

  3. 收到post_save/post_delete signals后更新帐户金额(感谢user3375448提供建议):

    from django.db.models.signals import post_save, post_delete
    from django.dispatch import receiver
    
    @receiver(post_save, sender=Rent)
    @receiver(post_delete, sender=Rent)
    def my_handler(sender, instance, **kwargs):
        instance.account.update_amount()
    

    这基本上与之前的解决方案类似,它具有基本相同的优点和缺点。

    以前的解决方案的一个改进是,在删除查询集时也会发送post_delete