Как переопределить метод сохранения Django для динамического обновления некоторых полей? - PullRequest
7 голосов
/ 28 мая 2019

Здесь у меня есть две модели. В этих моделях я хочу сделать значение amount_to_pay динамическим в Ledger модели. Например, у меня есть две разные формы для этих двух моделей и при сохранении формы расходов, если пользователь выбирает payment_option, который исходит из модели ledger и дает некоторое значение для поля amount_to_pay, и если только ledger.id и expense.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)

Ответы [ 3 ]

7 голосов
/ 30 мая 2019

Решение первое:

Я думаю, что вместо изменения в 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)

Решение второе:

Но, если честно, Solution One не кажется мне хорошим.Причина в том, что вы сохраняете одни и те же данные в 2 местах (как в расходах, так и в бухгалтерской книге).Вместо этого он должен быть один раз, тогда значение amount_to_pay в Ledger должно рассчитываться динамически.Например:

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 (так как он динамически вычисляет сумму_доплаты по БД) из предыдущего решения, то вы можетевсегда annotate значение.Например:

Для этого решения вам нужно изменить модель Expense и добавить related_name:

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

Затем используйте этот related_name в таком запросе (к вашему сведению: вы не можете сохранить метод def amount_to_pay(...) в Ledger модели для следующего примера использования):

from django.db.models import Sum, F, ExpressionWrapper, IntegerField

ledgers = Ledger.objects.all().annotate(expense_sum=Sum(<b>'expenses__amount_to_pay'</b>)).annotate(<b>amount_to_pay=ExpressionWrapper(F('opening_balance') - F('expense_sum'), output_field=IntegerField())</b>)

# usage one
for l in ledgers:
   l.amount_to_pay

# usage two
ledgers.values('amount_to_pay')
0 голосов
/ 30 мая 2019

проверьте, есть ли у 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)
0 голосов
/ 28 мая 2019

Лучше всего, чтобы вы переопределяли вызов в методе сохранения любым способом, которым у вас есть foreginkey.

    def save(self, *args, **kwargs):
        #self.date_created = timezone.now()
        # YOUR LOGIC HERE
        super(YOUR_OVERRIDING_MODEL , self).save(*args, **kwargs
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...