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')
)
Related
I'm having trouble doing a query spanning between a lot of models.
This is the query I do to display all animals and their corresponding vaccines with encounter.status 'in-progress' and the date immunization date is in the futur.
def current_with_futur_vaccines(self):
return (
Encounter.objects.filter(
status="in-progress").filter(
subject__immunizations__recorded__gte=datetime.now(),
)
.select_related("subject")
.prefetch_related("subject__immunizations", "location")
)
The things is when I want to list the immunizations from the query I get all the immunizations for this animal and not only the immunizations that have to take place in the futur.
{% for immunization in object.subject.immunizations.all %}
{{ immunization }}
{% endfor %}
This is the model
class Animal(models.Model):
name = models.CharField(max_length=250)
class Encounter(models.Model):
subject = models.ForeignKey(Animal, on_delete=models.PROTECT)
status = models.CharField(max_length=11)
class Vaccine(models.Model):
name = models.CharField(max_length=250)
class Immunization(models.Model):
subject = models.ForeignKey(
Animal, on_delete=models.PROTECT, related_name="immunizations"
)
recorded = models.DateTimeField(default=timezone.now)
vaccine = models.ForeignKey(Vaccine, on_delete=models.PROTECT)
EDIT
When I call all() I don't expect to have all the results related to the FK. What I want is the results filtered by the query. In my example I have all the encounters filtered with recorded date in the futur but when I can the related Imunization entries with .all() I don't get the filtered Immunization results filtered by recorded but all of them.
EDIT 2:
I think I have figured it out by using a filtered prefetch. Something like this.
def current_with_futur_vaccines(self):
immunizations_prefetch = models.Prefetch("subject__immunizations", Immunizations.objects.filter(recorded__gte=datetime.now())
return (
Encounter.objects.filter(
status="in-progress").filter(
subject__immunizations__recorded__gte=datetime.now(),
)
.select_related("subject")
.prefetch_related(immunizations_prefetch, "location")
)
You don't have a Foreign Key/M2M reference to Immunizations in you Animal Model.
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 3 Models Product,Company Categories.
class Product(Meta):
categories = models.ManyToManyField(Category)
company = models.ForeignKey(Company, related_name='products', on_delete=models.CASCADE)
updated_at = models.DateTimeField(auto_now_add=False, auto_now=True)
I need:
to get all the products of a company
show the product first category
count the number products per company and show
order products by reverse updated_at
I start from:
1. Company.objects.get(pk=company_pk).prefetch_related('products')
will give me an error, because get returns an object:
class CompanyProductListView(ListView):
model = Company
template_name_suffix = '_company_list'
def get_queryset(self):
company_pk = self.kwargs.get('pk')
return Company.objects.get(pk=company_pk).prefetch_related('products')
get without prefetch works.
return Company.objects.filter(pk=company_pk).prefetch_related('products')
there is no error, but in template:
{% for company in company_list %}
{{ company.name}}
{% endfor %}
I loop even is one, but doesn't show me anything.
Besides that I need to attach first category to each product, and count the number of products
I'm thinking on access something like this:
{{company.name}}
{% for product in company.products %}
{{ product.name }}
{{ product.category }}
This query will get a little complicated, but should help you solve your issue.
PS: I haven't tested this but should mostly work. Will take a deeper look once I get some more time.
First we get the company we want:
company = Company.objects.get(pk=company_pk)
Then we fetch all the first categories for all products, it can be done by using this question as a guide:
first_categories = Category.objects.order_by('product__id', '-id').distinct('product__id')
Now we use the first_categories to use to limit the amount of data we prefetch (giving this a different perspective, we will query the Product model instead of the Company model)
product_list = Products.objects.filter(company=company).prefetch_related(
Prefetch('categories', queryset=first_categories)
)
def get_queryset():
company_pk = ...
company = ...
first_categories = ...
product_list = ...
return product_list
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