Here in the template I am iterating MyModel objects. Now in the template I want to get the total sum of integer fields for the particular year.
I don't know how I can I use these three functions in order to get the total sum of field? Or there might be any better approach ?
model
class MyModel(models.Model):
date = models.DateField()
field1 = models.DecimalField(default=0.0)
field2 = models.DecimalField(default=0.0)
views
def get_total_field1(year):
return MyModel.objects.filter(date__year=year).annotate(total=Sum('field1'))['total']
def get_total_field2(year):
return MyModel.objects.filter(date__year=year).annotate(total=Sum('field2'))['total']
def get_total(year):
return get_total_field1() + get_total_field2()
class MyView(ListView):
model = MyModel
template_name = 'my_template.html'
template
{% for obj in objs %}
<tr>
<td>{{obj.date|date:'Y'}}</td>
<td>Field 1 total sum for obj.date </td>
<td>Field 2 total sum for obj.date</td>
<td>Field 1 total + field2 total </td>
{% endfor %}
Templates are for displaying data. Calculations are more like business logic that should go to Python code. So my advice - calculate everything you need in the code (for "sum" see .annotate() Django method) and then just display pre-calculated variables in your templates.
In your example I would move "get_total_field1" and other methods into the model itself as property, then you can just do
obj.get_total_field1 in your template.
Related
I'm trying to withdraw the order amount using the method from the model, but I can't. Have error - 'int' object is not iterable
models.py
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.PROTECT)
product = models.ForeignKey(Product, on_delete=models.PROTECT)
price = models.IntegerField()
quantity = models.PositiveIntegerField(default=1)
def get_total_price(self):
return sum(self.get_price())
def get_price(self):
return self.price * self.quantity
def __str__(self):
return str(self.order)
get_price method it's work, but in get_total_price have error, what I'am doing wrong?
order_success.html
{% for item in order_item %}
<tr>
<td>{{item.quantity}}</td>
<td>{{item.price}}</td>
<td>{{item.get_price}}</td>
</tr>
{% endfor %}
<h2>To pay - {{order_item.get_total_price}}</h2> # doesn't work
can you help me write right the sum method, please. I think my method is wrong.
Can you help me write right the sum method, please. I think my method is wrong.
I think your modeling is wrong. An OrderItem has no total_price, it has a certain price that is the quantity times the unit price, but the total price is an attribute of the entire queryset.
We can however calculate such aggregates, by using a queryset in the view for that, like:
from django.db.models import F, Sum
def some_view(request):
order_items = OrderItems.object.all() # might be a different query
aggrs = order_items.aggregate(
total_price=Sum(F('quantity') * F('price'))
)
context = {
'order_items': order_items,
'aggrs': aggrs
}
return render(request, 'some_template.html', context)
In the template, we can then render it like:
{% for item in order_items %}
<tr>
<td>{{item.quantity}}</td>
<td>{{item.price}}</td>
<td>{{item.get_price}}</td>
</tr>
{% endfor %}
<h2>To pay - {{ aggr.total_price }}</h2>
Note: since your order_item is a collection, it makes more sense to use a plular (like order_items here in this sample view).
Note: it makes sense to define this on the Order model, for example:
class Order(models.Model):
# ...
#property
def total_price():
return self.order_item_set.aggregate(
total_price=Sum(F('quantity') * F('price'))
)['total_price']
then we can thus write some_order.total_price to obtain the total price of that Order object.
Note: it is a bit confusing that you have a price field and a get_price method, perhaps you should consider renaming price to
unit_price, and then make get_price a #property named price.
I have a simple Product and Rating class like so:
class Product(models.Model):
name = ...
price = ...
class Rating(models.Model):
product = models.ForeignKey("Product", ...)
score = models.PositiveSmallIntegerField(...)
Now I want to get a list all products and the average rating for each product. Simply getting all products is easy:
product_list = Product.objects.all()
If I have a single product and I want to get the average rating for that single product:
product = get_object_or_404(Product, pk=product_id)
ratings = Rating.objects.filter(product=product)
product_avg_rating = ratings.aggregate((Avg("score")))
But let's assume I want to list all products and their average rating. This sounds like it's quite resource/compute heavy. So what is the best way to solve this? Should I make a class method for the Product class that just returns the average, so I can then call this in the template:
{{ product.get_average_rating }}
Or is there a better way to do this? I'm not quite sure what to do here and need some guidance.
Thank you so much in advance!
EDIT: I think I wasn't clear enough with my explanation, so here's an actual example. Let's say I have a query set, filtered by the slug (which is just a searchterm):
products = Product.objects.filter(tags__name__in=[slug])
Now how can I add the average rating to EACH SINGLE product so that I can do this in the template:
{% for product in products %}
{{ product.avg_rating_score }}
{% endfor %}
You can annotate your Product in the first query, so that it will only take one query to obtain the average, like:
product = get_object_or_404(
Product.object.annotate(avg_score=Avg('rating__score')),
pk=product_id
)
This will result in a single Product that contains an extra attribute (only for this specific QuerySet!), in the template, we thus can render it with:
{{ product.avg_score }}
The query that it does is:
SELECT product.*, AVG(rating.score) AS avg_score
FROM product
LEFT OUTER JOIN rating ON product.id = rating.product_id
WHERE product.id = product_id
This is a simplified version of the models:
class Toy(models.Model):
#generic fields
class Order(models.Model):
customer = models.ForeignKey(Customer)
class OrderItem(models.Model):
order = models.ForeignKey(Order)
toy = models.ForeignKey(Toy)
points = models.PositiveSmallIntegerField(default=3)
A customer can make multiple orders, to it increases the number of points per toy.
This subquery, only returns the first row of OrderItem:
class Customer(models.Model):
def get_toys_with_points():
order_items = OrderItem(toy=models.OuterRef('pk'), order__customer=self)
toys = Toy.objects.annotate(
points_sum = models.Sum(Subquery(order_items.values('points')))
)
return toys
So, when I pull that into my template:
{% for toy in customer.get_toys_with_points %}
{{ toy.points_sum }}
{% endfor %}
I am always getting the value of the first row (even if there are more purchases that would sum up to 25 for example).
You don't need a subquery here.
toys = Toy.objects.filter(orderitem__order__customer=self).annotate(
points_sum=models.Sum('orderitem__points')
)
I want to perform row level math on a model and display the results in the template. The database has a row for each company per day.
class MyModel(models.Model):
company = model.CharField(...
daily_target = model.PositiveSmallIntger(...
actual = model.PositiveSmallIntger(...
date = model.DateField(...
In the template I'd want to display the result of 100 * actual / daily_target for the logged-in company. I have no problem doing this in the python interpreter but am confused about how to do this with views & templates.
You could add a property to the model:
class MyModel(models.Model):
company = model.CharField(...
daily_target = model.PositiveSmallIntger(...
actual = model.PositiveSmallIntger(...
...
#property
def pct_target(self):
return 100. * self.actual / self.daily_target
Then in your template:
{% for item in queryset %}
{{ item.pct_target }}
{% endfor %}
The disadvantage of this is that you cannot filter or order the queryset by the property. Another option would be to annotate your queryset with the calculated field.
I have 3 models, Entry model and Category model, and I have created intermediate model CategoryEntry.
class Entry(models.Model):
entry_text = models.TextField()
class Category(models.Model):
user = models.ForeignKey(User)
category_text = models.CharField(max_length=200)
entries = models.ManyToManyField(Entry, through='CategoryEntry')
class CategoryEntry(models.Model):
category = models.ForeignKey(Category, related_name="related_entry_categories")
entry = models.ForeignKey(Entry)
viewed = models.BooleanField(default=False)
How can I get in template Users total Entry count.
For example I can get total users Category count with
{{ user.category_set.count }}
So I tried many different ways, but don't get how to follow next relation
{{ user.category_set.entries.count}}
{{ user.category_set.categoryentry_set.count}}
{{ user.category_set.all.categoryentry_set.count}}
{{ user.category_set.related_entry_categories.count }}
Is this even possible (good thing to do) to count in template? Or is there better way?
Thanks!
your queries don't make sense because category_set is a collection of objects rather than a single object, so you cannot simply ask for category_set.entries.count
first you have to think about what you want... do you want:
individual count of entries for each category in category_set?
or total count of entries across all categories in category_set?
For the former you need to annotate the queryset. this will have to be done in the view rather than template because the method needs arguments:
from django.db.models import Count
user_categories = user.category_set.annotate(entry_count=Count('entries'))
# then pass the user_categories queryset into your template along with user
you can then iterate over user_categories in the template to display individual counts:
{% for category in user_categories %}
No. of entries: {{ category.entry_count }}
{% endfor %}
For the latter you can use aggregate, again in the view:
from django.db.models import Count
total = user.category_set.aggregate(entry_count=Count('entries'))
# note that aggregate method returns a dict:
print total['entry_count']
# then pass total['entry_count'] as a value into your template along with user