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:
models.py
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)
views.py
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']
duplicate_line.save()
else:
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 ?