Django queryset related field lookup with filtering the last object
Question:
I am building a Price comparing django app, i came across this scenario where i need to filter the Last price for each seller in a related field lookup.
Seller model :
class Seller(models.Model):
name = models.CharField(max_length=250, null=True)
Part model :
class Part(models.Model):
name = models.CharField(null=True, blank=True, max_length=250, unique=True)
Seller model :
class Price(models.Model):
seller = models.ForeignKey(Seller, on_delete=models.CASCADE, null=True, blank=True, related_name='sellerprice')
part = models.ForeignKey(Part, on_delete=models.CASCADE, null=True, blank=True, related_name='partprice')
price = models.FloatField(null=True, blank=True)
added = models.DateTimeField(auto_now_add=True, null=True, blank=True)
Each item for sale has 4 price history ordered by "added" and each price has the seller name next to it.
views Queryset :
parts = Part.objects.all()
Template :
{% for part in parts %}
{% for y in part.partprice.all|slice:":4" %}
<a href="{{y.part.seller1URL}}"><p>${{y.price}} {{y.seller}}</p></a>
...
...
...
{% endfor %}
{% endfor %}
Problem is:
I am trying to query :
LAST PRICE per PRODUCT for each SELLER ordered by newest ADDED date
so far i tried :
>>> for part in parts:
... for price in part.partprice.all().order_by('price')[:4]:
... print(price)
result :
(NGK 3951) $4.0 item1 @2023-01-09 20:36:37.083544+00:00
(NGK 3951) $5.0 item2 @2023-01-09 20:26:12.961078+00:00
(NGK 3951) $5.5 item3 @2023-01-09 20:26:31.890411+00:00
(NGK 3951) $7.0 item4 @2023-01-09 20:26:20.358864+00:00
(Bosch Automotive 9603) $1.0 item4 @2023-01-10 22:21:53.431852+00:00
(Bosch Automotive 9603) $1.0 item1 @2023-01-10 22:22:00.237141+00:00
(Bosch Automotive 9603) $21.0 item3 @2023-01-09 20:26:44.716020+00:00
(Bosch Automotive 9603) $22.0 item1 @2023-01-09 20:26:39.625562+00:00
Expected query is to only display SELLER one time only for each iteration and if the product has no price from any of the 4 sellers leave it blank or just display the latest price available.
Any help is much appreciated and i hope all the details are included.
Answers:
based on the comment…
try something like this:
from django.db.models.aggregates import Max
latest_prices = Price.objects
.values('seller', 'price')
.annotate(latest_report=Max('added'))
The solution i found is much simpler and much better than the qs i was trying to accomplish, I simply used Querying History
I added the history model in my Price model and every time i edit the new price the histroy can be reached using this template :
<td>{% for y in part.partprice.all|dictsort:"price" %} # sort by lowest price
{% if y.seller.name == "seller1" %}
{{y.seller}} {% for x in y.history.all|slice:":1" %}<a href="{{y.part.seller1URL}}">${{x.price}}</a> {{x.history_date|timesince}} Ago<br>{% endfor %} {% for x in y.history.all|slice:"1:2" %} ${{x.price}} {{x.history_date|timesince}} Ago<br>{% endfor %} <hr>
{% endif %}
{% if y.seller.name == "seller2" %}
{{y.seller}} {% for x in y.history.all|slice:":1" %}<a href="{{y.part.seller2URL}}">${{x.price}}</a> {{x.history_date|timesince}} Ago<br>{% endfor %} {% for x in y.history.all|slice:"1:2" %} ${{x.price}} {{x.history_date|timesince}} Ago<br>{% endfor %} <hr>
{% endif %}
{% if y.seller.name == "seller3" %}
{{y.seller}} {% for x in y.history.all|slice:":1" %}<a href="{{y.part.seller3URL}}">${{x.price}}</a> {{x.history_date|timesince}} Ago<br>{% endfor %} {% for x in y.history.all|slice:"1:2" %} ${{x.price}} {{x.history_date|timesince}} Ago<br>{% endfor %} <hr>
{% endif %}
{% if y.seller.name == "seller4" %}
{{y.seller}} {% for x in y.history.all|slice:":1" %}<a href="{{y.part.seller4URL}}">${{x.price}}</a> {{x.history_date|timesince}} Ago<br>{% endfor %} {% for x in y.history.all|slice:"1:2" %} ${{x.price}} {{x.history_date|timesince}} Ago<br>{% endfor %} <hr>
{% endif %}
{% endfor %}
</td>
I am building a Price comparing django app, i came across this scenario where i need to filter the Last price for each seller in a related field lookup.
Seller model :
class Seller(models.Model):
name = models.CharField(max_length=250, null=True)
Part model :
class Part(models.Model):
name = models.CharField(null=True, blank=True, max_length=250, unique=True)
Seller model :
class Price(models.Model):
seller = models.ForeignKey(Seller, on_delete=models.CASCADE, null=True, blank=True, related_name='sellerprice')
part = models.ForeignKey(Part, on_delete=models.CASCADE, null=True, blank=True, related_name='partprice')
price = models.FloatField(null=True, blank=True)
added = models.DateTimeField(auto_now_add=True, null=True, blank=True)
Each item for sale has 4 price history ordered by "added" and each price has the seller name next to it.
views Queryset :
parts = Part.objects.all()
Template :
{% for part in parts %}
{% for y in part.partprice.all|slice:":4" %}
<a href="{{y.part.seller1URL}}"><p>${{y.price}} {{y.seller}}</p></a>
...
...
...
{% endfor %}
{% endfor %}
Problem is:
I am trying to query :
LAST PRICE per PRODUCT for each SELLER ordered by newest ADDED date
so far i tried :
>>> for part in parts:
... for price in part.partprice.all().order_by('price')[:4]:
... print(price)
result :
(NGK 3951) $4.0 item1 @2023-01-09 20:36:37.083544+00:00
(NGK 3951) $5.0 item2 @2023-01-09 20:26:12.961078+00:00
(NGK 3951) $5.5 item3 @2023-01-09 20:26:31.890411+00:00
(NGK 3951) $7.0 item4 @2023-01-09 20:26:20.358864+00:00
(Bosch Automotive 9603) $1.0 item4 @2023-01-10 22:21:53.431852+00:00
(Bosch Automotive 9603) $1.0 item1 @2023-01-10 22:22:00.237141+00:00
(Bosch Automotive 9603) $21.0 item3 @2023-01-09 20:26:44.716020+00:00
(Bosch Automotive 9603) $22.0 item1 @2023-01-09 20:26:39.625562+00:00
Expected query is to only display SELLER one time only for each iteration and if the product has no price from any of the 4 sellers leave it blank or just display the latest price available.
Any help is much appreciated and i hope all the details are included.
based on the comment…
try something like this:
from django.db.models.aggregates import Max
latest_prices = Price.objects
.values('seller', 'price')
.annotate(latest_report=Max('added'))
The solution i found is much simpler and much better than the qs i was trying to accomplish, I simply used Querying History
I added the history model in my Price model and every time i edit the new price the histroy can be reached using this template :
<td>{% for y in part.partprice.all|dictsort:"price" %} # sort by lowest price
{% if y.seller.name == "seller1" %}
{{y.seller}} {% for x in y.history.all|slice:":1" %}<a href="{{y.part.seller1URL}}">${{x.price}}</a> {{x.history_date|timesince}} Ago<br>{% endfor %} {% for x in y.history.all|slice:"1:2" %} ${{x.price}} {{x.history_date|timesince}} Ago<br>{% endfor %} <hr>
{% endif %}
{% if y.seller.name == "seller2" %}
{{y.seller}} {% for x in y.history.all|slice:":1" %}<a href="{{y.part.seller2URL}}">${{x.price}}</a> {{x.history_date|timesince}} Ago<br>{% endfor %} {% for x in y.history.all|slice:"1:2" %} ${{x.price}} {{x.history_date|timesince}} Ago<br>{% endfor %} <hr>
{% endif %}
{% if y.seller.name == "seller3" %}
{{y.seller}} {% for x in y.history.all|slice:":1" %}<a href="{{y.part.seller3URL}}">${{x.price}}</a> {{x.history_date|timesince}} Ago<br>{% endfor %} {% for x in y.history.all|slice:"1:2" %} ${{x.price}} {{x.history_date|timesince}} Ago<br>{% endfor %} <hr>
{% endif %}
{% if y.seller.name == "seller4" %}
{{y.seller}} {% for x in y.history.all|slice:":1" %}<a href="{{y.part.seller4URL}}">${{x.price}}</a> {{x.history_date|timesince}} Ago<br>{% endfor %} {% for x in y.history.all|slice:"1:2" %} ${{x.price}} {{x.history_date|timesince}} Ago<br>{% endfor %} <hr>
{% endif %}
{% endfor %}
</td>