Django - How to check if id contains in a table column? - python

guys.
I want to check in my django template, if request.user exists in some row of column user in my table LeagueMember. The way I found is not working.
views.py
#login_required(login_url='login/')
def search_leagues(request):
if request.method == 'POST':
return redirect('join_league')
leagues = League.objects.all()
return render(request, 'search_leagues.html', { 'allleagues': leagues })
model.py
class League(models.Model):
league_owner = models.ForeignKey('auth.User')
league_name = models.CharField(max_length=30)
creation_date = models.DateTimeField()
def is_member(self):
member = LeagueMember.objects.get(league=self)
if member:
return True
else:
return False
class LeagueMember(models.Model):
league = models.ForeignKey('League', related_name='leaguemember_league')
user = models.ForeignKey('auth.User')
search_leagues.html
{% for league in allleagues %}
<tr>
<td class="center">{{ league.league_name }}</td>
<td class="center">{{ league.leaguemember_league.count}}/{{ league.leaguesettings_league.league_number_teams }}</td>
<td class="center">{{ league.leaguesettings_league.league_eligibility }}</td>
<td class="center">{{ league.leaguesettings_league.league_lifetime }}</td>
{% if request.user in league.leaguemember_league.user %}
DO SOMETHING!!!
{% else %}
{% if league.leaguemember_league.count < league.leaguesettings_league.league_number_teams %}
{% if league.leaguesettings_league.league_eligibility == "Private" %}
<form method="post" action="{% url 'joinleague' pk=league.id %}">
<td class="center">Soliticar</td>
</form>
{% elif league.leaguesettings_league.league_eligibility == "Public" %}
<form method="post" action="{% url 'joinleague' pk=league.id %}">
<td class="center">Entrar</td>
</form>
{% endif %}
{% endif %}
{% endif %}
</tr>
{% endfor %}
This error is in this line:
{% if request.user in league.leaguemember_league.user %}
Always goes to ELSE block
Thank you all

league.leaguemember_league will not give you a LeagueMember object but a RelatedManager object (so you cannot find a user property in it, therefore your template logic will not work).
What you are trying to do is go two levels deep in your relationship (League -> LeagueMember -> User). You can't easily do this kind of logic in your template and probably need to do it in your view code instead. For example:
league_data = []
for league in League.objects.all():
league_data.append({
'league': league,
'users': User.objects.filter(leaguemember__league=league) # This gives you all the users that are related to this league
})
return render(request, 'search_leagues.html', { 'allleagues': league_data})
You then need to modify all your template logic to use this new structure:
{% for league_data in allleagues %}
# Replace league with league_data.league in all the template logic below this
In the if block you can then do:
{% if request.user in league_data.users %}
Note that this querying may not be very efficient if you have a large number of users/leagues - in which case you may need to rethink your model design.

Related

Accessing items in a list through template tags in Django template tags

Template tag screenshot and possible solution options
First time, posting to stack-overflow. Please excuse if formatting is not ideal.
html
<tbody>
{% for list in todolist %}
<tr>
<td>
{{ list.name }}
</td>
<td>
{{ list.items.all|length }}
</td>
<td>
{% comment %}need this to be allCompleted[list.id - 1]
#allCompleted.0 works. allCompleted[0] or allCompleted['0'] does not.{% endcomment %}
{% if allCompleted == True %}
{{ allCompleted|getindex:list.id }}
Yes
{% else %}
No
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
Views.py:
class TodoListListView(ListView):
model = TodoList
context_object_name = "todolist"
template_name = "todos/list.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
allCompletedList = []
for list in TodoList.objects.all():
allCompleted = True
for item in list.items.all():
if item.is_completed == False:
allCompleted = False
allCompletedList.append(allCompleted)
context['allCompleted'] = allCompletedList
print ('context: ', context)
return context
Models.py
class TodoList(models.Model):
name = models.CharField(max_length=100, blank=False)
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class TodoItem(models.Model):
task = models.CharField(max_length=100)
due_date = models.DateTimeField(null=True, blank=True)
is_completed = models.BooleanField(default=False)
list = models.ForeignKey("Todolist", related_name="items", on_delete=models.CASCADE)
def __str__(self):
return self.task
When printing context:
I get 'allCompleted': [False, True]
This is accurate as I have some items in housing chores not completed but I triple checked to make sure all my coding projects are completed.
As seen from the HTML screenshot, I need something like:
{{ allCompleted[list.id - 1] }} to match with the corresponding list in each row.
But it seems Django does not like that. I've tried many combos like allCompleted['list.id-1']. Strangely, allCompleted.0 = False but allCompleted[0] gets a parse error. I have also tried to create a custom template tag in my app/templatetag folder under a file I made (getindex.py)
from django import template
from todos.models import TodoItem, TodoList
register = template.Library()
def getindex(lst, idx):
return lst[idx]
register.filter(getindex)
For my template tag, I did {{ allCompleted|getindex:list.id-1 }} and it says getindex is not a valid filter so maybe I am registering it incorrectly?
If there is no way to access allCompleted[list.id - 1], I thought of other solutions explained in my HTMl screenshot.
Instead of using template tags for this purpose, it is best to give to the template data in the form it can easily use. The most efficient way to do this would be to leave the task to the database itself and write a query that will give you allCompleted as a column in the result. You can use Exists() subqueries to do this:
from django.db.models import Exists, OuterRef
class TodoListListView(ListView):
model = TodoList
context_object_name = "todolist"
template_name = "todos/list.html"
def get_queryset(self):
queryset = super().get_queryset()
subquery = TodoItem.objects.filter(
list=OuterRef('pk'),
is_completed=False
)
queryset = queryset.annotate(all_completed=~Exists(subquery))
return queryset
Then in your template you can simply write {{ list.all_completed }}:
<tbody>
{% for list in todolist %}
<tr>
<td>
{{ list.name }}
</td>
<td>
{{ list.items.all|length }}
</td>
<td>
{% if list.all_completed == True %}
Yes
{% else %}
No
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>

Django formset with no initial data

I am a Django rookie and I am developing a small app to register time (duration) and quantity of activities per user per day. Sort of like a work log. My problem is this: My “add entry” view displays and updates old records rather than adding new records to the db. I need a view to add new records, not replace old ones.
From searching around and from the #django IRC channel, I understand that the formset-way by default draws on old data rather than setting the client up for adding new data. I have, however, not found anything about how to avoid this behaviour and have the client provide a blank form for "appending new data" rather than "editing existing data".
My deadline is drawing really close and all help is greatly appreciated.
Here are the relevant code snippets:
From models.py
class Activity(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
class Workday(models.Model):
entrydate = models.DateField()
worker = models.ForeignKey(User, on_delete=models.CASCADE)
class Entry(models.Model):
duration = models.DurationField()
quantity = models.PositiveIntegerField()
activity = models.ForeignKey(Activity, on_delete=models.CASCADE)
workday = models.ForeignKey(Workday, on_delete=models.CASCADE)
From forms.py
class EntryForm(ModelForm):
activity = ModelChoiceField(queryset=Activity.objects.order_by('name'), initial=0)
class Meta:
model = Entry
fields = ['activity',
'duration',
'quantity',
]
class WorkdayForm(ModelForm):
class Meta:
model = Workday
fields = ['entrydate']
widgets = {'entrydate': SelectDateWidget}
From views.py
def addentry(request):
EntryFormSet = modelformset_factory(Entry, form=EntryForm, extra=0, fields=('activity', 'duration', 'quantity'))
if request.method == 'POST':
workdayform = WorkdayForm(request.POST, prefix='workday')
formset = EntryFormSet(request.POST)
if formset.is_valid() and workdayform.is_valid():
# Generate a workday object
workday = workdayform.save(commit=False)
workday.entrydate = workdayform.cleaned_data['entrydate']
workday.worker = request.user
workday.save()
# Generate entry objects for each form in the entry formset
for form in formset:
e = form.save(commit=False)
e.workday = workday
e.save()
form.save_m2m()
messages.add_message(request, messages.SUCCESS,
"Registrert aktivitet " +
e.workday.entrydate.strftime('%A %d. %B %Y') +
": " + e.activity.name + " (" + str(e.quantity) +") - " +
str(e.duration)
)
return redirect('index')
else:
workdayform = WorkdayForm(request.POST, prefix='workday')
formset = EntryFormSet(request.POST)
for dict in formset.errors:
messages.add_message(request, messages.ERROR, dict)
context = {
'workdayform': workdayform,
'formset': formset,
}
return render(request, 'register/addentry.html', context)
else:
workdayform = WorkdayForm(prefix='workday')
formset = EntryFormSet()
context = {
'workdayform': workdayform,
'formset': formset,
}
return render(request, 'register/addentry.html', context)
From addentry.html
{% block content %}
{% if user.is_authenticated %}
<h1>Ny dag</h1>
{% if formset and workdayform %}
<form id="newdayform" method="POST" class="post-form">
{% csrf_token %}
{{ workdayform.as_p }}
{{ formset.management_form }}
<table>
<thead>
<tr>
<td>Aktivitet</td>
<td>Varighet<br/>(HH:MM:SS)</td>
<td>Antall</td>
</tr>
</thead>
<tbody>
{% for form in formset %}
<tr>
<td>{{ form.activity }}</td>
<td>{{ form.duration }}</td>
<td>{{ form.quantity }}</td>
<td class="hidden">{{ form.id }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit">Registrer tid</button>
</form>
<script src="{% static 'register/jquery.formset.js' %}"></script>
<script type="text/javascript">
$(function() {
$('#newdayform tbody tr').formset();
})
</script>
{% if entryform.errors or workdayform.errors %}
<h3>Feil i utfyllingen</h3>
{{ entryform.errors }}
{{ workdayform.errors }}
{% endif %}
{% else %}
<p>No form!</p>
{% endif %}
{% endif %}
{% endblock %}
Thanks to #e4c5 and this previous Q&A, the issue is solved by passing a queryset of no objects to the formset, like this:
def addentry(request):
(...)
qs = Entry.objects.none()
formset = EntryFormSet(queryset=qs)
(...)

Paginate results from django_filter

I am very new at this technology and I want to paginate a table containing results using django_filter (In my case I want to filter results from Book Model which are displayed in a HTML Table).
I tried every documentation and forum post but don't know what I am doing wrong or if I am doing something correctly.
Here is my code:
models
class Book(models.Model):
name = models.CharField(max_length=350)
author = models.CharField(max_length=350)
category = models.CharField(max_length=200)
def __str__(self):
return self.name
filters
class BookFilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr='icontains')
author = django_filters.CharFilter(lookup_expr='icontains')
category = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Book
ields = ['name', 'author', 'category',]
views
class SearchBooksView(ListView):
template_name = "booksearch.html"
book_list = Book.objects.all()
context_object_name = 'book_list'
paginate_by = 3
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
book_filter = BookFilter(self.request.GET)
paginator = Paginator(book_filter, self.paginate_by)
page = self.request.GET.get('page')
try:
book_list = paginator.page(page)
except PageNotAnInteger:
book_list = paginator.page(1)
except EmptyPage:
book_list = paginator.page(paginator.num_pages)
context['book_list'] = book_list
return context
** html **
<h1 class="h1"> Search Books </h1>
<form method="get">
{{ book_filter.form.as_p }}
<button type="submit">Search</button>
</form>
<div class="container">
<table>
<thead>
<tr>
<th>Name</th>
<th>Author</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{% for book in book_list %}
<tr>
<td>{{ book.name }}</td>
<td>{{ book.author }}</td>
<td>{{ book.category }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li>
<span>Previous</span>
</li>
{% endif %}
<li class="">
<span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</span>
</li>
{% if page_obj.has_next %}
<li>
<span>Next</span>
</li>
{% endif %}
</ul>
{% else %}
<p>No books available</p>
{% endif %}
</div>
with this code I am getting :
paginator.py, line 84, in count return len(self.object_list)
'BookFilter' has no len()
Can you please help me? Thanks!
You need to pass the filtered queryset to the Paginator which can be accessed as filterObj.qs.
From the django_filter docs:
If you want to access the filtered objects in your views, for example if you want to paginate them, you can do that. They are in f.qs
So create a paginated response like this:
book_filter = BookFilter(self.request.GET)
paginator = Paginator(book_filter.qs, self.paginate_by)
You are using generic ListView from django right? That view already take care of pagination for you, you should refactor the code:
class SearchBooksView(ListView):
model = Book
template_name = "booksearch.html"
context_object_name = 'book_list'
paginate_by = 3
def get_queryset(self):
return BookFilter(self.request.GET, queryset=Book.objects.all()).qs

Optimize code in Django - view ManyToMany as matrix

I'm trying to show user group permissions in Django and show them in a "Drupal" style like a matrix. It works, but it takes too long to make the query and paint it in the template. Is there some way to improve my code?
view img
up(accomplished),down(views and template.html)
views :
def GroupPermissionsView(request):
title = "Groups Permissions"
groups = Group.objects.all()
permissions = Permission.objects.all()
context = Context({
'title': title,
'groups': groups,
'permissions': permissions,
})
return render(
request,
'forms_permissions.html',
context
)
template:
<table class="table table-striped table-inverse table-responsive table-bordered">
<thead>
<tr>
<th>Permission</th>
{% for group in groups %}
<th>{{ group.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for permission in permissions %}
<tr>
<td><b>{{permission.name}}<b></td>
{% for group in groups %}
{% if permission in group.permissions.all %}
<td><input type="checkbox" name="" checked="checked"></input></td>
{% else %}
<td><input type="checkbox" ></input></td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
Your problem was that you run more than 4 * 200 queries, one query for every combination or rows and columns (permissions and groups). It is useful to get them all by one query. It is however not easy because the intermediate model of ManyToMany relationship between Permission and Group models is not explicit in django.contrib.auth.models. You can get that model by Model._meta API:
>>> GroupPermissions = Permission._meta.get_field('group').through
>>> GroupPermissions
<class 'django.contrib.auth.models.Group_permissions'>
>>> GroupPermissions._meta.db_table # the table that you use in the raw query
'auth_group_permissions'
Put it all together. Prefer a longer view and simple template:
update the view:
from collections import OrderedDict
GroupPermissions = Permission._meta.get_field('group').through
groups = Group.objects.all()
permissions = Permission.objects.all()
permission_group_set = set()
for x in GroupPermissions.objects.all():
permission_group_set.add((x.permission_id, x.group_id))
# row title and cells for every permission
permission_group_table = OrderedDict([
(permission, [(permission.id, group.id) in permission_group_set for group in groups])
for permission in permissions
])
context = Context({
'title': title,
'groups': groups,
'permission_group_table': permission_group_table,
})
update the template
{% for permission, cells in permission_group_table.items %}
<tr><td><b>{{permission.name}}<b></td>
{% for cell in cells %}
{% if cell %}
<td><input type="checkbox" name="" checked="checked"></input></td>
{% else %}
<td><input type="checkbox" ></input></td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}

Can't access a sub-query's attributes in a Django template. I can print out each item's name though

In this view, I'm passing it the user's favorite albums and songs:
#login_required
def my_favorites(request):
fav_albums = request.user.album_set.all()
fav_songs = request.user.song_set.all()
return render(request, "billyjoel/my_favorites.html", {"fav_albums": fav_albums, "fav_songs" : fav_songs})
It's basically the "my account" page in my project.
I'm iterating through each of the songs ("fav_songs"), and then, for each song, iterating through all albums on which that song exists.
{% if fav_songs.count > 0 %}
...
{% for song in fav_songs %} <!-- No colon after "fav_songs" -->
...
{% for albumsong in song.albums.all %}
<BR/><I>Number {{ albumsong.sequence_num }} on
{##}{{ albumsong.name }}</I>
{% endfor %}
But while it's printing the correct number of albums, the name and id and sequence_num are all the empty-string. (This is why the url is commented out)
I noticed if I pre-pend {{ albumsong }} to it, the album's name prints out, but I'm not sure how to tell what kind of object it really is, given the limited Python available in Django templates.
How do I access the album's properties, and are there any template tags that would help me diagnose this?
(I'm noticing this is similar to a question I asked a couple weeks ago, but I'm not sure if it's connected yet.)
models.py:
from django.db import models
from django.contrib.auth.models import User
from time import time
def get_upload_file_name(instance, filename):
return "uploaded_files/%s_%s" % (str(time()).replace(".", "_"), filename)
class Album(models.Model):
OFFICIALITY = (
('J', 'Major studio release'),
('I', 'Non-major official release'),
('U', 'Unofficial'),
)
title = models.CharField(max_length=70)
description = models.TextField(max_length=500, default="", null=True, blank=True)
pub_date = models.DateField('release date')
officiality = models.CharField(max_length=1, choices=OFFICIALITY)
is_concert = models.BooleanField(default=False)
main_info_url = models.URLField(blank=False)
thumbnail = models.FileField(upload_to=get_upload_file_name, blank=True, null=True)
#virtual field to skip over the through table.
songs = models.ManyToManyField("Song", through="AlbumSong")
users_favorited_by = models.ManyToManyField('auth.User')
def __str__(self):
return self.title
class Meta:
#Default ordering is by release date, ascending.
ordering = ['pub_date']
class Song(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=500, default="", null=True, blank=True)
length_seconds = models.IntegerField()
lyrics_url = models.URLField(default="", blank=True, null=True)
albums = models.ManyToManyField("Album", through="AlbumSong")
users_favorited_by = models.ManyToManyField(User)
def get_length_desc_from_seconds(self):
if(self.length_seconds == -1):
return "-1"
m, s = divmod(self.length_seconds, 60)
h, m = divmod(m, 60)
if(h):
return "%d:%02d:%02d" % (h, m, s)
else:
return "%d:%02d" % (m, s)
def __str__(self):
return self.name
class AlbumSong(models.Model):
song = models.ForeignKey(Song)
album = models.ForeignKey(Album)
sequence_num = models.IntegerField()
class Meta:
unique_together = ('album', 'sequence_num',)
unique_together = ('album', 'song',)
def __str__(self):
return str(self.album) + ": " + str(self.sequence_num) + ": " + str(self.song)
my_favorites.html
{% extends "base.html" %}
{% load bj_filters %}
{% block title %}My Favorites{% endblock %}
{% block content %}
<H1>Billy Joel Album Browser: Favorite albums and songs for user {{ user. }} </H1>
<H1>Albums</H1>
{% if fav_albums.count > 0 %}
<TABLE ALIGN="center" WIDTH="100%" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle">
<TD><B><U>Title</U></B><BR><I><FONT SIZE="-1">(click a title to view its song list)</FONT></I></TD>
<TD><B><U>Released</U></B></TD>
<TD>Officiality</TD>
<TD>Concert</TD>
<TD>Wiki</TD>
<TD>Favorite?</TD>
{% for album in fav_albums %} <!-- No colon after "fav_albums" -->
</TR><TR>
<TD VALIGN="top">
{% if album.thumbnail %}
<img src="/static/{{ album.thumbnail }}" width="25"/>
{% else %}
<img src="/static/images/white_block.jpg" width="25"/>
{% endif %}
{{ album.title }}
{% if album.description %}
<BR/><FONT SIZE="-1"><I>{{ album.description|truncatewords:10 }}</I></FONT>
{% endif %}
<TD>{{ album.pub_date|date:"m/y" }}</TD>
<TD><IMG SRC="/static/images/{{ album.officiality|multival_to_str:"J,I,U->major,minor,unofficial,broken_image"}}.jpg" height="20"/></TD>
<TD>{{ album.is_concert|yesno:"Yes,No" }}</TD>
<TD>Wiki</TD>
<TD><I>unfavorite</I></TD>
{% endfor %}
</TR></TABLE>
{% else %}
<P><I>You have no favorite albums.</I></P>
{% endif %}
<H2>Songs</H2>
{% if fav_songs.count > 0 %}
<TABLE ALIGN="center" WIDTH="100%" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle"><TR>
<TD><B><U>Len</U></B></TD>
<TD><B><U>Name</U></B></TD>
<TD><B><U>Lyrics</U></B></TD>
</TR>
{% for song in fav_songs %} <!-- No colon after "fav_songs" -->
<TR>
<TD>{% if song.get_length_desc_from_seconds == "-1" %}
<I>n/a</I>
{% else %}
{{ song.get_length_desc_from_seconds }}
{% endif %}
</TD>
<TD>{{ song.name }}
{% if song.description %}
<BR/><FONT SIZE="-1"><I>{{ song.description|truncatewords:50 }}</I></FONT>
{% endif %}
{% for albumsong in song.albums.all %}
<BR/><I>Number {{ albumsong.sequence_num }} on {##}{{ albumsong.name }}</I>
{% endfor %}
</TD>
<TD>
{% if song.lyrics_url %}
Lyrics (direct link)
{% else %}
Lyrics (search)
{% endif %}
</TD>
</TR>
{% endfor %}
</TABLE>
{% else %}
<P><I>You have no favorite songs.</I></P>
{% endif %}
{% endblock %}
Got it. This
{% for albumsong in song.albums.all %}
<BR/><I>#{{ albumsong.sequence_num }} on
{{ albumsong.name }}</I>
{% endfor %}
needs to be changed to this
{% for albumsong in song.albumsong_set.all %}
<BR/><I>#{{ albumsong.sequence_num }} on
{{ albumsong.album.title }}</I>
{% endfor %}
I wish there were diagnostic error messages to help figure this kind of stuff out, instead of just printing empty strings.

Categories

Resources