Nested loop in a dictionnary template django - python

In my view.py, my context look like this:
context = {'books': books, 'asked_author': asked_author, 'role': role_field_list}
books is a list of dictionary (a list of book) and for each book, it exists one or more keys for which the key is named by a name in role_field_list:
I try to execute this template:
{% for book in books %}
{% for name in role %}
<h1>{{name}}</h1>
{% for authors in book.name %}
{{ authors.lastname }} {{authors.firstname}}
{% endfor %}
{% endfor %}
{% endfor %}
But book.name doesn't worked.
It takes name as a litteral 'name' not as a variable...
Thank you for any help.
To have more details about "field" and "type" of my context dictionnary, this my view.py:
def booksAuthor(request, author):
books_role = AuthorRoleBook.objects.filter(author_book=author).values() # get all the books written by one author
asked_author = AuthorBook.objects.get(pk=author) # get lastname and firstname for the author selected
books=[]
for book_role in books_role:
book = Book.objects.get(pk=book_role['book_id'])
book_dict = model_to_dict(book)
authors_role = AuthorRoleBook.objects.filter(book_id=book).values() # Get id of the different contributor for each book
role_field_list = ['auteur', 'traducteur', 'editeur_scientifique', 'directeur_publication']
for name in role_field_list:
list_author=[]
for author_role in authors_role:
if author_role['role_id']==name:
author=AuthorBook.objects.get(pk=author_role['author_book_id'])
author_dict = model_to_dict(author)
list_author.append(author_dict)
else:
pass
book_dict[name]=list_author
books.append(book_dict)
print(book_dict)
context = {'books': books, 'asked_author': asked_author, 'role': role_field_list}
return render(request, 'books_author.html', context)
And for my models, maybe it is not the easier way but this is:
class AuthorRoleBook(models.Model):
author_book = models.ForeignKey(AuthorBook)
role = models.ForeignKey('Role')
book = models.ForeignKey('Book')
class AuthorBook(models.Model):
lastname = models.CharField(max_length=100, blank=True, null=True)
firstname = models.CharField(max_length=100, blank=True, null=True)
.... ....
unique_together = (('lastname', 'firstname'),)
class Role(models.Model):
id = models.CharField(primary_key=True, max_length=100)
class Book(models.Model):
titre = models.TextField(blank=True, null=True)
....... .... ...
isbn_electronique = models.CharField(max_length=250, blank=True, null=True)
Thank you in advance.

For me it is working now. I keep the same views.py but in my template I do this:
{% for book in books %}
<h4>{{ book.titre }}</h4>
{% if book.directeur_publication %}
Directeur Publication:
{% for name in book.directeur_publication %}
{{ name.lastname }} {{name.firstname}}
{% endfor %}
{% endif %}
{% if book.editeur_scientifique %} ....
...
.....Etc
And for each value in role_field_list (['auteur', 'traducteur', 'editeur_scientifique', 'directeur_publication']) I put manually the value n my template in a "if condition".
It is not really a beautiful way... but it is working. For sure to use model object and not dictionary will be better.
Thanks for your answer, nevertheless if you have a smart code or idea to do that...

Related

Check if logged in user is the author of a post | Django | If-else doesn't work

I want to check if the logged in user is the author of a post in my Forum. I have written some code to figure that out:
<div class="right-section-posts">
user: {{ user }} <!--Output: Admin-->
author: {{ post.author }} <!--Output: Admin-->
{% if user == post.author %}
<form action="DELETE">
{% csrf_token %}
<button type="submit" class="delete-btn" name="post-id" value="{{ post.id }}">Delete</button>
</form>
<button class="edit-btn">Edit</button>
{% endif %}
</div>
They both output the same but the statement returns false! Why?
Models.py
class Post(models.Model):
vote_count = models.IntegerField(default=0)
id = models.BigAutoField(primary_key=True)
created_at = models.DateField(default=date.today)
title = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
tags = models.CharField(max_length=200)
author = models.CharField(max_length=100, default="none")
def __str__(self):
return str(self.id) + ' ' + self.title
I tried different ways to get the Current user and the author. Doesn't work to.
(I think you might say that I should use ForeignKey instead of ´CharField´, when using that I get this Error:
ERROR: Column forum_app_post.author_id does not exist.
LINE 1: ...app_post". "description", "forum_app_post". "tags", "forum_app...
^
HINT: Perhaps the intention was to refer to the "forum_app_post.author" column.
)
The author field cannot be a CharField because it represents the user. You need to set author field as foreignkey.
You need to update your model like this:
from django.contrib.auth.models import User
class Post(models.Model):
vote_count = models.IntegerField(default=0)
id = models.BigAutoField(primary_key=True)
created_at = models.DateField(default=date.today)
title = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
tags = models.CharField(max_length=200)
author = models.ForeignKey(User,on_delete= models.CASCADE, verbose_name='Post Author')
def __str__(self):
return str(self.id) + ' ' + self.title
If you want to check the logged in user from all the registered posts, you should get all the posts first.
def get_all_posts(request):
posts = Post.objects.filter.all()
context = {
"posts" : posts,
}
return render(request,"my_page.html",context)
Then in the html page :
<div class="right-section-posts">
{% if posts %}
{% for post in posts %}
{% if request.user == post.author %}
<!--do what you want here-->
{% else %}
{% endif %}
{% endfor %}
{% else %}
<div class="alert alert-info">You have no registered post yet!</div>
<!-- /.container-fluid -->
</div>
{% endif %}
</div>
I also recommend using django-taggit for tags.

Creating a context object with values from several related models

I can't seem to figure this out. My view takes an argument for Team.id, and I want to return a context object with each User object with a certain value in User.profile.team and the associated date from Reports. I feel like I started on the right track, but am missing something. The output of my template contains all the data I'm trying to get, but not in a way that can be displayed logically.
Basically, I'm using models similar to the following:
class Reports(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.PROTECT)
product = models.CharField(max_length=15)
apps_activated = models.IntegerField(blank=True, null=True)
prem_submitted = models.DecimalField(max_digits=30, decimal_places=2)
class Team(models.Model):
name = models.CharField(max_length=255)
leader = models.ForeignKey(User,on_delete=models.PROTECT)
Extended user profile:
class Profile(models.Model):
COORDINATOR = 1
LEADER = 2
ADMIN = 3
ROLE_CHOICES = (
(COORDINATOR, 'Coordinator'),
(LEADER, 'Leader'),
(ADMIN, 'Admin'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.PROTECT,null=True)
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, null=True, blank=True)
The closest I got in my view to return the desired data is with the following:
team = 1
team_name = Team.objects.get(id=team)
team_users = User.objects.filter(profile__team=team).all()
team_stats = []
for user in team_users:
team_stats.append(Reports.objects.filter(user_id=user.id))
With a template that looks like:
{% block content %}
<h1>{{ team }}</h1>
<ul>
{% for user in team_users %}
<li>{{ user.first_name }} {{ user.last_name }}</li>
{% endfor %}
</ul>
<ul>
{% for stat in team_stats %}
<li>
{% for line in stat %}
{{ line.product }} {{ line.type }} #etc #etc
{% endfor %}
</li>
{% endfor %}
</ul>
{% endblock %}
I thought I was on to something with prefetch_related(), but couldn't figure it out. Ideally, I'd only have to return one context object to my template.
Edit:
If it makes it more clear, this query returns the results I'm attempting to pass to the template:
select auth_user.first_name, auth_user.last_name, r.product, r.apps_activated, r.prem_submitted, r.conversion_percentage, r.type
from auth_user
join home_profile
on auth_user.id = home_profile.user_id
join reports_reports as r
on auth_user.id = r.user_id
where home_profile.team_id = 1
which returns rows that look like:
first_name-last_name-product-apps_activated-prem_submitted-conversion_rate-type
user_1-user_1_last-product_1-693-139764.00-53.86-type1
user_1-user_1_last-product_2-74-27400.10-0.00-type1
user_1-user_1_last-product_3-102-19782.00-47.00-type2
user_2-user_2_last-product_1-7-2437.70-0.00-type2
user_2-user_2_last-product_2-52-10608.00-42.54-type3
user_2-user_2_last-product_3-260.40-0.00-type3
Potential Solution 1:
So, I would do the following. Change user in your Reports model to link explicitly to Profile (which, in turn, links to the user)
class Reports(models.Model):
profile = models.ForeignKey(Profile, null=True, on_delete=models.PROTECT)
product = models.CharField(max_length=15)
apps_activated = models.IntegerField(blank=True, null=True)
prem_submitted = models.DecimalField(max_digits=30, decimal_places=2)
It seems likely that you are looking for a are probably looking for something like a select_related() query, adding in an extra ForeignKey field for the Profile:
reports = Reports.objects.select_related('profile')
You can check the resulting SQL via str(reports.query), which should result in sth along the lines of what you outlined in your question.
The returned cursor values are then translated into the appropriate ORM model instances, so that when you loop over these reports, you access the related tables' values via their own objects. However, these accesses along the pre-selected forward relations will not cause extra db hits:
{% for report in reports %}
{{ report.profile.user.username }}
{{ report.product }}
# ...
{% endfor %}
Let me know how you get on, and if we can brainstorm a solution more suitable, if this one isn't.
Potential Solution 2:
Perhaps another solution, and maybe the simplest of the two, would be to have a ManyToMany relationship in your Profile model to Reports:
class Profile(models.Model):
COORDINATOR = 1
LEADER = 2
ADMIN = 3
ROLE_CHOICES = (
(COORDINATOR, 'Coordinator'),
(LEADER, 'Leader'),
(ADMIN, 'Admin'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.PROTECT,null=True)
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, null=True, blank=True)
reports = models.ManyToManyField(Reports, ...)
Then you should be able to loop over users in the template:
{% for user in users %}
{% for report in user.reports.all %}
{{ report.product }}
{% endfor %}
{% endfor %}

Foreign key not rendering in template

I'm building a commenting system, which is working fine but i'm now trying to integrate voting. So I made another model to handle that and I tried to pair it using ForeignKey. Not too familiar with ForeignKey but i've looked at some other answers here to see how to render it in the template. I tried that using the nested for loop in my template below but {{ j.user }} doesn't render anything. Can anyone tell me what I'm doing wrong?
models.py
class Comment(models.Model):
destination = models.CharField(default='1', max_length=12, blank=True)
author = models.CharField(max_length=120, blank=True)
comment_id = models.IntegerField(default=1)
parent_id = models.IntegerField(default=0)
comment_text = models.TextField(max_length=350, blank=True)
timestamp = models.DateTimeField(default=timezone.now, blank=True)
def __str__(self):
return self.comment_text
class CommentScore(models.Model):
user = models.ForeignKey(User)
comment = models.ForeignKey(Comment)
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
views.py
...
comment_list = Comment.objects.filter(destination=id)
score = CommentScore.objects.all()
context = {
'score': score,
'comment_list': comment_list,
}
return render(request, 'article.html', context)
template
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
{% for j in comment_list.score_set.all %}
{{ j.user }} #nothing comes up
{% endfor %}
<p>{{ i.comment_text }}</p>
</div>
{% endfor %}
when using _set, the reverse relationship lookup, you must provide the full model name, you must also specify which instance this list of related models this "set" is for so it should be
{% for j in i.commentscore_set.all %}
{% empty %}
No scores found
{% endfor %}
You may also wish to set a related_name for the foreign key
comment = models.ForeignKey(Comment, related_name='scores')
...
{% for j in i.scores.all %}

Filter the data and display result ordered by no. of post

In Django I need to filter the data and display the result like. for example
Company3(20)
Company1(12)
Company2(3)
Here "Company1,Company2,Company3" are Company Names and inside the brackets "20,12,3" are no. of jobs posted by the particular company.
models.py
class User(models.Model):
first_name= forms.CharField(max_length=30,widget=forms.TextInput())
last_name = forms.CharField(max_length=30,widget=forms.TextInput())
username = forms.CharField(max_length=30,widget=forms.TextInput())
email = forms.EmailField(widget=forms.TextInput())
password = forms.CharField(widget=forms.PasswordInput())
companyname = forms.CharField(max_length=30,widget=forms.TextInput())
class jobs(models.Model):
emp = models.ForeignKey(User, unique=False)
title = models.CharField(max_length=30)
referencecode = models.CharField(max_length=30)
jobsummary = models.TextField()
jobdetails = models.TextField()
key_skills = models.CharField(max_length=30)
I tried to give views.py is like
def search_result(request):
details=User.objects.filter(jobs__isnull=False).select_related()
return render_to_response('searchresult.html', {'details': details})
templates
<ul>
{% for d1 in details %}
<li>{{ d1.companyname }}({{ d1.count }})</li>
{% endfor %}
</ul>
Give some ideas to display the results as like above
perhaps a more efficient would look like
details = User.objects.filter(jobs__isnull=False).annotate(job_count=Count('jobs'))\
.order_by('job_count')
and then in the template
<ul>
{% for d1 in details %}
<li>{{ d1.companyname }}({{ d1.job_count }})</li>
{% endfor %}
</ul>
You should use d1.jobs_set.count instead, to get the count of jobs.
So update your template to:
<ul>
{% for d1 in details %}
<li>{{ d1.companyname }}({{ d1.jobs_set.count }})</li>
{% endfor %}
</ul>
You really should have a Company model; which would have made this a simple task with the help of the aggregation api; but for your case you'll need to do it in your view:
from collections import defaultdict
from django.shortcuts import render
def search_result(request):
company_count = defaultdict(int)
for obj in User.objects.filter(jobs__isnull=False).select_related():
company_count[obj.companyname] += 1
return render(request, 'searchresult.html', {'details': company_count})
Your template would become:
{% for company_name in details %}
{{ company_name }}({{ details.company_name }})
{% endfor %}

Complex django query in a many to many relationship

I have the following models
class Book(models.Model):
name = models.CharField(max_length=140)
class UserProfile(models.Model):
favorites = models.ManyToManyField(Book, null=True, blank=True)
user = models.OneToOneField(User)
I need to craete a list of all books and show which ones are favorites and which ones are not.
I need a queryset for a view that gets me all the books like
Book.objects.all()
but i also need to know for each book if it is a favorite for that user and then pass this queryset to the template.
Thanks.
This is a relatively straightforward use of the ManyToManyField.
class Book(models.Model):
name = models.CharField(max_length=140)
class UserProfile(models.Model):
favorites = models.ManyToManyField(Book, null=True, blank=True)
user = models.OneToOneField(User)
favorite_books = this_user_profile.favorites.all()
for b in Book.objects.all():
if b in favorite_books:
print "Book", b.name, "is a favorite of this user!"
else:
print "Book", b.name, "is not a favorite of this user!"
ETA: Since you say you want to add it to the template, give it to the template as a list of tuples.
book_list = [(b, (b in favorite_books)) for b in Book.objects.all()]
In your template, have the code
{% for book, is_favorite in book_list %}
{% if is_favorite %}
{% comment %} Do something with this favorite book {% endcomment %}
{% else %}
{% comment %} Do something with this non favorite book {% endcomment %}
{% endif %}
{% endfor %}

Categories