@Dbtzhv

Как автоматически обновлять поле модели, значение которого делается на основе полей других моделей?

users models.py:
from django.contrib.auth.models import AbstractUser
from phonenumber_field.modelfields import PhoneNumberField
from django.db import models

class CustomUser(AbstractUser):
    USER_CHOICES = (
        ('s', 'Сотрудник'),
        ('p', 'Подрядчик'),
        ('k', 'Клиент'),
    )

    user_type = models.CharField('Тип сотрудника', choices=USER_CHOICES, null=False, blank=False, max_length=1)
    balance = models.DecimalField('Баланс', max_digits=10, decimal_places=2, default=0.0)
    description = models.TextField('Описание', null=False, blank=False)
    phone = PhoneNumberField("Телефон", null=False, blank=False, unique=True)
    first_name = models.CharField('Имя', max_length=30, null=False, blank=False)
    last_name = models.CharField('Фамилия', max_length=30, null=False, blank=False)
    financial_accounting = models.BooleanField('Вести учёт финансов', default=False)

    def save(self, *args, **kwargs):
        from payments.models import Payment, Invoice
        payments_sum = sum([sum([payment.total for payment in invoice.payments.filter(approved=True)]) for invoice in Invoice.objects.filter(receiver=self.id)])
        self.balance = payments_sum
        super().save(*args, **kwargs)

    def __str__(self):
        return self.username

    class Meta:
        verbose_name = 'Сотрудник'
        verbose_name_plural = 'Сотрудники'


payments models.py:
class Invoice(models.Model):
    payments = models.ManyToManyField('Payment', related_name='invoices', verbose_name='Платежи', blank=True)
    comment = models.TextField('Комментарий к счёту')
    payment_type = models.ForeignKey('PaymentType', on_delete=models.CASCADE, related_name='invoices', verbose_name="Тип начисления")
    subtype = models.ForeignKey('SubType', on_delete=models.CASCADE, related_name='invoices', verbose_name="Назначение начисления")
    payer = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='invoices', verbose_name="Плательщик")
    receiver = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='received_invoices', verbose_name="Получатель")
    project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='invoices', verbose_name="Выбор проекта")
    approved = models.BooleanField('Счёт оплачен', default=False)


    class Meta:
        verbose_name = 'Счёт на оплату'
        verbose_name_plural = 'Счета на оплату'

    def __str__(self):
        return f'Счёт {self.id}'


class Payment(models.Model):
    id = models.CharField(primary_key=True, max_length=10, unique=True, blank=True)
    project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='payments', verbose_name="Проект")
    date = models.DateField("Дата начисления", default=date.today)
    subtype = models.ForeignKey('SubType', on_delete=models.CASCADE, related_name='payments', verbose_name="Назначение платежа")
    total = models.DecimalField('Сумма платежа', max_digits=10, decimal_places=2, default=0.0)
    approved = models.BooleanField('Подтверждение платежа', default=False)
    

    def save(self, *args, **kwargs):
        if not self.id:
            last_id = Payment.objects.order_by('-id').first()
            if last_id:
                last_number = int(last_id.id)
                self.id = str(last_number + 1)
            else:
                self.id = "10001"


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


    class Meta:
        verbose_name = 'Платёж'
        verbose_name_plural = 'Платежи'

    def __str__(self):
        return f'Платёж {self.id}'


Сейчас поле balance у юзера заполняется, только когда я сохраняю его в админке. Т.е. я меняю payment'ы, и чтобы изменения отобразились на балансе, мне нужно обязательно нажать на сохранение. Как сделать, чтобы это всё происходило как-нибудь автоматически?
  • Вопрос задан
  • 77 просмотров
Решения вопроса 1
sergey-gornostaev
@sergey-gornostaev Куратор тега Django
Седой и строгий
Во-первых, такие поля вообще не стоит иметь. Во-вторых, если уж они есть зачем-то, то их стоит менять в обработчиках сигналов, а не в методе save. В-третьих, если нужно пересчёт в режиме реального времени, то это задача фронта, то есть надо написать соответствующий javascript-код и внедрить его в админку.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы