Связанная модель, не обновляемая при переопределении сохранения модели, сохраняемой с помощью ModelFormset

#django #django-models #django-forms

#django #django-models #django-forms

Вопрос:

Итак, у меня есть модель транзакции, которая является FK-d для общего ресурса. В представлении «Учетная запись» у меня есть ModelFormset этих транзакций, и я могу сохранить несколько транзакций, прокручивая формы и сохраняя их.

С помощью метода save () моей транзакции я пытаюсь обновить баланс на связанном ресурсе. это работает, если я сохраняю одну транзакцию, но когда я ПУБЛИКУЮ свой ModelFormset с несколькими транзакциями, каждый раз, когда я нажимаю на строку self.share.balance = self.share.balance amt в переопределении сохранения транзакции () (то есть для каждой новой транзакции), share.balance является тем, что было до сохранения любой из предыдущих транзакций в наборе форм.

Кто-нибудь знает, почему добавленная сумма к общему балансу из предыдущей сохраненной транзакции не переносится при последующих сохранениях (почему к общему балансу будет добавлена только сумма последней транзакции)?

Модель транзакции, которая должна обновлять баланс на общей базе данных родительской модели

 class Transaction(models.Model):
    share = models.ForeignKey(Share, on_delete=models.CASCADE, null=True, blank=True)
    account = models.ForeignKey(Account, on_delete=models.CASCADE, null=True, blank=True)

    db_cr = models.CharField(choices=DBCR, max_length=2)
    amt = models.DecimalField('Amount', max_digits=11, decimal_places=2)
    post_dt = models.DateTimeField('Post Time', null=True, blank=True)

    def save(self, *args, **kwargs):
        if not self.pk:

        ...

            if self.share:
                if self in self.share.transaction_set.all():
                    logging.error('Transaction %s already posted' % self.id)
                    return False

                amt = self.amt if self.db_cr == 'cr' else -self.amt
                self.share.balance = self.share.balance   amt
                self.share.save()
  

Поделиться моделью

 class Share(models.Model):
    name = models.CharField(max_length=80)
    account = models.ForeignKey(Account, on_delete=models.CASCADE)
    definition = models.ForeignKey(ShareDef, on_delete=models.PROTECT)
    balance = models.DecimalField('Balance', max_digits=11, decimal_places=2, default=0)
    def __str__(self):
        return '%s %s %s %s'%(self.account,
                   self.name,
                   self.definition.sym_code,
                   self.balance )

    def save(self, *args, **kwargs):
        if not self.pk:

            if not self.name:
                self.name = self.definition.name
        super(Share, self).save(*args, **kwargs)
  

На мой взгляд, у меня есть набор форм транзакции

 #...in view
TranFormSet = modelformset_factory(Transaction, exclude=('origin','ach_entry'), extra=1)

if request.method=='POST':
...
    tran_formset = TranFormSet(request.POST)
...
    if tran_formset.is_valid():
        for form in tran_formset:

            tran = form.save(commit=False)
            tran.account = account
            tran.origin = 'tt'
            tran.save()

else:

    #...following kind of weird because of how I'm setting querysets of ModelChoiceFields
    kwargs = {'account_instance': account}
    tran_formset = TranFormSet(queryset=Transaction.objects.none())
    tran_formset.form = (curry(TranForm, **kwargs))
  

Форма

 class TranForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        account_instance = kwargs.pop('account_instance', None)
        super(TranForm, self).__init__(*args, **kwargs)

            if account_instance:
                self.fields['share'].queryset = account_instance.share_set.all()

            if self.instance.pk:
                del self.fields['share']



    class Meta:
        model=Transaction
        exclude=['origin', 'ach_entry', 'account']


    post_dt = forms.DateTimeField(initial=datetime.date.today(), widget=forms.TextInput(attrs=
{
    'class': 'datepicker'
}))

    share = forms.ModelChoiceField(empty_label='---------', required=False, queryset=Share.objects.all())
  

Комментарии:

1. Возможно ли, что сохранения для транзакций выполняются параллельно? Они выглядят последовательными при просмотре вашего кода, но ваша проблема звучит как состояние гонки

2. Я бы не знал, как проверить — я ввел точку останова и запустил debug, и даже при медленном выполнении возникла эта проблема

Ответ №1:

Неясно, что может быть причиной проблемы, но может быть полезно выполнить обновление self.share.balance в одном update() запросе. Это можно сделать с помощью F выражений:

 from django.db.models import F

class Transaction(models.Model):
    # ...

    def update_share_balance(self):
        if self.db_cr == "cr":
            amount = self.amt
        else:
            amount = -self.amt

        # By using the queryset update() method, we can perform the
        # change in a single query, without using a potentially old
        # value from `self.share.balance`
        return Share.objects.filter(id=self.share_id).update(
            balance=F("balance")   amount
        )

    def save(self, *args, **kwargs):
        if not self.pk:
            # ...
            if self.share:
                # ...
                self.update_share_balance()

        # Also, be sure to call the super().save() method at the end!
        super().save(*args, **kwargs)