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

Есть такие модели.

class Document(models.Model):
    number = models.PositiveIntegerField(unique=True, verbose_name='Номер')
    sale = models.BooleanField(default=True, verbose_name='Расход')


class DocumentItem(models.Model):
    document = models.ForeignKey(Document, on_delete=models.CASCADE, verbose_name='Накладная')
    product = models.ForeignKey('Product', on_delete=models.CASCADE, verbose_name='Товар')
    quantity = models.PositiveIntegerField(verbose_name='Количество')

    def save(self, *args, **kwargs):
        old = False
        try:
            old = DocumentItem.objects.get(document__number=self.document.number,
                                           product__name=self.product.name)
        except:
            if self.document.sale:
                self.product.stock -= self.quantity
            else:
                self.product.stock += self.quantity
        if old:
            if self.document.sale:
                self.product.stock -= (self.quantity - old.quantity)
            else:
                self.product.stock += (self.quantity - old.quantity)
        self.product.save()
        super().save(*args, **kwargs)


class Product(models.Model):
    name = models.CharField(max_length=100, unique=True, verbose_name='Наименование')

@receiver(post_delete, sender=DocumentItem)
def correct_stock_after_delete(sender, **kwargs):
    doc_item = kwargs.get('instance')
    if doc_item.document.sale:
        doc_item.product.stock += doc_item.quantity
    else:
        doc_item.product.stock -= doc_item.quantity
    doc_item.product.save()

Управление осуществляю через админ панель. При сохранении экземпляра DocumentItem, то есть при сохранении накладной Document, в зависимости от флага sale (приходная или расходная накладная), товар на складе либо увеличивается, либо уменьшается на quantity. При удалении экземпляра DocumentItem, то есть при удалении накладной Document, вызывается сигнал post_delete, который корректирует количество товара на складе. Нужно исключить возможность добавлять в накладную одинаковый товар. Сейчас в админке если, например, добавить в накладную товар Бананы 1шт, а затем опять Бананы 2шт, то количество в базе данных никак не изменится. Необходимо, чтобы в админ панели при добавлении одинакового товара возникало предупреждение и вручную можно было бы поправить, либо чтобы в бд учитывалось Бананы 3шт, но это, видимо, сложнее сделать, чем первый вариант. С какой стороны хотя бы подходить к этому вопросу?

У вас неправильно работает потому, что в коде есть две проблемы.

Первая, это то как вы достаете старый объект. Нужно доставать по идентификатору, а вы берете по номеру накладной и продукту, и если вы добавили два одинаковых продукта, то вы не найдете таким образом нужный, тот который сохраняется. Когда объекта два, то get бросает исключение, так как он ожидает найти один объект, а находит два.

И тут случается вторая проблема, а именно ваш код обработки ошибок все ошибки при вызове get трактует как "объект не найден", т.е. считает, что это новый объект и отнимает не разницу, а все количество. Отсюда неправильное изменение количества продукта.

Исправить можно так:

    def save(self, *args, **kwargs):
        old = None
        try:
            old = DocumentItem.objects.get(id=self.id)
        except DocumentItem.DoesNotExist as e:
            if self.document.sale:
                self.product.stock -= self.quantity
            else:
                self.product.stock += self.quantity
        ...

Можно и запретить добавлять два одинаковых продукта в одну накладную. Для этого нужно добавить ограничение:

from django.db.models.constraints import UniqueConstraint


class DocumentItem(models.Model):

    ...

    class Meta:
        constraints = [
            UniqueConstraint(fields=['document', 'product'], name='unique_product')
        ]

Вернуться на верх