Filter queries inside templates django - python

models:
class Level(models.Model):
number = models.IntegerField()
badge = models.ImageField()
locked_badge = models.ImageField()
timestamp =
models.DateTimeField(auto_now_add=True,auto_now=False,blank=True,
null=True)
unlock = models.CharField(max_length=10,default="A")
def __str__(self):
return str(self.number)
def get_absolute_url(self):
return reverse('student:level-detail', kwargs={'pk': self.pk})
class ToDo(models.Model):
level = models.ForeignKey(Level, on_delete=models.CASCADE)
name = models.CharField(max_length=150)
description = models.TextField()
timestamp =
models.DateTimeField(auto_now_add=True,auto_now=False,blank=True,
null=True)
def __str__(self):
return self.name
class Task(models.Model):
level = models.ForeignKey(Level, on_delete=models.CASCADE)
todo = models.ForeignKey(ToDo, on_delete=models.CASCADE)
student = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
content = models.TextField()
timestamp = models.TimeField(auto_now=True)
datestamp = models.DateField( auto_now=True)
like =
models.ManyToManyField(User,related_name='user_likes',blank=True)
is_verified=models.BooleanField(default=False,blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('student:dashboard')
objects = PostManager()
#property
def comments(self):
instance = self
qs = Comment.objects.filter_by_instance(instance)
return qs
#property
def get_content_type(self):
instance = self
content_type =
ContentType.objects.get_for_model(instance.__class__)
return content_type
Template:
{% for level in levels %}
{% if tasks.filter(student=student,level=level).count ==
todos.filter(level=level).count %}
<li>
<img src="{{ level.badge.url }}" alt="" />
</li>
{% else %}
<li>
<a href="javascript:;"><img src="{{
level.locked_badge.url }}" alt="" /></a>
</li>
{% endif %}
{% endfor %}
sample view:
#login_required(login_url='/account/login/')
def StudentPublicProfile(request,pk=None):
student = User.objects.get(pk=pk)
levels = Level.objects.all()
todos=ToDo.objects.all()
tasks=Task.objects.all()
context={
'student': student,
'levels' : levels,
'tasks' : tasks,
'todos' : todos,
}
return render(request, 'student_public_profile2.html', context)
This might need a little explanation. I have 3 models- Level Todo and Task. Each level contains many todos.When a todo is completed,ie,when a todo form is submitted,It gets saved in the Task model.The form uses model form,so basically task is the set of completed todos of the user of a level. I want to display badges for the levels based on a condition which is : If all the todos of a level is completed,it should display one type of badge,if not it should display a locked badge!How do i go about this?
I came accross template tags..I make my custom tags like this:
#register.filter
def completed(tasks,student, level):
return tasks.filter(student=student,level=level).count()
#register.filter
def alltodos(todo, level):
return todo.filter(level=level).count()
How to call function from template for this to work correctly?
{% for level in levels %}
{% if tasks|completed:tasks,student,level ==
todos|alltodos:todos,level %}
<li>
<a href="javascript:;"><img src="{{
level.badge.url }}" alt="" /></a>
</li>
{% else %}
<li>
<a href="javascript:;"><img src="{{
level.locked_badge.url }}" alt="" /></a>
</li>
{% endif %}
{% endfor %}

Related

How to check if an identical element exists in another table in Django?

I have two models: CaseRequest and Case. The CaseRequest object has values name and datebirth. I need to check if the same values exists for name and datebirth in Case. For instance, The result should be like this:
Case:
Name DateBirth
Don Honn 01.03.1959
Freak Hu 04.11.1993
Wend Don 06.03.1988
CaseRequest:
Name DateBirth
Tom Hawk 01.05.1999 - no
Don Honn 01.03.1959 - yes
Fold Len 03.07.1967 - no
Freak Hu 04.11.1993 - yes
Wend Don 13.12.1956 - no
My code:
Models.py
class CaseRequest(models.Model):
name = models.CharField(max_length=255)
datebirth = models.DateField()
status = models.CharField(max_length=255)
timeapplication = models.DateField()
category = models.CharField(max_length=255)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('caserequest')
class Case(models.Model):
name = models.CharField(max_length=255)
datebirth = models.DateField()
def __str__(self):
return self.name + ' | ' + str(self.datebirth)
def get_absolute_url(self):
return reverse('case_list')
Views.py
class CaseRequestView(ListView):
model = CaseRequest
template_name = 'caserequest.html'
caserequest.html
<div>
{% for caserequest in object_list %}
<div>
<p>{{ caserequest.name }}, {{ caserequest.datebirth }}</p>
<!-- The result should be here: YES or NO -->
</div>
{% endfor %}
</div>
You can simply create a #property decorator, say for example case_exists in the models so:
class CaseRequest(models.Model):
name = models.CharField(max_length=255)
datebirth = models.DateField()
status = models.CharField(max_length=255)
timeapplication = models.DateField()
category = models.CharField(max_length=255)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('caserequest')
#property
def case_exists(self):
return Case.objects.filter(name=self.name, datebirth=self.datebirth).exists()
Then in the template use it as:
<div>
{% for caserequest in object_list %}
<div>
<p>{{ caserequest.name }}, {{ caserequest.datebirth }} - {% if caserequest.case_exists %} Yes {% else %} No {% endif %}</p>
</div>
{% endfor %}
</div>
I think you must to create a py file called utils.py or similar in same directory where you have view.py with following code:
from django import template
from ptii.models import Case
register = template.Library()
#register.simple_tag
def get_case(caserequest_name, caserequest_datebirth):
if Case.objects.filter(name=caserequest_name).filter(
datebirth=caserequest_datebirth).exists():
return('Yes')
else:
return('No')
and in your html you have to load the file utils.py and use the function like this:
{% load utils.py %} {# load the file #}
{% get_case caserequest.name caserequest_datebirth as exist %} {# get Yes or No #}
Then you can use exist field in yout html as this:
{{ exist }}

get_object_or_404 only for DetailView

Im new to programming, im working on a app to study a given topic, and after I read the topic, im showing some flashcards, but I want to show only the flashcards related to the topic, but I always get more then one Deck of flashcards, probably because I'm not getting the correct deck_id. Here is the code:
models.py:
class Topic(models.Model):
author = models.ForeignKey(
User, related_name="topic", on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=100)
body = RichTextUploadingField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=120)
class Meta:
ordering = ["title"]
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('topic:topic-detail', kwargs={
"topic_slug": self.slug,})
class Deck(models.Model):
deckTopic = models.ForeignKey(
Topic, null=True, blank=True, on_delete=models.CASCADE)
description = models.CharField(max_length=510, null=False, blank=True)
is_active = models.BooleanField(default=False)
def __str__(self):
return self.description
def get_number_of_cards(self):
'''
Returns the number of cards in the decks related card_set
'''
return self.card_set.count()
get_number_of_cards.short_description = 'Card Count'
class Card(models.Model):
parentDeck = models.ForeignKey(Deck, on_delete=models.CASCADE)
front = models.TextField()
back = models.TextField()
def __str__(self):
return self.front
def has_prev_card(self):
'''
Returns true if card is not thee first card in the deck.
'''
first_card_in_deck = self.parentDeck.card_set.first()
if self == first_card_in_deck:
return False
return True
def get_prev_card(self):
'''
Return previous card in deck
'''
return self.parentDeck.card_set.filter(id__lt=self.id).last()
def has_next_card(self):
'''
Returns true if card is not the last card in the deck.
'''
last_card_in_deck = self.parentDeck.card_set.last()
if self == last_card_in_deck:
return False
return True
def get_next_card(self):
'''
Return next card in deck
'''
return self.parentDeck.card_set.filter(id__gt=self.id).first()
views:
class TopicDetailView(DetailView):
model = Topic
def get_context_data(self, *args, **kwargs):
context = super(TopicDetailView,
self).get_context_data(*args, **kwargs)
# Here is where i want to get the Deck related to the topic, but get more then one:
deck_obj = get_object_or_404(Deck)
card_list = deck_obj.card_set.all()
card_obj = card_list.first()
if self.request.method == 'GET' and 'card' in self.request.GET:
card_obj = get_object_or_404(Card, id=self.request.GET['card'])
context['deck_obj'] = deck_obj
context['card_obj'] = card_obj
return context
topic_detail.html:
{% extends 'base.html' %}
{% block content %}
<div class="topic-title">
<h5 class="mb-0">
{{object.title}}
</h5>
</div>
<div class="topic-body">
<p class="mb-0">
{{object.body}}
</p>
</div>
<div class="topic-deck">
{% if card_obj %}
<div class="notecard">
<div class="notecard-nav text-center">
{% if card_obj.has_prev_card %}
<a href="{% url 'topic:viewDeck' deck_obj.id %}?card=
{{card_obj.get_prev_card.id}}">Prev</a>
{% endif %}
{% if card_obj.has_next_card %}
<a href="{% url 'topic:viewDeck' deck_obj.id %}?card=
{{card_obj.get_next_card.id}}">Next</a>
{% endif %}
</div>
<div class="notecard-front">
<p class="text-center">Front</p>
<p>{{card_obj.front}}</p>
</div>
<div class="notecard-back">
<p class="text-center">Back</p>
<p>{{card_obj.back}}</p>
</div>
</div>
{% else %}
<p>No card found.</p>
{% endif %}
</div>
{% endblock %}
urls.py:
app_name = 'topic'
urlpatterns = [
path('topic/<slug:topic_slug>/<deck_id>/',
TopicDetailView.as_view(), name='viewDeck'),
]
How can I show only the Deck related to de Topic detail?
Your call to get_object_or_404 is incomplete. You've provided the model class, but not the parameters to get the object with:
deck_obj = get_object_or_404(Deck, pk=self.kwargs['deck_id'])
self.kwargs, contains all keyword arguments from the url. In your case it will contain topic_slug and deck_id.
pk is a shortcut for whatever the primary key is on your model. This makes me not think about how I named it and is resistant to future name changes of the primary key field.
I don't have to worry about deck_id not being in kwargs, because if it's not the URL won't match and the view isn't called.
And there it is :)

django iterate over a list that is an item of a queryset

I have a list that is generated by a method on one of my models. On the home page it works wonderfully, however when I go to a detail view of one project I can access all the parts of that project as they are direct fields of the Model, but I can't access the items in the list.
Model:
class Project(models.Model):
date_published = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
area = models.ForeignKey(Area, on_delete=models.PROTECT)
title = models.CharField(max_length=128, unique=True)
slug = models.SlugField(max_length=64)
summary = models.CharField(max_length=256)
others = models.CharField(max_length=128, blank=True)
deadline = models.DateField(null=True, blank=True)
priority = models.ForeignKey(Priority, on_delete=models.PROTECT)
closed = models.DateTimeField(null=True, blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Project, self).save(*args, **kwargs)
#property
def updates(self):
updates = []
sequence_id = 1
categories = set(self.update_set.all().values_list(
'category__id', flat=True))
for cat_id in categories:
a = Update.objects.filter(
project=self, category__id=cat_id).order_by('added').last()
if cat_id == sequence_id:
updates.append(a)
else:
for i in range(cat_id - sequence_id):
updates.append('null')
updates.append(a)
sequence_id = cat_id
sequence_id += 1
return updates
class Update(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
category = models.ForeignKey(UpdateCategory, on_delete=models.PROTECT)
update = models.TextField(max_length=240, blank=True)
added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.update
The view is simple:
class ProjectDetailView(DetailView):
template_name = 'project_portal/project_detail.html'
queryset = Project.objects.all()
and here is the dynamic url that I am using:
path('project/<int:pk>/',
ProjectDetailView.as_view(), name='project_detail'),
As for the template, I'm lost, here is one of the things I have tried:
<!DOCTYPE html>
{% extends "project_portal/base.html" %}
{% block home %}
<div id="main">
<div id="content">
<div>
<h1>{{ object.title }}</h1>
<h1>hello</h1>
{% if object_list %}
{% for item in updates %}
<p>{{ item }}</p>
{% endfor %}
{% else %}
<h2>No records found for this project</h2>
{% endif %}
</div>
</div>
</div>
{% endblock %}
What do I need to do to access the "updates" list that gets generated?
update is a property of the model instance, you need to access it from there like any other attribute. Also note, there is no object_list in a detail view.
<div>
<h1>{{ object.title }}</h1>
<h1>hello</h1>
{% for item in object.updates %}
<p>{{ item }}</p>
{% endfor %}
</div>

Check if all objects belong to a list django

Template:
{% for level in levels %}
{% if level.todo_set.all in tasks %}
<li>
<img src="{{ level.badge.url }}" alt="" />
</li>
{% else %}
<li>
<img src="{{ level.locked_badge.url }}" alt="" />
</li>
{% endif %}
{% endfor %}
views.py:
#login_required(login_url="/account/login/")
def StudentPublicProfile(request, pk=None):
student = User.objects.get(pk=pk)
levels = Level.objects.all()
todos = ToDo.objects.all()
list = []
tasks = Task.objects.filter(student=student)
for task in tasks:
list.append(task.todo)
context = {"student": student, "levels": levels, "tasks": list, "todos": todos}
return render(request, "student_public_profile2.html", context)
models.py:
class Level(models.Model):
number = models.IntegerField()
badge = models.ImageField()
locked_badge = models.ImageField()
timestamp = models.DateTimeField(
auto_now_add=True, auto_now=False, blank=True, null=True
)
unlock = models.CharField(max_length=10, default="A")
def __str__(self):
return str(self.number)
def get_absolute_url(self):
return reverse("student:level-detail", kwargs={"pk": self.pk})
class ToDo(models.Model):
level = models.ForeignKey(Level, on_delete=models.CASCADE)
name = models.CharField(max_length=150)
description = models.TextField()
timestamp = models.DateTimeField(
auto_now_add=True, auto_now=False, blank=True, null=True
)
def __str__(self):
return self.name
class Task(models.Model):
level = models.ForeignKey(Level, on_delete=models.CASCADE)
todo = models.ForeignKey(ToDo, on_delete=models.CASCADE)
student = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
content = models.TextField()
timestamp = models.TimeField(auto_now=True)
datestamp = models.DateField(auto_now=True)
like = models.ManyToManyField(User, related_name="user_likes", blank=True)
is_verified = models.BooleanField(default=False, blank=True)
def __str__(self):
return self.title
Basically, each level contains multiple todos. I want to check if all todos of the level are contained in the tasks list. This is what I'm doing in the template. But I'm not getting a correct result. What could be the issue here? Here I'm checking if all todos of a level are completed by a particular user. Only when a todo is completed it gets saved in Task
You are probably best to handle this inside your view.
for level in levels_list:
if Todo.objects.filter(level=level).count() == Task.objects.filter(student=student, level=level).count():
return (level completion code)
levellist = []
for level in levels:
if (
ToDo.objects.filter(level=level).count()
== Task.objects.filter(student=student, level=level).count()
):
levellist.append(level.number)
perc = (len(levellist) / len(levels)) * 100
In Template:
{% for level in levels %}
{% if level.number in levellist %}
<li>
<a href="javascript:;"><img src="{{ level.badge.url }}"
alt=""/></a>
</li>
{% else %}
<li>
<a href="javascript:;"><img src="{{ level.locked_badge.url }}"
alt=""/></a>
</li>
{% endif %}
{% endfor %}
Found a fix!

Calling a method from template

I seem to have a problem with calling a method from a django template. Given the model:
class Gallery(models.Model):
class Meta:
verbose_name_plural = "Galerie"
def photo_count(self):
return self.photo_set.count()
def random_image(self):
return self.photo_set.get(id=random.randint(1,self.photo_count()))
title = models.CharField(max_length=50)
imgur_id = models.CharField(max_length=15)
date_created = models.DateTimeField('Data utworzenia', auto_now=True)
is_active = models.BooleanField()
def __unicode__(self):
return self.title
where Gallery is foreignKey for Photo
views.py:
def index(request):
galleries = Gallery.objects.get(is_active=True)
if galleries.count() is 0:
messages.warning(request, "Niestety, żadna galeria nie jest aktywna, zaglądnij niebawem!")
return redirect(reverse(home))
elif galleries.count() is 1:
return render(request, 'gallery/home.html', {'gallery': galleries})
else:
return render(request, 'gallery/index.html', {'galleries': galleries})
I want to do this in template:
{% for gallery in galleries %}
{{ gallery.random_image }} <br />
{% endfor %}
The result I get is:
[ Photo object ]
[]
[]
[]
But when i call
{% for gallery in galleries %}
{{ gallery.photo_count }} <br />
{% endfor %}
The result is correct.
Why does this not work?
In my opinion you are complicated a little bit.
class Gallery(models.Model):
class Meta:
verbose_name_plural = "Galerie"
def random_image(self):
return self.photo_set.order_by('?')[0] if self.photo_set.count() else None
title = models.CharField(max_length=50)
imgur_id = models.CharField(max_length=15)
date_created = models.DateTimeField('Data utworzenia', auto_now=True)
is_active = models.BooleanField()
def __unicode__(self):
return self.title
and in the template,
{% for gallery in galleries %}
{% if gallery.random_image %}
{{ gallery.random_image }} <br />
{% else %}
No images found.
{% endif %}
{% endfor %}
Read more on order_by here
Note that ? could prove slightly heavy. If so, get thelist of ids of the photo_set associated with this gallery object, and get a random id from this list, rather than doing a
random.randint(1,self.photo_count())
Add #property to your fonction.
#property
def photo_count(self):
return self.photo_set.count()

Categories