Hello good people. I have a surely common problem but I can’t figure it out.
I have a basic Order <=> Products relation.
Models
class Product(model):
### some fields
def get_price(self):
return x * 100 # Simplified
class Order(model):
product = ManyToManyField(Product)
total_price = models.DecimalField()
def get_total_price(self):
return sum(p.get_product_price() for p in self.ordered_products.all())
class OrderProduct(model):
product = ForeignKey(Product)
order = ForeignKey(Order)
quantity = PositiveIntegerField()
def get_product_price(self):
return self.quantity * self.product.get_price()
What I’m trying:
I want to save an Order Form with Products formsets (Product - Quantity)
I need the price to be calculated before the form is saved.
(I’m hiding the total_price field from the user)
Questions
I’m not sure where to put this logic:
I believe this answer from is one way, but I getting some errors on calling get_total_price inside form_valid()
Other question is: Do I need to include the total_price in my Order? My answer to this is that product prices will change in the future so I need to store somewhere the price of an Order that was made in the past.
It’s probably going to be necessary to see the complete view where you tried this, along with the full error message and trace back you received.
Whether you want to save the total price within the table depends upon whether or not you’re going to be able to accurately recalculate the total from the information available. (There are cases where the final charged price is not simply equal to the sum of the individual item prices - perhaps due to the application of sales taxes, or a quantity discount, or some type of coupon or credit being applied, etc, etc. Regardless of what factors are involved, you are always going to want to be able to track / identify the total amount changed and/or collected.)
Those variables are the reasons why I keep it this way. I was thinking to maybe keep an historical registry of some models with this library for example.
On 1), I basically did in form_valid() what you pointed out:
class OrderCreateView(CreateView):
model = Order
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context["products"] = OrderProductsFormset(self.request.POST)
else:
context["products"] = OrderProductsFormset()
return context
def form_valid(self, form):
context = self.get_context_data()
products_formset = context["products"]
# Save the order
new_order = form.save(commit=False)
# I was wrongly calling the `get_total_price` method
new_order.total_price = form.instance.get_total_price()
new_order.save()
Hey, if you understand it, and it works (for any reasonable definition of “works”), then it’s the best way.
You’re not doing anything shady, you’re not monkey-patching core, you’re not relying upon unpublished APIs, you’re not writing unnecessary code - I think you’re good.
Actually, for the sake of completeness and accuracy, form_valid should return an HttpResponse, which in the case of a form submission is usually an HttpResponseRedirect (but is not required to be).
But yes, unless you’re overriding post, the response is created by form_valid or form_invalid.