Django overriden instance save metod won't return actual data after super().save()
I have a simple Shipment
model. I need to access updated Shipment
instance fields data after save()
. But whenever I try to count Product
instances related to Shipment
instance I get not actual data (one save behind).
Flow example:
- Creating new
Shipment
using form with adding oneProduct
instance withChoiceField
; - Overriding
Shipment
save()
saving instance first; - After
save()
I'm trying to count products just added withself.products.all().count()
getting 0 on firstsave()
; - After repeating creation of new
Shipment
instance and again adding oneProduct
instanceself.products.all().count()
will return1
. I'm aware of m2m problem when new m2m instance added after save() performed but in my case FK suppose not to have such a problem.
(simplified)
class Product(models.Model):
shipments = models.ForeignKey(Shipment, related_name="products")
class Shipment(models.Model):
order = models.ForeignKey(Order, related_name="shipments")
def save(self, *args, **kwargs):
super(Shipment, self).save(*args, **kwargs)
count = self.products.all().count() # <-- Returns 0 even after instance saved with including FK model
The final goal is to acces Product
shipped quantity and depend on result change the Order
(FK related model) status.
I'm aware of m2m problem when new m2m instance added after
save()
performed but in my case FK suppose not to have such a problem.
It is essentially the same problem. The admin, or (Model
)Form
will first save the Shipment
and then save the Product
s, it can not do that in the opposite order, since it needs the primary key (or some other field the ForeignKey
is referring to). So it can not create the Product
first, since it refers to a Shipment
that needs to be created first. Strictly speaking, update logic could go in the opposite order, but it would probably only complicate matters more if the update flow is different from the create flow.
That being said, typically it is better not store aggregates in the model: determine aggregates when needed: storing aggregates in the model makes updating and keeping data in sync harder. You can use .annotate(…)
[Django-doc] to generate counts, sums, etc. per object when needed. So I don't think you should determine the number of products in the Shipment
.
Indeed, one can dynamically add, remove or link a Product
to a (different) Shipment
. So without the .save()
of the Shipment
being called, the number of Product
s can change. Storing the number of Product
s explicitly in a field in the Shipment
thus makes the software less reliable. I would implement some logic in a function, and let views that change Product
s or Shipment
call the logic to fix the status, or perhaps better: determine the status dynamically in a @property
or annotation.
Indeed, for example:
class Shipment(models.Model):
# …
@property
def status(self):
if self.products.count() > 5:
return 'too heavy'
else:
return 'ready to ship'