Как убрать повторяющиеся записи при заполнении базы данных через админ панель?
Есть такие модели.
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')
]