Foreign Key - how to solve this in template - django - python

i have 3 models:
Book, Topiccenter and EntryBook.
these are model definitions:
class Book(models.Model):
title = models.TextField()
language = models.TextField()
class Topiccenter(models.Model):
title = models.TextField():
description = models.TextField()
class EntryBook(models.Model):
book = models.ForeignKey(Book,related_name="b_entries")
topiccenter = models.ForeignKey(Topiccenter,related_name="tc_books")
Now I am in Topiccenter T. and i search for books and get all books in DB. As you see, each book can be in multiple topiccenters.
What i want to do is, in the search result, i want to show whether each book is contained in current Topiccenter or not:
I will take all books books = Book.objects.all() and the current topiccenter as tc and render them to template and in template,
{% for book in books %}
{% for entry in book.b_entries.all %}
{% if entry.topiccenter.id == tc.id %}
already in this Topiccenter
{% else %}
add to this topiccenter
{% endif %}
{% endfor %}
{% endfor %}
but the problem is that one book is in two topiccenters and in template i am getting both already in this Topiccenter and add to this topiccenter which is nonesense. How can i repair my logic so that I can check if the book in this current topiccenter, and if not, show them add button
thanks

See how you can move it to the view. In this case, get all books associated with the tc and send that in the context.
Now, the template logic would be:
{% for book in books %}
{% if book in tc_books %}
already in this Topiccenter
{% else %}
add to this topiccenter
{% endif %}
{% endfor %}
Where (in the view)
tc_books = Books.objects.filter(b_entries__topiccenter = tc)
and send that in the context

Related

How do properly make an if statement in html

Im supposed to write an if statement in a detail.html template that states "if project has tasks" display a table otherwise display "no tasks in project.
I've tried
{% if task in project %}
{% if task in projects_list %}
{% if tasks in project %}
"displays table"
{% else %}
<p>no tasks for this project</p>
{% endif %}
here is my task model
class Task(models.Model):
name = models.CharField(max_length=200)
start_date = models.DateTimeField()
due_date = models.DateTimeField()
is_completed = models.BooleanField(default=False)
project = models.ForeignKey(
"projects.Project",
related_name="tasks",
on_delete=models.CASCADE,
)
assignee = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,
related_name="tasks",
on_delete=models.SET_NULL,
)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("show_my_tasks")
here is the view for projects
class ProjectListView(LoginRequiredMixin, ListView):
model = Project
template_name = "projects/list.html"
context_object_name = "projects_list"
If project is a list you probably want:
{% if project|length > 0 %}
Similar question Check if an array is not empty in Jinja2
If I'm understanding correctly, you want to check if a project has any relationship with a task. If this is so, you can refer to the project attribute on the Task model by using the related_name which is tasks in the template. For example:
# using project.tasks to check for an existing relationship with project and task;
# calling the count method as well to count how many tasks are connected to a project within the loop.
{% if project.tasks.count > 0 %}
# Displaying the table in here with the project's task info...
{% else %}
<p> no tasks for this project </p>
{% endif %}
Ideally, your for loop would look something like:
{% for project in projects_list %}
...
{% if project.tasks.count > 0 %}
# Displaying the table in here with the project's task info...
{% else %}
<p> no tasks for this project </p>
{% endif %}
...
{% endfor %}
That should work.
Partial answer, too long to add as a comment. You often don't need to handle the case of an empty list or set outside of the for loop. Instead:
{% for task in project.tasks %}
{% if forloop.first %}
<table> ... and table header
{% endif %}
<tr>
... stuff involving display of {{task.field}}s
</tr>
{% if forloop.last %}
</table> ... and any other table footer stuff
{% endif %}
{% empty %} ... optional
stuff to show if there are no tasks
{% endfor %}

How do I iterate ManyToMany field in Django template tag?

I have an object that contains a Many-to-Many field. I'm trying to iterate this field in Django template, but apparently I can't. Let me show you the code first.
models.py:
class Book(models.Model):
title = models.CharField(max_length = 100, blank=True)
category = models.ManyToManyField(Category)
def __str__(self):
return self.title
views.py:
def book_list(request):
books = Book.objects.all().order_by('-pk')
context = {
'books' : books,
}
return render(request, 'contents/book_list.html', context)
Template file:
{% for b in books %}
<div>
{{b.title}}
{% for cat in b.category %}
{{cat}}
{% endfor %}
</div>
{% endfor %}
Now I get 'ManyRelatedManager' object is not iterable error. How do I iterate the field and show all the category in each object?
It's because if you call b.category it returns only the relation object. To get its values (category objects) you have to add .all. Like this:
{% for b in books %}
<div>
{{ b.title }}
{% for cat in b.category.all %}
{{cat}}
{% endfor %}
</div>
{% endfor %}
By the way, I've also changed c.title to b.title, because I assume you want this book title, not something from global.

Django: How to query based on a ForiegnKey and display related data for each ForeignKey?

I am brand new to Django and to programming and I'm trying to make a page that will display the Workout with all the associated Exercises listed under each work out.
For example:
Chest
Chest Press
Incline Press
Flat Flyes
Shoulders
Shoudler Press
Arnold Press
Back/Legs
Wide Grip Pull Up
Neutral Grip Pull Up
Bent Over Row
Here is my code:
models.py
class Workout(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class Exercise(models.Model):
workout = models.ForeignKey(Workout, on_delete=models.CASCADE, related_name='workouts')
title = models.CharField(max_length=100)
weight = models.DecimalField(decimal_places=0, max_digits=10000)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
views.py
def home(request):
context = {
'workouts': Workout.objects.all()
}
return render(request, 'workouts/home.html', context)
def workout(request):
context = {
'workouts': Workout.objects.all(),
'exercises': Exercise.objects.all()
}
return render(request, 'workouts/workout.html', context)
workout.html
{% extends 'workouts/base.html' %}
{% block content %}
{% for workout in workouts %}
<h1>{{ workout.title }}</h1>
{% for exercise in exercises %}
<h3>{{ exercise.title }}</h3>
<p>{{ exercise.weight }}</p>
{% endfor %}
{% endfor %}
{% endblock content %}
In my view.py I have it set to Exercise.objects.all() which just displays all of the exercises under each Workout title, I can figure out how to get only the exercises that are associated with the Workout.
Like I said I am brand new to all of this and I'd appreciate any help! Thanks!
You can define property to Workout model to retrieve exercises related to the instance. Just add a new method with property decorator to retrieve Exercises related with that Workout.
class Workout(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
#property
def exercises(self):
return self.workouts.all()
Then you can use exercises in your html file like:
{% extends 'workouts/base.html' %}
{% block content %}
{% for workout in workouts %}
<h1>{{ workout.title }}</h1>
{% for exercise in workout.exercises %}
<h3>{{ exercise.title }}</h3>
<p>{{ exercise.weight }}</p>
{% endfor %}
{% endfor %}
{% endblock content %}
Another approach may be grouping Exercises with workouts in your view and pass them to template. For more information, you can check regroup
When you define FK relationship between two models, Django adds a manager to target model to query related objects. When you use this manager, you basicly run a query something like Exercise.objects.filter(workout_id=workout_instance_id). This is a queryset, so you can join with features of queryset features like filter, first and extra. You can use a plenty of queryset features on related objects. This is basicly the power of Django ORM mechanism. You can find a bit more in documentation if you need to.

Django: How to display author of query of posts?

I'm trying to make individual pages for each author showing their name and posts. I can't seem to get the username displayed.
views.py
class UserProfileView(generic.ListView):
template_name = 'howl/user-profile.html'
context_object_name = 'user_howls'
def get_queryset(self):
author = self.request.user
u = User.objects.get(username=author)
return Howl.objects.filter(author=u)
models.py
class Howl(models.Model):
author = models.ForeignKey(User, null=True)
content = models.CharField(max_length=150)
Here is where I'm stuck.
user-profile.html
{% extends 'howl/base.html' %}
{% block content %}
<h1>User: {{user_howl.author}}</h1>
{% for user_howl in user_howls %}
<ul>
<li>{{user_howl.content}}</li>
</ul>
{% endfor %}
{% endblock %}
The content is displayed just fine, but the heading just says "User: ", how do I give it a context without using a for loop?
I've tried:
{% for author in user_howls.author %}
<h1>User: {{author}}</h1>
{% endfor %}
and
{% if user_howls.author %}
<h1>User: {{user_howl.author}}</h1>
{% endif %}
Still the same outcome, displaying "User: "
user_howls is a queryset so it won't have an author attribute, you need to get the author of the iterated object
{% for howl in user_howls %}
<h1>User: {{ howl.author}}</h1>
{% endfor %}
More to the point though, it doesn't make sense to start from a Howl list, when you are just returning the results for the user_profile, nor does it make sense to use a ListView. so instead, start from the user and then look up its howls
user_obj.howl_set.all()
Since your queryset is based on the posts belonging to the current user, you can shortcut all of this and just show the user directly:
User: {{ user }}

Backward relationship in Django templates

I am trying to display data from different models in my template and I have no idea why it doesn't work properly.
Models.py:
class Company(models.Model):
name = models.CharField(max_length=100)
class Location(models.Model):
company = models.ForeignKey('Company',
related_name='locations')
Views.py:
def sth(request):
loc_list = Location.objects.all()
return render(request, 'blabla/index.html', {'loc_list': loc_list})
Template:
{% for loc in loc_list %}
{% for entry in loc.company_set.all %}
{{entry.name}}
{% endfor %}
{% endfor %}
Why the name of the company is not showing?
You foreign key relationship is the wrong way around... the way you have currently defined them, a Location can have only one Company associated with it, but in your template you are trying to fetch a list of Companys.
For your template logic to work, your models would have to be defined as follows:
class Company(models.Model):
name = models.CharField(max_length=100)
location = models.ForeignKey('Location')
class Location(models.Model):
# You probably need some other fields in here.
Then this would work:
{% for loc in loc_list %}
{% for entry in loc.company_set.all %}
{{entry.name}}
{% endfor %}
{% endfor %}
Only use "_set" when accessing backwards relationships. There is no
loc.company_set.all
because each location only has one company (that is what a ForeignKey is. You can access the location's company by doing loc.company). If you want location to have multiple companies, either see solarissmoke's answer (where you put the ForeignKey attribute on Company and not on Location), or use a ManyToManyField relationship (this will allow companies to have multiple locations and each location to have multiple companies).
Then you can access all the companies of a given location by doing this in the template:
{% for loc in loc_list %}
{% for entry in loc.company.all %}
{{entry.name}}
{% endfor %}
{% endfor %}
You can also access all the locations for a given company by using the related_name (company.locations).
Short answer:
{% for loc in loc_list %}
{{loc.company.name}}
{% endfor %}

Categories

Resources