I have this Django model:
class Location(models.Model):
name = models.CharField(primary_key=True, max_length=100)
customer = models.OneToOneField(Customer, default=None)
class Customer(models.Model):
name = models.CharField(primary_key=True, max_length=100)
class Order(models.Model):
amount = models.PositiveIntegerField(default=0)
customer = models.ForeignKey(Customer, default=0)
in my view I get them like this:
locations = models.Location.objects.all()
and the template lists them like this:
{% for location in locations %}
{{ location.customer.name }}
{% endfor %}
I would like to add a sum of all amount of all Orders connected that customer, something like:
{% for location in locations %}
{{ location.customer.name }} ordered {{ location.customer.orders.sum(amount) }} items
{% endfor %}
And according to this question, I should do that in the view, but how?
You should use .annotate (look in docs):
from django.db.models import Count
customers = models.Customer.objects.annotate(orders_count=Count('order'))
Then in templates you can use it like this:
{% for customer in customers %}
{{ customer.name }} ordered {{ customer.orders_count }} items
{% endfor %}
After some folling around I found this works:
locations = models.Location.objects.annotate(num_order=Count('customer__order'))
and then use this in the template:
{% for location in locations %}
{{ location.customer.name }} ordered {{ location.num_order }} items
{% endfor %}
Related
I am started learning Django and I have question. I have code like this:
from django.db import models
from django.utils import timezone
class Worker(models.Model):
firstname = models.CharField(max_length = 32, blank=False, null=False)
lastname = models.CharField(max_length = 32, blank=False, null=False)
class Computer(models.Model):
TYPE = {
(0, 'PC'),
(1, 'Laptop'),
}
workerId = models.ForeignKey(Worker, on_delete=models.SET_NULL, blank=True, null=True)
prod = models.CharField(max_length = 32, blank=False, null=False)
type = models.PositiveSmallIntegerField(default=0, choices=TYPE)
What I should do in views and templates if I want to do list with workers and theirs computers? One computer can be for one user or for no one but one worker can have a few computers.
My template for this look like:
{% block site %}
{% for work in worker %}
<p>{{ work.id }} {{ work.fullname }}
{{ work.lastname }}</p><br>
{% if comp.workerId == work.id%}
{{ comp }}
{% else %}
<p>Empty</p>
{% endif %}
{% endfor %}
{% for c in comp %}
<p>{{ c }}</p>
{% endfor %}
{% endblock %}
But I always have "Empty". Thanks for help
You can try like this with for-empty in template:
{% for work in worker %}
<p>{{ work.id }} {{ work.fullname }}
{{ work.lastname }}</p><br>
{% for comp in work.computer_set.all %}
{{ comp }}
{% empty %}
<p>Empty</p>
{% endfor %}
{% endfor %}
I've actually explained this before in 2 questions, here and here.
Basically, a ForiegnKey object is a key because it's unique for the class owning it, This means your computer has only one worker, BE SURE TO CALL IT WORKER, NOT WORKERID as this is OOP and when you access mycomputer.worker, you have a Worker object which has .id and .pk so it makes no sense to call it workerId.
Each worker now has a .computer_qs which you can change like this
workerId = models.ForeignKey(Worker,
on_delete=models.SET_NULL,
blank=True, null=True, related_name="computers")
don't forget to call it worker. now it's .computers instead of .computer_qs.
In your templates iterate workerObj.computers.all to access single computer objects and have fun with it!
I have django models like this:
class FittingBag(models.Model):
...
fittings = models.ManyToManyField("app.Fitting", through="FittingInBagInstances")
class FittingInBagInstances(models.Model):
fitting = models.ForeignKey("app.Fitting", on_delete=models.CASCADE)
bag = models.ForeignKey("app.FittingBags", on_delete=models.CASCADE)
qty = models.DecimalField(verbose_name='Quantity' );
Is there any way to access intermediate fields (like "qty" ) from Django template without preparing the date in the view?
You can simply iterate over the releverse relation that is constructed by the many-to-many intermediate model:
{% for fittingbag in fittingbags %}
Rendering the fittingbag: {{ fittingbag }}
{% for fittinginbag in fittingbag.fittinginbaginstances_set.all %}
qty: {{ fittinginbag.qty }}
fitting: {{ fittinginbag.fitting }}
{% endfor %}
{% endfor %}
When I add a filter based on a template tag ("provinceonly") to my Django template, debug-toolbar indicates that the database is hit 115 time instead of 5 previously.
Is it a well-known issue ? I cant' find a way to prefetch correctly the required information to reduce the number of hits. Is there something I can do about it ?
Template:
{% for country in allcountries %}
{% for province in country.fkcountrysecondaries.all|provinceonly %}
{{ province.basicname }}
{% for populatedplace in province.fkprovince.fkprovincesecondaries.all %}
{{ populatedplace.basicname }}
{% endfor %}
{% endfor %}
{% endfor %}
Template tag:
from django import template
register = template.Library()
#register.filter
def provinceonly(thelist):
return thelist.filter(type="province")
View:
def indexconsistencycheck(request):
allcountries = NaturalEarthCountry.objects.all().prefetch_related('fkcountrysecondaries__fkprovince__fkprovincesecondaries').select_related('fkcountrymain')
Model:
class NaturalEarthAll(models.Model):
fkcountry = models.OneToOneField(NaturalEarthCountry, blank=True, null=True, on_delete=models.SET_NULL, related_name="fkcountrymain")
fkprovince = models.OneToOneField(NaturalEarthProvince, blank=True, null=True, on_delete=models.SET_NULL, related_name="fkprovincemain")
fktouristicarea = models.ForeignKey(TouristicArea, blank=True, null=True, related_name='relatednatmerged', on_delete=models.SET_NULL)
fkcountrysecondary = models.ForeignKey(NaturalEarthCountry, blank=True, null=True, on_delete=models.SET_NULL, related_name="fkcountrysecondaries")
fkprovincesecondary = models.ForeignKey(NaturalEarthProvince, blank=True, null=True, on_delete=models.SET_NULL, related_name="fkprovincesecondaries")
As explained by Sachin, the templatetag generates a new queryset which loses prefetched data. I therefore had to perform the main queryset directly on the model I needed to filter (provinces) and not on the previous one (countries), and then regrouped the results by country.
Thanks a lot
view:
allprovinces_list = NaturalEarthMerged.objects.filter(type="province").order_by('fkcountrysecondary__name').prefetch_related('fkprovince__fkprovincesecondaries', 'fkcountrysecondary__fkcountrymain').select_related('fkcountrysecondary')
template:
{% regroup allprovinces by fkcountrysecondary.name as countrylist %}
{% for listofprovinces in countrylist %}
{{ listofprovinces.grouper }}
{% for province in listofprovinces.list %}
{{ province.basicname }}
{% for populatedplace in province.fkprovince.fkprovincesecondaries.all %}
{{ populatedplace.basicname }}
{% endfor %}
{% endfor %}
{% endfor %}
Here is my model:
class Item(models.Model):
title = models.TextField()
class Storage(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
rating = models.IntegerField(blank=True, null=True)
review = models.CharField(max_length=1500, blank=True, null=True)
class SocialProfile(models.Model):
user = models.OneToOneField(User, unique=True)
follows = models.ManyToManyField('self', related_name='followed_by', symmetrical=False)
Say there are 4 users A,B,C,D. User A follows only B and C.
I'm trying to do a view that when log in as User A. Show a list of items that B and C has (duplicates show only one time). And under each item, show their summary and ratings.
I have something here: (user = User A here)
//get a list of users that User A follows
followings = user.socialprofile.follows.all()
//get B and C's saved item, eliminate the duplicates
context['following_saved_article'] = Item.objects.filter(storage__user__in=followings.values('user')).distinct()
//get a flat list of all users A follows
context['followings'] = list(followings.values_list('user', flat=True).order_by('user'))
Then in the template:
{% for item in following_saved_item %}
{{ item.title }}
{% for x in item.storage_set.all %}
{% if x.user_id in followings %}
{{ x.review }} {{ x.rating }}
{% endif %}
{% endfor %}
{% endfor %}
This seems too redundant, and I will have a pretty hard time if I want to sort reviews by user rating.
Is there any way to generate a list of distinct Item instance, fetch only the storage_set based on following, sort by rating or other field in storage, and pass that to view? Thanks
I think a custom Prefetch object will do the trick
followings = user.socialprofile.follows.all()
items = Item.objects.filter(
storage__user__in=followings.values('user')
).distinct().prefetch_related(
models.Prefetch('storage_set',
queryset=Storage.objects.filter(
user__in=followings.values('user')
),
to_attr='storage_from_followings'
)
)
#in the template
{% for item in items %}
{{ item.title }}
{% for x in item.storage_from_followings %}
{{ x.review }} {{ x.rating }}
{% endfor %}
{% endfor %}
I have this models:
class House(models.Model):
name = models.CharField()
class BedRoom(models.Model):
name = models.CharField()
class Catalog(models.Model):
name = models.CharField()
house = models.ForeignKey(House)
bedroom = models.ForeignKey(BedRoom)
at admin.py Catalog is inline to House
class CatalogInline(admin.TabularInline):
model = Catalog
class HomeAdmin(admin.ModelAdmin):
inline = [CatalogInline]
then I need obtain at view the list of Bedrooms from House Catalog, at Home model I have:
def bedrooms(self):
return self.catalog_set.all()
but when I do at template obtaining "houses" from a view:
{% for house in houses %}
{% for h in house %}
<p>{{h.name}}</p>
{% endfor %}
{% endfor %}
I get an error:
'Catalog' object is not iterable
what I'm doing wrong?
I should define the model in a different way?
As I mentioned, you don't seem to actually be calling the bedrooms method. I guess (and it is just a guess) you mean this:
{% for house in houses %}
{% for h in house.bedrooms %}
<p>{{h.name}}</p>
{% endfor %}
{% endfor %}
The bedrooms method is pointless though, as you can just as easily do this:
{% for house in houses %}
{% for h in house.catalog_set.all %}
<p>{{h.name}}</p>
{% endfor %}
{% endfor %}