Django display limited images in grid row - python

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!

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

How to filter queryset based on boolean value, then count in template

I want to count how many jobs are open vs closed. I don't understand why this isn't working, I have added {{ap.id}} and {{open.line_num_id }} after the forloop just to see what was rendered. I would think that if they match, then it's added to the count. This is not the case, all jobs are counted regardless of the "if" statement. Totally lost as to what is happening and why. Any help would be very appreciated .
I have two models:
class Airplane(models.Model):
line_num = models.CharField(max_length=10, unique=True)
vh_num = models.CharField(max_length=4, unique=True)
vz_num = models.CharField(max_length=4, unique=True)
stall = models.CharField(max_length=30, choices=stall_picker)
status = models.CharField(max_length=30, choices=status_picker)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
is_completed = models.BooleanField(default=False)
pm = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.line_num
class Job(models.Model):
line_num = models.ForeignKey(
Airplane, on_delete=models.CASCADE, related_name="ap_jobs")
job_num = models.CharField(max_length=10, unique=True)
description = models.CharField(max_length=200)
status = models.CharField(max_length=30, choices=status_picker)
category = models.CharField(max_length=30, choices=categories_picker)
is_completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class meta:
ordering = ["category", "status"]
def __str__(self):
return (f"{self.job_num}: {self.description} : {self.is_completed}")
My View:
def Ap_linup(request):
context = {
'ap_list': Airplane.objects.all().filter(is_completed=False),
'open_jobs': Job.objects.all().filter(is_completed=False),
'closed_jobs': Job.objects.all().filter(is_completed=True)
}
return render(request, 'airplane/airplane_list.html', context)
Template:
{% extends 'base.html' %}
{% block title %} Airplane List {% endblock %}
{% block content %}
<center>
<div class="row">
{% for ap in ap_list %}
<div class="col-md-3">
<div class="card">
<div class="card-body">
<h1>
<center>
{{ ap.line_num }}
</center>
</h1>
<h3>
<p>Stall:<strong>{{ ap.stall }}</strong> VH:<strong>{{ ap.vh_num }}</strong> VZ:<strong>{{ap.vz_num }}</strong>
<p>MGR: {{ ap.owner }}</p>
<hr class="lead border border-dark">
<h1>{{ ap.id }}</h1>
{% if ap.status == "Avalible" %}
<p>Current Status:</p>
<p class="text-success">{{ ap.status }}</p>
{% else %}
<p>Current Status:</p>
<p class="text-danger">{{ ap.status }}</p>
{% endif %}
</h3>
<!-- Open Job counts -->
<div>
{% for open in open_jobs %}
{% if open.line_num_id == ap.id %}</p>
<p>Open Jobs: {{ open_jobs.count }}</p>
{% endif %}
{% endfor %}
{% for closed in closed_jobs %}
{% if closed.line_num_id == ap.id %}</p>
<p>Closed Jobs: {{ closed_jobs.count }}</p>
{% endif %}
{% endfor %}
</div>
<div class="lead border border-dark">
{% for comment in ap.ap_comments.all %}
{% if comment_id == ap_id %}
<p><strong>{{ comment.author }}</strong>: {{ comment.text }} # {{comment.created_at }}</p>
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</center>
{% endblock %}
OUTPUT:
Output
I want to display open and closed job counts per Airplane. Count of Open and Close jobs just add up to total jobs on Airplane.
As Kusko mentions in the comments, you can pass that number to the context. You can do so by doing something like this:
def Ap_linup(request):
context = {
'ap_list': Airplane.objects.all().filter(is_completed=False),
'open_jobs': Job.objects.all().filter(is_completed=False),
'closed_jobs': Job.objects.all().filter(is_completed=True),
'open_jobs_count': Job.objects.filter(is_completed=False).count(),
'closed_jobs_count': Job.objects.filter(is_completed=True).count()
}
Also, you don't need .all() because filter is sufficient.
and if you wanted to clean up the code a bit more so you don't have a lot of queries, you can declare variables before the context.
def Ap_linup(request):
open_jobs = Job.objects.filter(is_completed=False)
closed_jobs = Job.objects.filter(is_completed=True)
context = {
'ap_list': Airplane.objects.all().filter(is_completed=False),
'open_jobs': open_jobs,
'closed_jobs': closed_jobs,
'open_jobs_count': open_jobs.count(),
'closed_jobs_count': closed_jobs.count()
}
return render(request, 'airplane/airplane_list.html', context)
UPDATE:
To display this information in the template you can do this outside of your loop:
For closed jobs you just call the variable you set in the context
<p>Closed Jobs: {{ closed_jobs_count }}</p>
and for open jobs you would do the same thing
<p>Open Jobs: {{ open_jobs_count }}</p>
You no longer need to add a .count to the template because you already did the work on the backend. The value stored in closed_jobs_count and open_jobs_count is an number.

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

How to retrieve object that a different object is linked to in template? Django

I have a quiz model and a view where I'm displaying a list of all the quizzes on the page. I want to show the score that they got alongside each quiz. Currently, I have a score model that links to each quiz and the user that took the quiz. I want to do something like Score.objects.filter(user=request.user, quiz=quiz).get_percentage() to show the percentage achieved for that quiz. However this doesn't work in template code of course.
def learn(request): #Quiz Screen
context = {
'quizzes':Quiz.objects.all(),
'scores':Score.objects.filter(user=request.user),
}
return render(request, 'quiz_app/learn.html', context)
{% extends "quiz_app/base.html" %}
{% block content %}
New Quiz
{% for quiz in quizzes %}
<a id="quiz-anchor" href="{% url 'quiz-detail' quiz.id %}">
<article class="quiz-thumbnail-large">
<h2>{{ quiz.title }}</h2>
<h3>{{ quiz.question_amount }} Questions</h3>
<p>Score: {{ quiz.score.get_percentage }}%</p>
</article>
</a>
{% endfor %}
{% endblock content %}
class Quiz(models.Model):
title = models.CharField(max_length=100) #or TextField
#slug = models.SlugField(max_length=200, default=1) #url
video_link = models.CharField(max_length=100, default="https://www.youtube.com/watch?v=p60rN9JEapg")
question_amount = models.IntegerField()
author = models.ForeignKey(User, default=1, on_delete=models.CASCADE, related_name='quiz_author',)
date_created = models.DateTimeField(default=timezone.now)
class Meta:
verbose_name_plural = "Quizzes"
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('quiz-detail', kwargs={'pk': self.pk})
class Score(models.Model):
quiz = models.ForeignKey(Quiz, default=1, on_delete=models.CASCADE)
user = models.ForeignKey(User, default=1, on_delete=models.CASCADE)
correct_answers = models.IntegerField()
incorrect_answers = models.IntegerField()
def __str__(self): #returns as a/b
score = str(self.correct_answers) + '/' + str(self.get_total_answers())
return score
def add_correct_answers(self, amount):
self.correct_answers = self.correct_answers + amount
self.save()
def add_incorrect_answers(self, amount):
self.incorrect_answers = self.incorrect_answers + amount
self.save()
def get_total_answers(self):
total = self.correct_answers + self.incorrect_answers
return total
def get_percentage(self):
percentage = round(self.correct_answers / self.get_total_answers() * 100)
return percentage
So as you have a foreign Key relationship between Quiz and Score, you can have multiple scores for a single quiz, therefore you should use the backward relationship from the quiz model like this:
{% extends "quiz_app/base.html" %}
{% block content %}
New Quiz
{% for quiz in quizzes %}
<a id="quiz-anchor" href="{% url 'quiz-detail' quiz.id %}">
<article class="quiz-thumbnail-large">
<h2>{{ quiz.title }}</h2>
<h3>{{ quiz.question_amount }} Questions</h3>
{% for score in quiz.score_set.all %}
<p>Score: {{ score.get_percentage }}%</p>
{% endfor %}
</article>
</a>
{% endfor %}
{% endblock content %}
Edit
This is a quick fix:
{% extends "quiz_app/base.html" %}
{% block content %}
New Quiz
{% for quiz in quizzes %}
<a id="quiz-anchor" href="{% url 'quiz-detail' quiz.id %}">
<article class="quiz-thumbnail-large">
<h2>{{ quiz.title }}</h2>
<h3>{{ quiz.question_amount }} Questions</h3>
{% for score in quiz.score_set.all %}
{% if score.user == request.user %}
<p>Score: {{ score.get_percentage }}%</p>
{% endif %}
{% endfor %}
</article>
</a>
{% endfor %}
{% endblock content %}
But it's not a good idea, because you will retrieve all the score for all the users first, and then filter them; It's better to use a template tag here.

How to work with Choices in Django - How to display specific content?

I try to create a book database in Django. I have to do one more thing. So i have a model with CHOICES:
#model Book
class Book(models.Model):
#book types and placed
BIOGRAPHY = 1
FANTASY = 2
HISTORICAL = 3
HORROR = 4
CLASSIC = 5
YOUTH_LITHERATURE = 6
NON_FICTION = 7
MODERN_LITERATURE = 8
POETRY = 9
ADVENTURE = 10
ESSAYS = 11
ROMANCE = 12
SATIRE = 13
THRILLER = 14
DRAMA = 15
NONE = 0
B00K_CHOICES = (
(BIOGRAPHY,'Biography'),
(FANTASY, 'Fantasy/Sci-Fi'),
(HISTORICAL, 'Historical'),
(HORROR, 'Horror'),
(CLASSIC, 'Classic'),
(YOUTH_LITHERATURE, 'Youth Litherature'),
(NON_FICTION, 'Non-Fiction'),
(MODERN_LITERATURE, 'Modern Literature'),
(POETRY, 'Poetry'),
(ADVENTURE, 'Adventure'),
(ESSAYS, 'Essays'),
(ROMANCE, 'Romance'),
(SATIRE, 'Satire'),
(THRILLER, 'Thriller'),
(DRAMA, 'Drama'),
(NONE, 'No Information'),
)
book_image = models.ImageField(upload_to='book_image', blank=True, null=True)
book_name = models.CharField(max_length=255, unique=True)
book_author = models.ForeignKey(Author, on_delete=models.CASCADE)
book_types = models.IntegerField(choices=B00K_CHOICES, default= NONE)
book_description = models.TextField(null=True, blank=True)
book_pages = models.PositiveIntegerField(null=True, blank=True)
book_published = models.DateField(null=True, blank=True)
book_ratings = GenericRelation(Rating, related_query_name='book', default=NONE)
def __str__(self):
return '{}'.format(self.book_name)
And i create a simple view with display this choices:
def book_types_list(request):
book = Book.objects.all()
context = {'book': book, 'book_types': Book.B00K_CHOICES}
return render(request, 'plibrary_core/book_types_list.html', context)
I create also a html template with list of this choices:
{% extends 'base.html' %}
{% load static %}
{% block custom %}
<link rel='stylesheet' type="text/css" href="{% static 'css/background_color_styles.css' %}">
{% endblock %}
{% block title %}
<title>Book Types List | Private Library</title>
{% endblock %}
{% block content %}
<div class="container">
<div class="col">
<div id="background-color-content">
<h3>Book Types</h3>
<hr>
{% for book_type in book_types %}
<ul>
<h6>{{ book_type }}</h6>
</ul>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
And in this moment, i need to have something like this - User click on the book type (for example Horror) and then he see another template with all books with only this types. I try to do something like this but it doesn't work. I don't know how to fix it. Somebody know what i need to do?;X
Firt use ul apropiatelly, with each item in a li tag, and in the correct place:
<ul> <!-- ul is outside the loop -->
{% loop %}
<li> ...</li>
{% endloop %}
</ul>
Then in your view you don't need to call the object Book.objects.all() unless you want to show all your books. It looks like you are just displaying the book choices.
def book_types_list(request):
book = Book.objects.all() # why do you need this in your view?
context = {'book': book, 'book_types': Book.B00K_CHOICES}
return render(request, 'foo/books.html', context)
Then in your template you just need to:
<ul>
{% for book_type in book_types %}
<li>
<!-- you have to handle the logic of the url here -->
{{ book_type.1 }}
</li>
{% endfor %}
</ul>
urls.py
url(r'^my_url/(?P<book_type_id>\d+)/$', my_view, name='my_url'),
with this element you get to render a select element on screen, and it will use the choices you are providing in choices, use that instead of the for loop of book types
{{ book.booktypes }}

Categories