How to get a parent from a child FK? - python

models.py
class Product(models.Model)
name = models.CharField()
class User(models.Model)
name = models.CharField()
class List(models.Model)
user = models.ForeignKey(User)
class Item(models.Model)
name = models.CharField()
list = models.ForeignKey(List)
user = models.ForeignKey(User)
product = models.ForeignKey(Product)
views.py
class ListView(View)
def get(self, request, pk)
list = List.objects.get(id=pk)
return render(request, "list.html", {"list" : list})
list.html
{% for item in list.item_set.all %}
{{ item.name }}
{{ item.user.name }} - ??
{{ item.product.name }} - ??
{% endfor %}
How get user.name и product.name?
I tried it:
{% item.user_set.first.name %} - does not work
{% for user in item.user_set.all %}{{ user.name }}{% endfor %} - does not work
Model method:
def get_user(self):
self.user_ser.get() #does not work
How do I solve this problem?

Since an Item has a ForeignKey to a Product object, you can access the related object with item.product, so you can render this with:
{{ item.product.name }}
{{ item.user.name }}
If you use the standard User module, it is {{ item.user.username }}. It is possible that this is empty, for example if the name of the related user is the empty string ''.
In order to retrieve the elements more efficient however, it might be better to perform a .prefetch_related(..) [Django-doc] to retrieve these items in bulk:
class ListView(View)
def get(self, request, pk)
list = List.objects.prefetch_related('item_set', 'item_set__product', 'item_set__user').get(id=pk)
return render(request, "list.html", {'list' : list})

From user : {{user.items}} # to access to the items objects
From item: {{item.user.name}} # to access to the user name field
class Item(models.Model)
name = models.CharField()
list = models.ForeignKey(List)
user = models.ForeignKey(User, related_name='items')
product = models.ForeignKey(Product)

It works!
My error was that I looked at the field names in the database, not in the file models.py. They are different.
Thank you for your help!
All the answers above are correct!

Related

Django - forloop to show/filter a value to show only once the entrys

I have a model (Restrictie) in which those who voted (participanti) and those who voted (votant) are registered. If I forloop in the templates, I can see who voted for whom, but one line is displayed for each record. How can I make a forloop or query in which participants are displayed only once and next to all the voters for example:
name participanti - (name votant 1, name votant 2, ....)
Below my code
models.py
class Participanti(models.Model):
campanievot = models.ForeignKey(Campanie, on_delete=models.CASCADE)
nume_participant = models.CharField(max_length=200, verbose_name='nume')
dep_participant = models.CharField(max_length=200, verbose_name='departament')
votes = models.IntegerField(default=0)
def __str__(self):
return self.nume_participant
class Meta:
verbose_name = 'Participanti'
verbose_name_plural = 'Participanti'
class Restrictie(models.Model):
participanti = models.ForeignKey(Participanti,on_delete=models.CASCADE, verbose_name='Votat')
votant = models.ForeignKey(UserVot,on_delete=models.CASCADE)
def __str__(self):
return str(self.participanti)
class Meta:
verbose_name = 'Voturi efectuate'
verbose_name_plural = 'Voturi efectuate'
views.py
def rezultate(request):
cineavotat = Restrictie.objects.all()
.......
I tried this but it's show all entries like : name 1 = voters 1, name 1 = voters 2 ...
{% for p in cineavotat %}
{{ p.participanti }} - {{ p.votant}}
{% endfor %}
Please help me with a solution. Thank you!
In Views.py
def rezultate(request):
cineavotat = Restrictie.objects.all()
context = {'cineavotat': cineavotat}
return render(request, 'appname/templatename.html', context)
Maybe you better to select a list of Participanti and for each of them add list of Restrictie in your view, like this:
def rezultate(request):
cineavotat = []
for p in Participanti.objects.all():
cineavotat.append((p, p.restrictie_set.all()))
return render(request, 'appname/templatename.html', {'cineavotat': cineavotat})
And then you could do this in your template
{% for participanti, restrictie in cineavotat %}
{{ participanti }} -
{% for r in restrictie %}
{{r.votant}};
{% endfor %}
</br>
{% endfor %}
The result is
Some Participanti - User 1; User 2; User 3;
Another Participanti - User 2; User 3;

django querysets in templates

I am trying to make specific queries by using some model entry fields.
I have the following model entry:
models.py
class Work(models.Model):
categories =(
('cat1', 'cat1'),
('cat2', 'cat2'),
('cat3', 'cat3'),
('cat4', 'cat4'),
('cat5', 'cat5'),
)
title = models.CharField(max_length=200)
description = RichTextUploadingField(config_name='awesome_ckeditor')
date = models.DateTimeField(default=timezone.now)
category = models.CharField(max_length=200, choices = categories, default = 'projects')
thumb = models.ImageField(upload_to = 'works/thumbs', blank = True)
content = models.FileField(upload_to = 'works/content_media', blank = True)
published = models.BooleanField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("work_detail",kwargs={'pk':self.pk})
#property
def thumb_url(self):
if self.thumb and hasattr(self.thumb, 'url'):
return self.thumb.url
#property
def content_url(self):
if self.content and hasattr(self.content, 'url'):
return self.content.url
here is the view:
views.py
class WorksListView(ListView):
template_name = 'template.html'
model = Work
def get_queryset(self):
return Work.objects.filter(published=True).order_by('-date')
and I am trying to query first by the category field then by entry in the following template:
template.html
{% for category in works_list.category %}
<ul data-category-name={{category.name}}>
{% for work in category.works %}
<li data-thumbnail-path={{thumbnail.url}} data-url={{content.url}} >
<div>
<p class="gallery1DecHeader">{{work.title}}</p>
<p class="gallery1DescP">{{work.description}}</p>
</div>
</li>
{% endfor %}
{% endfor %}
what do I need to change?
Okay, from what I can see there are a few problems. First, try adding context_object_name = 'works_list' That way you will be able to refer to the object_list as works_list like you do in the template outer for loop. The bigger problem is you are iterating over works_list.category, which according to your Work model is a Charlist(). I think you might be getting confused about what the choices kwarg does and expecting {% for category in works_list.category %} to iterate over your choices and giving you the list of cats you defined in categories. As far as I know, that's not how choices works.
If you go to your admin panel and add a new entry for your Work model, you'll see that category has a dropdown list that contains your list of cats. So, choices defines a set of legal category options for new Work objects, not a list in existing Work objects.
I think what you actually want is an additional model Category which defines: work = models.ForeignKey(Work, on_delete=models.CASCADE) as a one-to-many relationship. Basically, you want is for Work to have a subset of Category objects that you can iterate over. This will involve redesigning the way you structure and access your data though.
You need to change at least your views.py and template.html. Add a context_object_name and an extra context(Doc Link)
views.py
class WorksListView(ListView):
template_name = 'template.html'
model = Work
context_object_name = 'work_list'
def get_queryset(self):
return Work.objects.filter(published=True).order_by('-date')
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(WorksListView, self).get_context_data(**kwargs)
# Insert categories so that it can be used in template
context['categories'] = Work.categories
return context
template.html
{% for category in categories%}
<ul data-category-name={{category.0}}>
{% for work in work_list %}
{% if category.0 == work.category %}
<li data-thumbnail-path={{work.thumb_url}} data-url={{work.content_url}} >
<div>
<p class="gallery1DecHeader">{{work.title}}</p>
<p class="gallery1DescP">{{work.description}}</p>
</div>
</li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}

Django/SQL double entry table for template

I have the following models in my django app:
from django.contrib.auth.models import User
class Poll(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(User)
class Choice(models.Model):
poll = models.ForeignKey(Poll, on_delete=models.CASCADE)
text = models.CharField(max_length=200)
class Vote(models.Model):
choice = models.ForeignKey(Choice)
user = models.ForeignKey(User)
For a given Poll, I need to display in the template a double-entry table like the following, listing all Users who have voted in this Poll and placing an 'x' for each Choice that the user has voted:
[choice1] [choice2] [choice3]
[user1] x
[user2] x x
[user3] x
[user4] x x
What would be the optimal way to implement this using the django ORM in order to minimize the database hits when populating the table?
Which object should be passed to the template in the context variable, in order conduct the logic in the view instead of the template?
The queries I know:
# get all votes for the given Poll (pk)
votes = Vote.objects.filter(choice__poll__pk=pk)
# get all users that has voted
usernames = votes.values('user__username')
# get the choices for the Poll
choices = Poll.objects.get(pk=pk).choice_set.all()
I was able to solve the problem with the following:
# yourapp/models.py
from django.utils.functional import cached_property
from django.db.models import Case, When, BooleanField
class Poll(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(User)
#cached_property
def choices(self):
return self.choice_set.order_by('id')
#cached_property
def users_choices(self):
users = User.objects.filter(vote__choice__poll=self)
for choice in self.choices:
users = users.annotate(
**{str(choice.id): Case(When(vote__choice_id=choice.id, then=True), output_field=BooleanField())}
)
return users
For example in PostgreSQL users qs converted in such sql query:
SELECT
"yourapp_user"."id",
"yourapp_user"."username",
CASE WHEN "yourapp_vote"."choice_id" = 1
THEN TRUE
ELSE NULL END AS "1",
CASE WHEN "yourapp_vote"."choice_id" = 2
THEN TRUE
ELSE NULL END AS "2",
CASE WHEN "yourapp_vote"."choice_id" = 3
THEN TRUE
ELSE NULL END AS "3"
FROM "yourapp_user"
LEFT OUTER JOIN "yourapp_vote" ON ("yourapp_user"."id" = "yourapp_vote"."user_id")
LEFT OUTER JOIN "yourapp_choice" ON ("yourapp_vote"."choice_id" = "yourapp_choice"."id")
WHERE "yourapp_choice"."poll_id" = 1
View and template can look like this (you don't even need to pass context to template, everything will be taken from Poll model attributes):
# yourapp/views.py
class PollDetailView(DetailView):
model = Poll
template_name = 'user_choices.html'
# yourapp/templates/user_choices.html
{% extends 'base.html' %}{% load customtags %}
{% block content %}
<h1>{{ poll.title }}</h1>
<table border="1">
<tr>
<th>username</th>
{% for choice in poll.choices %}<th>choice{{ choice.id }}</th>{% endfor %}
</tr>
{% for user_choice in poll.users_choices %}
<tr>
<td>{{ user_choice.username }}</td>
{% for choice in poll.choices %}
<td>{% if user_choice|get_attr:choice.id %}+{% endif %}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endblock %}
I had to add custom template filter to get attr by choice id (propably there is more elegant solution which i missed):
# yourapp/templatetags/customtags.py
from django import template
register = template.Library()
#register.filter(name='get_attr')
def get_attr(obj, attr_name):
return getattr(obj, str(attr_name), None)

Django - reduce the number of queries in ORM

I used related_name in a Django template to look the foreign key record, and call the count method. Because I have so many "Main" record, the for loop inside the template will create too many queries to the database. If there an easy way for me to reduce the number of queries to the database? Please see below for my setup.
# models.py
class Main(models.Model):
name = models.CharField(_('Name'), max_length=255)
class Sub1(models.Model):
main = models.ForeignKey(Main, on_delete=models.CASCADE)
name = models.CharField(_('Name'), max_length=255)
class Sub2(models.Model):
main = models.ForeignKey(Main, on_delete=models.CASCADE)
name = models.CharField(_('Name'), max_length=255)
class Sub3(models.Model):
main = models.ForeignKey(Main, on_delete=models.CASCADE)
name = models.CharField(_('Name'), max_length=255)
# views.py
def get_main(request):
main_list = Main.objects.all()
...
# template
{% for main in main_list %}
{{main.sub1_set.count}}
{{main.sub2_set.count}}
{{main.sub3_set.count}}
{% endfor %}
You can use annotations to do this logic all in one query:
from django.db.models import Count
def get_main(request):
main_list = Main.objects.all().annotate(sub1_count=Count('sub1', distinct=True),
sub2_count=Count('sub2', distinct=True),
sub3_count=Count('sub3', distinct=True))
Then in the template:
{% for main in main_list %}
{{ main.sub1_count }}
{{ main.sub2_count }}
{{ main.sub3_count }}
{% endfor %}
(Edit: added distinct)

Django: Foreign Key Query to post the information on the html template

I'm stuck on my next step of showing the query from the database to the html template. I'm able to put some basic information, but I got stuck on the foreign key queries.
Here is sample of my code:
Here is my model:
class Player_Bios(models.Model):
my_id = models.SlugField(unique=True)
player_id = models.IntegerField(primary_key=True, max_length=50)
name = models.CharField(max_length=50)
last = models.CharField(max_length=50)
def __unicode__(self):
return self.player_id
class BatStat (models.Model):
player_id = models.ForeignKey('Player_Bios')
team_id = models.ForeignKey('Team')
bat_stat_id = models.CharField(max_length=50, unique=True)
sport_code = models.CharField(max_length=50, blank=True)
ab = models.IntegerField(max_length=50, null=True)
def __unicode__(self):
return self.bat_stat_id
My View:
def SpecificPLayer(request, playerslug):
player = Player_Bios.objects.get(player_id=playerslug) #this is the name, last and it is working fine
playerStat = BatStat.objects.filter(player_id=playerslug) #Here I'm calling the foreign key
print playerStat
context = {'player' : player, 'today' : date.today(), 'playerStat' : playerStat }
return render_to_response ('singleplayer.html', context, context_instance=RequestContext(request))
My HTML Template:
{% extends 'base.html' %}
{% block content %}
<div id="singleplayer">
<p>Name: {{ player.name|capfirst }}</p>
<p>Last Name: {{ player.last|capfirst }}</p>
</div>
{% endfor %}
{% endblock %}
Now when I do print playerStat, I get all of the BatStat from the player_id. In y case I get the following:
[<BatStat: 40539520011>, <BatStat: 40539520021>, <BatStat: 40539520031>]
I get the result that I want on the shell by doing the following:
playerStatID=BatStat.objects.filter(player_id='the player id here')
print playerStatID
[<BatStat: 40539520011>, <BatStat: 40539520021>, <BatStat: 40539520031>]
for i in playerStatID:
playerStat= BatStat.objects.get(bat_stat_id=i)
print BatStat.ab
200
So by doing that I can get the information that I need it, now how can I get that, but to put it on the template. I can't use a loop on the template to get a query, so I guess the loop has to be done on the view, but how. Thanks
Since you have a ForeignKey between BatStat and Player_Bios, you can use a batstat_set to get all related BatStats for the single player.
[Docs for _set here.][1]
So you would have your views.py as:
def SpecificPLayer(request, playerslug):
player = Player_Bios.objects.get(player_id=playerslug) #this is the name, last and it is working fine
batstats = player.batstat_set.all()
context = {'player' : player, 'today' : date.today(), 'batstats': batstats, }
return render_to_response ('singleplayer.html', context,context_instance=RequestContext(request))
and in your template:
{% for stat in batstats %}
{{ stat.ab }}
{% endfor %}

Categories

Resources