I am using Django 2.2 and I have a model with two classes Product
and ProductRevision
. When I retrieve a Product
, or a list of Product
s, I always fetch the corresponding ProductRevision
. ProductRevision
objects are incremented and only the last revision should be fetched with the Product
.
class Product(models.Model):
name = models.CharField(max_length=50, null=False, blank=False,
verbose_name=_("name"))
product_code = models.CharField(max_length=10, null=False, blank=False,
verbose_name=_("product code"))
slug = models.SlugField(null=False, unique=True)
@property
def current_item(self):
return ProductRevision.objects.filter(product=self, active=True).order_by('-version').first()
class ProductRevision(models.Model):
product = models.ForeignKey(Product, null=True, on_delete=models.PROTECT)
version = models.IntegerField(default=0,
verbose_name=_("version"))
active = models.BooleanField(default=False, null=False, blank=True,
verbose_name=_("is active"))
qty_close = models.IntegerField(default=0,
verbose_name=_("qty of accounts to be closed"))
price_eur = models.DecimalField(max_digits=6, decimal_places=2, default=0,
verbose_name=_("price in EUR"))
I tried to add a property current_item
to get the last revision of a given product. While this is working, it is very inefficient because when I use it in a template, it hits the database every time I display a field from the related ProductRevision
.
I found this answer based on an old version of Django (Fetch latest related objects in django) and I'm wondering if there is no other ways to achieve the same result with current versions of Django? I'm particularly interrested in achieving this in my models.
I have managed to achieve something similar to this by using a custom prefetch related queryset
products = Product.objects.prefetch_related(Prefetch(
'productrevision_set',
queryset=ProductRevision.objects.order_by('product', '-version').distinct('product'),
to_attr='latest_revision'
))
The queryset above will only return one ProductRevision
per Product
which in effect gives us the latest ProductRevision
for all Products
in only 2 queries
for product in products:
for latest_revision in product.latest_revision:
print(product, latest_revision)