Django problem with foreginKey in templates - django

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!

Related

Django query from multiple table base on FK

hi i am new in django coding
i have 3 tables(models) in my different Django app , i try to make a simple report in htmpl page , so need to retrive specific data for each item base on foreign key.
by below code i can accress machine stored table in Html page by
making a loop , but i want to fetch data from other table and filter them ,
{% for m in machines %}
<h2>{{m.title}}</h2>
<h3>{{}}</h3>.
//i** need to access the ordertitle field in tb_order base on machine id. ??**
<p>{{ }}</p>.
// **access ordertitle in tb_order base on status field ??**
{%end for %}`
here is a view.py
def allmachinesLOGO(request):
machines=Machine.objects.all()
context ={'machines':machines}
return render(request,'pline/machineslogos.html',context)
Models :
class tb_order(models.Model):
ordertitle= models.CharField(max_length=200)
customer=models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL)
mould=models.ForeignKey(Mould, null=True, on_delete=models.SET_NULL, blank=True)
status=models.CharField(choices=status_choices, default="0", max_length=20)
accept=models.BooleanField
machine=models.ForeignKey(Machine,null=True, on_delete=models.SET_NULL, blank=True)
id =models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False, unique=True)`
class Machine(models.Model):
title = models.CharField(max_length=200)
machine_model = models.CharField(max_length=200, null=True, blank=True)
description = models.CharField(max_length=600, null=True, blank=True)
featured_image=models.ImageField(default="defaultmachine.webp", blank=True, null=True)
id=models.UUIDField(default=uuid.uuid4, unique=True,primary_key=True,editable=False)`
advice a solution
access ordertitle in tb_order base on status field ??
// i need to access the ordertitle field in tb_order base on machine id.
By using the primary model, you can get the data associated with it from the secondary model. To do this, a special property (object) with the name secondary model_set is created in the primary model by default. In your case, for example: tb_order_set(lower case). Using related_name for the ForeignKey (in which case it will be whatever you write it without the _set prefix).
In the template, the outer loop loops through the rows of the model: Machine. The internal one, through tb_order_set.all, accesses the rows of the tb_order model. Although in my opinion it is better to do all calculations and comparisons in views, and in templates only display information.
Template file:
{% for m in machines %}
<p>{{ m.tb_order_set.all }}</p>
{% for r in m.tb_order_set.all %}
<p>{{ 'ordertitle' }} : {{ r.ordertitle }} {{ 'status' }} : {{ r.status }}</p>
{% endfor %}
{% endfor %}
To show by specific status:
{% for m in machines %}
<p>{{ m.tb_order_set.all }}</p>
{% for r in m.tb_order_set.all %}
{% if r.status == '2' %}
<p>{{ 'ordertitle' }} : {{ r.ordertitle }} {{ 'status' }} : {{ r.status }}</p>
{% endif %}
{% endfor %}
{% endfor %}

Django templatetags seem to make the prefetch_related useless

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 %}

Django remove duplicates in queryset and access their list of foreign key

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 %}

Get aggregate in a template

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 %}

Django getting list of records (with FK) related to object -not rendering template correctly

I am trying to follow this previous question here:
Django: getting the list of related records for a list of objects
but can't seem to get it to work.
I get a list of owners but do not get a list of pet names. The html code doesn't seem to execute the 2nd FOR loop. Any ideas?
models.py
class Teacher(models.Model):
teacher = models.CharField(max_length=300, verbose_name="teacher")
def __unicode__(self):
return self.teacher
class Owner(models.Model):
relevantteacher = models.ForeignKey(Teacher, verbose_name="teacher")
owner = models.CharField(max_length=300, verbose_name="owner")
def __unicode__(self):
return self.owner
class PetName(models.Model):
relevantowner = models.ForeignKey(Owner, verbose_name="owner")
pet_name = models.CharField(max_length=50, verbose_name="pet Name")
def __unicode__(self):
return self.pet_name
views.py
def ownersandteachers(request):
owners = Owner.objects.all()
context = {'owners': owners}
return render(request, 'ownersandpets.html', context)
template
{% for i in owners %}
{{ i.owner }} has pets:<br />
{% for v in owners.petname_set.all %} //this doesn't seem to be executing
- {{ v.pet_name }}<br />
{% endfor %}
{% endfor %}
You are doing
{% for v in owners.petname_set.all %}
where you should be doing
{% for v in i.petname_set.all %}
owners is the queryset, but you need to get the petname_set for the individual object in the queryset which is i in this case.
Also, I would recommend a condition check if i.petname_set exists. If not, do not show the has pets: text.
{% for i in owners %}
{{ i.owner }}
{% if i.petname_set.count %} has pets:<br />
{% for v in i.petname_set.all %} //this doesn't seem to be executing
- {{ v.pet_name }}<br />
{% endfor %}
{% endif %}
{% endfor %}