Django Template filtering - python

I am trying to create a website where you can view different "ranks" and the people within them. I have gotten it to create the ranks into there own Collapsable Panels, as I am using Bootstrap 3, but I am unable to list the Users who have that rank. Please help.
#team.html template
<div class="container">
<div class="col-md-2">
<div class="panel-group" id="accordion">
{% for rank in Rank_list %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapse{{rank.rank_num}}">
{{ rank.rank_name }}s
</a>
</h4>
</div>
<div id="collapse{{rank.rank_num}}" class="panel-collapse collapse in">
<div class="panel-body">
{% for name in User_list %}
<li>
<p>{{name}}</p>
</li>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
#views.py
class team(generic.ListView):
template_name = 'main/team.html'
context_object_name = 'Rank_list'
context_object_name2 = 'User_list'
def get_queryset(self):
return Rank.objects.order_by('rank_num')
def get_queryset2(self):
return User.objects.order_by('name')
#models.py
class Rank(models.Model):
rank_name = models.CharField(max_length=200)
rank_num = models.IntegerField(default=0)
def __str__(self): # __unicode__ on Python 2
return self.rank_name
class User(models.Model):
rank = models.ForeignKey(Rank)
name = models.CharField(max_length=200)
def __str__(self): # __unicode__ on Python 2
return self.name

You need to add a related_name to the foreign key. Like this:
rank = models.ForeignKey(Rank, related_name='users')
Then the users can be accessed in a loop like this:
{% for user in rank.users.all %}
{{ user }}
{% endfor %}

For each Rank object, you can reverse the relationship and get all of the Users related to that Rank
# models.py
# - Use `related_name` for making relationship that are easy to read and follow
# - https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
class User(...):
rank = models.ForeignKey(Rank, related_name="users")
# view.py
# - Use prefetch_related allows us to cut down our queries when iterating as we do an up-front join
# - https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related
...
def get_queryset(self):
return Rank.objects.prefetch_related('users')('rank_num')
# template.py
{% for rank in Rank_List %} # You shouldn't capitalise your template variables
{% for user in rank.users.all %}
{{ user }}
{% endfor %}
{% endfor %}
Finally, you should use the built-in User model that django's auth module provides

Related

Django display data from two different models

I have two seperated models. One with two text fields and one for multiple images. Now I want to display the entire data in one html div. What do I have to change in the projects view and in projects.html?
models.py
class Project(models.Model):
title = models.CharField(max_length=200)
describtion = models.TextField(null=True, blank=True)
class ProjectImage(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
image = models.FileField(upload_to="products/")
forms.py
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['title', 'describtion']
class ProjectImageForm(forms.ModelForm):
class Meta:
model = ProjectImage
fields = ['image']
widgets = {
'image': ClearableFileInput(attrs={'multiple': True}),
}
views.py
def createProject(request):
form = ProjectForm()
form2 = ProjectImageForm()
if request.method == 'POST':
form = ProjectForm(request.POST)
form2 = ProjectImageForm(request.POST, request.FILES)
images = request.FILES.getlist('image')
if form.is_valid() and form2.is_valid():
title = form.cleaned_data['title']
describ = form.cleaned_data['describtion']
project_instance = Project.objects.create(
title=title, describtion=describ)
for i in images:
ProjectImage.objects.create(project=project_instance, image=i)
context = {'form': form, 'form2': form2}
return render(request, 'projects/project_form.html', context)
def projects(request):
projects = Project.objects.all()
context = {"projects":projects}
return render(request, 'projects/projects.html', context)
projects.html
{% for project in projects %}
<div class="column">
<div class="card project">
<img class="project__thumbnail" src="{{project.image.url}}" alt="project thumbnail" />
<div class="card__body">
<h3>{{project.title}}</h3>
<h2>{{project.describtion}}</h2>
</div>
</a>
</div>
</div>
{% endfor %}
You don't need to change anything.
You should be able to access the reverse with project.project_image_set attribute in the template:
<div class="card__body"
<h3>{{project.title}}</h3>
<h2>{{project.describtion}}</h2>
{% for image in project.project_image_set.all %}
{{ image.image }}
{% endfor %}
</div>
Docs: https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_one/
I don't really understand the question here but i see a problem in your template considering you are using foreign key in ProjectImage. And update the question
{% for project in projects %}
<div class="column">
<div class="card project">
{% for j in project.projectimage_set.all %}
<img class="project__thumbnail" src="{{j.image.url}}" alt="project thumbnail" />
{% endfor %}
<div class="card__body">
<h3>{{project.title}}</h3>
<h2>{{project.describtion}}</h2>
</div>
</a>
</div>
</div>
{% endfor %}
I would change FileField to ImageField and add function:
#property
def get_photo_url(self):
if self.image and hasattr(self.image, 'url'):
return self.image.url
else:
return ''
If createProject function works( I would rename it to create_project) then in projects.html:
{% for project in projects %}
<div class="column">
<div class="card project">
<div class="card__body">
<h3>{{project.title}}</h3>
<h2>{{project.describtion}}</h2>
{% for img in project.projectimage_set.all %}
<img class="project__thumbnail" src="{{img.get_photo_url}}" alt="project thumbnail" />
{% endfor %}
</div>
</a>
</div>
</div>
{% endfor %}

Django sorting by a category and pagination combining both in one function view

I'm trying to sort my projects by categories: all, css, HTML, Django, and so on. and also trying to add pagination when showing all projects have a limit of 6 projects per page. I'm stuck and have trouble combining either the pagination work or the filter/ sorting the items work, here's my code. Please Help :)
models.py
class Category (models.Model):
category_name = models.CharField(max_length=150)
slug = models.SlugField(unique=True)
class Meta:
ordering = ('-category_name',)
def __str__(self):
return self.category_name
def get_absolute_url(self):
return reverse('mainpages:project_by_category', args=[self.slug])
class Project(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, default='', null=True)
title = models.CharField(max_length=100)
description = models.TextField()
technology = models.CharField(max_length=20)
proj_url = models.URLField(max_length=200, blank=True)
blog_link = models.URLField(max_length=200, blank=True)
image = models.ImageField(default='post-img.jpg', upload_to='proj-img')
class Meta:
ordering = ('-title',)
def __str__(self):
return self.title
view.py
def portfolioView(request, category_slug=None):
# added
category = None
categories = Category.objects.all()
# filtering by project
projs = Project.objects.all()
# paginator added
projects = Project.objects.all()
paginator = Paginator(projects, 3)
page = request.GET.get('page')
try:
projects = paginator.page(page)
except PageNotAnInteger:
projects = paginator.page(1)
except EmptyPage:
projects = paginator.page(paginator.num_pages)
# added
if category_slug:
category = get_object_or_404(Category, slug=category_slug)
projs = projs.filter(category=category)
#
return render(request, 'mainpages/portfolio.html', {
'category': category,
'categories': categories,
'projects': projects,
'page': page,
'projs': projs,
})
def projectDetail(request, pk):
project = Project.objects.get(pk=pk)
context = {
'project': project
}
return render(request, 'mainpages/portfolio-detail.html', context)
pagination.html
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
Previous
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}
</span>
{% if page.has_next %}
Next
</span>
{% endif %}
</div>
porftfolio.html
{% extends 'mainpages/base.html' %}
{% load static %}
{% block content %}
<!-- -->
<section class="portfolio-section">
<!-- -->
<div class="portfolio-info">
<h1>PORTFOLIO</h1>
<p>Industry's so electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop</p>
</div>
<!-- -->
<div class="category-container">
<div class="category-wrapper">
<div class="category-title">
<h3>Catergories</h3>
</div>
<!-- -->
<div class="category-links">
<ul>
<li>ALL</li>
{% for c in categories %}
<li>
{{ c.category_name}}
</li>
{% endfor %}
</ul>
</div>
<div>
<h1>
{% if category %}
<p class="center-category">All {{ category.category_name }} Projects</p>
{% else %}
<p class="center-category">All Projects</p>
{% endif %}
</h1>
{% for p in project %}
{{ p.title }}
{% endfor %}
</div>
<!-- -->
</div>
</div>
<div class="work-container">
<div class="work-wrapper">
{% for project in projects %}
<div class="work-links">
<div class="card">
<img class="work-img" src="{{ project.image.url }}" alt="">
<h6 class="title">{{ project.title }}</h6>
<span> {{ project.description |truncatechars:50 }}</span>
<button class="works-btn">
View Project
</button>
</div>
</div>
{% endfor %}
</div>
</div>
{% include 'mainpages/pagination.html' with page=projects %}
</section>
{% endblock %}

Django display limited images in grid row

As mentioned in the title. Is it possible to display a specific number of photos in a particular grid-container? For example, a maximum of 3 columns with a card in the row using a loop?
I was able to achieve the effect of displaying all the photos from the defined model,but I don't know how to set the limit.
Below I present fragments of the code responsible for displaying the created model
forms.py
class ProductsForm(forms.ModelForm):
class Meta:
model = Product
fields = ('name', 'description', 'image', 'file_type')
models.py
class Product(models.Model):
name = models.CharField(max_length=20, default='')
description = models.CharField(max_length=100, default='')
file_type = models.CharField(max_length=256, choices=[('image', 'image'), ('thumbnail', 'thumbnail')], default='')
image = models.ImageField(upload_to='products', default='')
def __str__(self):
return self.name
views.py
def gallery(request):
image = Product.objects.filter(file_type='image')
thumbnail = Product.objects.filter(file_type='thumbnail')
return render(request, 'products/fruits.html', {'img': image, 'thumb': thumbnail})
fruits.html
<!DOCTYPE html>
{% load staticfiles %}
{% load thumbnail %}
{% block body_block %}
<div class="grid-container">
<div class="card-deck">
{% for i in thumb %}
{% thumbnail i.image "150x150" crop="center" as im %}
<!-- Card -->
<div class="card mb-4">
<!--Card image-->
<div class="view overlay">
<img src="{{ im.url }}"
alt="Card image cap">
<a href="#!">
<div class="mask rgba-white-slight"></div>
</a>
</div>
<!--Card content-->
<div class="card-body">
<!--Title-->
<h4 class="card-title">Card title</h4>
<!--Text-->
<p class="card-text">Some quick example text to build on the card title and make up
the
bulk
of
the
card's
content.</p>
<!-- Provides extra visual weight and identifies the primary action in a set of buttons -->
<button type="button" class="btn btn-light-blue btn-md">Read more</button>
</div>
</div>
{% endthumbnail %}
{% endfor %}
</div>
</div>
{% endblock %}
The build-in Django filter divisibleby may work, inside your loop, you check whether the iteration is divisible by (in your case) 3, then you break the row to jump to another one:
{% for i in thumb %}
{% if forloop.counter|divisibleby:3 %}
----------
{% endif %}
{% endfor %}
I was able to get what you want to do by creating a list of lists of images in the view, then send this list to the HTML template and display it with two loops. The advantage of this approach is that you can control the number of columns by changing one variable. Each element of the main list contains X number of columns.
The idea is to send a list like the one represented below (more or less)
photos_list = [[photo1, photo2, ... photoN], [photo1, photo2, ... photoN]]
the view code is:
def index(request):
photos = Photo.objects.all()
counter = -1
columns_qty = 5 # << Just change this number for the columns you want
photos_row = []
photos_list = []
for photo in photos:
counter += 1
if counter < columns_qty:
photos_row.append(photo)
else:
photos_list.append(photos_row)
photos_row = []
counter = -1
context = {'photos_list': photos_list}
return render(request, 'photos/index.html', context)
the model code is:
class Photo(models.Model):
file_name = models.CharField(max_length= 250)
original_path = models.CharField(max_length=250)
saved_path = models.CharField(max_length=250)
file_size = models.IntegerField(null=True)
file_created = models.DateTimeField(null=True)
file_last_modified = models.DateTimeField(null=True)
loaded_datetime = models.DateTimeField(default=timezone.now())
def __str__(self):
return self.file_name
and the html template code is:
{% extends 'photos/base.html' %}
{% load static %}
{% block content %}
<table>
{% for photo_row in photos_list %}
<tr>
{% for photo_col in photo_row %}
<td><img src="{% static photo_col.file_name %}" width="300px"
height="auto"></td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endblock %}
I hope this helps!

Access Django's class model and display on template

I am new to django and is working on my pet project. I am having a bit of problem accessing the data from one of my classes in models.py
models.py
class Team_Region(models.Model):
name = models.CharField(max_length=50)
# String representation
def __str__(self):
return self.name
class Team_Name(models.Model):
t_name = models.CharField(max_length=100)
logo = models.ImageField(upload_to='team_logos', blank=True)
region_member = models.ForeignKey(Team_Region, related_name='regions')
def __str__(self):
return self.t_name + ' - ' + str(self.region_member)
class Team_Member(models.Model):
mem_name = models.CharField(max_length=100)
position = models.CharField(max_length=50)
member_of_team = models.ForeignKey(Team_Name, related_name='teams')
def __str__(self):
return self.mem_name + ' - ' + self.position + ' - ' + str(self.member_of_team)
views.py
# Display regions in list
class TeamRegionListView(ListView):
context_object_name = 'regions_list'
model = Team_Region
template_name = 'dota_teams/team_regions_list.html'
# Display all teams associated to the region
class TeamRegionDetailView(DetailView):
context_object_name = 'region_teams'
model = Team_Region
template_name = 'dota_teams/team_regions_detail.html'
class MemberDetailView(DetailView):
context_object_name = 'team_members'
model = Team_Name
template_name = 'dota_teams/member_detail.html'
urls.py
url(r'^$', views.TeamRegionListView.as_view(), name='all_regions'),
url(r'^(?P<pk>\d+)/$', views.TeamRegionDetailView.as_view(), name='region_teams'),
url(r'^(?P<pk>\d+)/(\d+)/$', views.MemberDetailView.as_view(), name='member_details'),
UPDATE
team_regions_list.html
{% block body_block %}
<div class="row">
{% for region in regions_list %}
<div class="col-xs-12 col-lg-4">
<a href="{{ region.id }}" class="thumbnail" style="width: 350px; height:350px">
<h4 style="text-align: center; margin-top: 150px"> {{ region.name }} </h4>
</a>
</div>
{% endfor %}
</div>
{% endblock %}
team_regions_detail.html
{% block body_block %}
<div class="row">
{% for team in region_teams.regions.all %}
<div class="col-xs-12 col-lg-4">
<a href="{{ team.id }}" class="thumbnail">
<img style="width: 100px; height:90px" src="{{ team.logo.url }}" alt="Image not found.">
<div class="caption">
<h4 style="text-align:center">{{ team.t_name }}</h4>
</div>
</a>
</div>
{% endfor %}
</div>
{% endblock %}
I'm not sure how to access the mem_name and position variables under the Team_Member class. In my views.py, if I use the model Team_Name, the ID is not properly assigned to the region and team. I have tried accessing the Team_Member by using a For loop from Team_Region and use the 'related_name' of Team_Name then access the 'related_name' of Team_Member but it won't work either (e.g. {% for member in regions_list.regions.teams.all %} ). I'm a bit loss on this.
Any suggestions please?
TIA
Based on your models, it has the relationship:
Team_Region --> has many Team_Name
Team_Name --> has many Team_Member
you have already defined the foreign keys with related_name, if you access the Team_Member from Team_Region, you have to for loop the regions_list first, then use the related_name regions to get the team names, after that for loop all the teams, use the related_name teams to get the team members. In your template team_regions_list.html, like this:
{%for rl in regions_list%}
{%for tn in rl.regions.all %}
{{tn.teams.all}}
{% endfor %}
{% endfor %}
Update:
in your views.py, the modal is not correct for class TeamRegionDetailView and MemberDetailView, change to:
# Display regions in list
class TeamRegionListView(ListView):
context_object_name = 'regions_list'
model = Team_Region
template_name = 'dota_teams/team_regions_list.html'
# Display all teams associated to the region
class TeamRegionDetailView(DetailView):
context_object_name = 'region_teams'
model = Team_Name
template_name = 'dota_teams/team_regions_detail.html'
class MemberDetailView(DetailView):
context_object_name = 'team_members'
model = Team_Member
template_name = 'dota_teams/member_detail.html'
Use region_teams.teams.all instead of region_teams.regions.all, change your team_regions_detail.html to :
<div class="row">
{% for team in region_teams.teams.all %}
<div class="col-xs-12 col-lg-4">
<a href="{{ team.id }}" class="thumbnail">
<img style="width: 100px; height:90px" src="{{ region_teams.logo.url }}" alt="Image not found.">
<div class="caption">
<h4 style="text-align:center">{{ team.t_name }}</h4>
</div>
</a>
</div>
{% endfor %}
</div>
Update 2:
I believe that you manage your upload files incorrectly, refer to official doc:
By default, Django stores files locally, using the MEDIA_ROOT and
MEDIA_URL settings.
in your project settings.py file, add them:
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
and in your project urls.py file, add:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
After that the template can access your uploaded images with (/media/team_logos/test.png), eg:
http://127.0.0.1:8000/media/team_logos/test.png
I'd like to thank Tiny.D for the idea on the solution.
I updated my views.py
from django.views.generic import ListView, DetailView
from .models import Team_Region, Team_Name, Team_Member
#Display regions in list
class TeamRegionListView(ListView):
context_object_name = 'regionlist'
model = Team_Region
template_name = 'dota_teams/team_region_list.html'
#Display teams associated to the region
class TeamRegionDetailView(DetailView):
context_object_name = 'regiondetail'
model = Team_Region
template_name = 'dota_teams/team_region_detail.html'
#Display members of each team
class TeamDetailView(DetailView):
context_object_name = 'teamdetail'
model = Team_Region
template_name = 'dota_teams/team_detail.html'
urls.py
url(r'^$', views.TeamRegionListView.as_view(), name='region_list'),
url(r'^(?P<pk>\d+)/$', views.TeamRegionDetailView.as_view(), name='region_detail'),
url(r'^(?P<pk>\d+)/(\d+)/$', views.TeamDetailView.as_view(), name='member'),
The logic of the team_region_list.html and team_region_detail.html are the same to my post above.
team_detail.html
{% block body_block %}
<div class="row">
{% for member in teamdetail.regions.all %}
{% for member_detail in member.teams.all %}
<h4>{{ member_detail.mem_name }}</h4>
{% endfor %}
{% endfor %}
</div>
{% endblock %}
For the team_detail.html, I just need to perform a nested loop just what Tiny.D mentioned. Everything is working now. Thanks.

How do I call a custom method on a model with a template in Django?

I'm trying to make a poll app, and I'm kinda stuck on the "view poll" page.
I want to display the votes with a Twitter Bootstrap progress bar, and I wrote a method in the Choice model to calculate the percentage compared to all the other choices on the poll.
However, when I try doing {{ choice.percentage }} it just returns... blank. nothing.
Screenshot:
Here's models.py:
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=256)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.question
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice_text = models.CharField(max_length=256)
votes = models.IntegerField(default=0)
def __unicode__(self):
return self.choice_text
def percentage(self):
total = 0.0
for ch in self.poll.choice_set.all():
total = total + ch
return (self.votes/total)*100
And here's view_poll.html:
{% extends "quickpoll_web/base.html" %}
{% block title %}Viewing poll #{{ poll.id }} {% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title text-center">{{ poll.question }}</h3>
</div>
<div class="panel-body">
{% for choice in poll.choice_set.all %}
<div class="row">
<div class="col-md-3 text-right">{{ choice.choice_text }}</div>
<div class="col-md-9">
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="{{ choice.percentage }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ choice.percentage_of_votes }}%">
<span class="sr-only">{{ choice.votes }} out of {{ total_votes }}</span>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
Your problem is in this method:
def percentage(self):
total = 0.0
for ch in self.poll.choice_set.all():
total = total + ch
return (self.votes/total)*100
self.poll.choice_set.all(): returns a queryset of Choice objects.
Now, in the view if you try to do choice.percentage(), you will notice the error.
To fix this, try
def percentage(self):
total = 0.0
for ch in self.poll.choice_set.all():
total = total + ch.votes
return (self.votes/total)*100

Categories