Here is the context first. I have a 'Stock' table, in which I record all the stocks with as information: the product, the quantity, the batch number and the expiry date.

However I have a constraint: I cannot have for the same product 2 records having the same batch. For instance:

product | qty | batch | expiry_date
A         10    E155    11/2023
A         5     E155    10/2024

This situation is impossible.

On the other hand I can have for the same product, 2 or more records having different batches with the same expiry date...

product | qty | batch | expiry_date
A         10    E155    11/2023
A         5     E156    11/2023

Moreover, if I insert a record whose batch is already present, I must merge the two records, i.e. the existing quantity must be incremented by the new quantity.

Now the code:


class Stock(models.Model):
    product = models.ForeignKey(Product, on_delete=models.PROTECT)
    qty = models.IntegerField(blank=False, null=False)
    batch = models.CharField(max_length=15, blank=False, null=False)
    expiry_date = models.DateTimeField(blank=False, null=False)


from common.utils import is_duplicate

class StockCreateView(LoginRequiredMixin, CreateView):
    """This view manages the add of an entry in the stock"""
    model = Stock
    template_name = 'common/generic_form.html'
    form_class = StockForm

    def form_valid(self, form):
        form_data = {'product': form.cleaned_data['product'].id,
                     'batch': form.cleaned_data['batch']}

        items = Stock.objects.filter(product=form.cleaned_data['product'].id)
        items_to_dict = [model_to_dict(i) for i in items]

        # I check if identical data is already in the database
        duplicate = is_duplicate(items_to_dict, form_data)

        if duplicate:
            duplicate_line = Stock.objects.get(id=duplicate['id'])
            duplicate_line.qty += form.cleaned_data['qty']
            return super().form_valid(form)

        return redirect('stock_details', pk=self.kwargs['pk'])

    def get_success_url(self):
        return reverse('entree_stock_details', args=(self.kwargs['pk'],)) 

What are the best practices for this kind of implementation ? is this the right place for this kind of logic ? Or is it more appropriate to move this logic directly into the model class by possibly rewriting the save method ?

  • \$\begingroup\$ I assume that products in the same batch always share the same expiry date — is that true? Also, can one batch include multiple products, or does the batch contain purely one product? \$\endgroup\$ Commented Aug 15, 2022 at 6:12
  • \$\begingroup\$ Actually that's it. For the same batch number, the expiry date is the same. Two different products may have the same lot number, but this is extremely rare. This is why the control must be done at the level of the product AND the batch. \$\endgroup\$
    – Tobin
    Commented Aug 15, 2022 at 8:37


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.