Showing a Django ForeignKey value in template - python

I'm trying to show a model's ForeignKey value in a template, all other fields are showing fine but I couldn't make it work. Here is my code:
models.py:
class BodyPart(models.Model):
body_part = models.CharField(max_length=20, unique = True)
class Exercise(models.Model):
body_part = models.ForeignKey(BodyPart, on_delete=models.CASCADE, default = "bodypart", related_name="bodypart")
views.py:
exercises = Exercise.objects.filter(category=exercisedetailcategory).values()
context = {
"exercises" : exercises,
}
return render(request,"exercises-categories.html",context)
template:
{% for exercise in exercises %}
<span class="post-meta-category">{{exercise.body_part}}</span>
<div class="post-item-description">
{{exercise.title}}
<p>{{exercise.content}}</p>
{% endfor %}

This is one of the many reasons why you should not use .values(). If you pass Exercise models, you can fetch the related object into memory. You can make use of .select_related(..) to optimize the query:
exercises = Exercise.objects.filter(
category=exercisedetailcategory
).select_related('body_part')
context = {
'exercises' : exercises,
}
return render(request, 'exercises-categories.html', context)
Then in the template, we can render this with:
{% for exercise in exercises %}
<span class="post-meta-category">{{ exercise.body_part.body_part }}</span>
<div class="post-item-description">
{{ exercise.title }}
<p>{{ exercise.content }}</p>
{% endfor %}
You can furthermore implement a __str__ method for BodyPart:
class BodyPart(models.Model):
body_part = models.CharField(max_length=20, unique=True)
def __str__(self):
return self.body_part
and then render this with:
{% for exercise in exercises %}
<span class="post-meta-category">{{ exercise.body_part }}</span>
<div class="post-item-description">
{{ exercise.title }}
<p>{{ exercise.content }}</p>
{% endfor %}

in your exercise model, ignore the default part.(its possible to show any message that tell users "no body_part" such as
{% if not exercise.body_part %} -> No thing to show)
and make sure, you have a value in your exercise.body_part Which it means you have to have an object in your BodyPart model in relation to the current object of this model.
also it should be {{ exercise.body_part.body_part }}
the second one is to extract the value of related BodyPart objects value

Related

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.

Render relationship model in template

I am trying to render a model with a relationship but I am not able to do so.
class LogBook(models.Model):
name = models.CharField(max_length=50, verbose_name="Nom du registre de maintenance")
members = models.ManyToManyField(User)
class LogMessages(models.Model):
logbook = models.ForeignKey(LogBook)
message = models.CharField(max_length=200, verbose_name="Détail du problème")
class LogDone(models.Model):
logmessage = models.ForeignKey(LogMessages)
message = models.CharField(max_length=200)
My view:
log = get_object_or_404(LogBook, pk=log_id)
logmessages = LogMessages.objects.filter(logbook=log_id)
My template
{% for logmessage in logmessages.all %}
{{logmessage.logdone.message}}
{{% endfor %}}
But the logdone object is not showing, any idea ?
Since your LogMessage model has a foreign key to log done, it's not a One to One relation and you have to access the related LogDone objects using the _set notation. There's also a slight typo, I believe. It should logmessages and not logmessages.all
{% for logmessage in logmessages %}
{% for done in logmessage.logdone_set.all %}
{{ done.message }}
{% endfor %}
{% endfor %}
I forgot that I added a related_name equal to "logdones" so I did the following :
{% for logmessage in logmessages %}
{% for done in logmessage.logdones.all %}
{{ done.message }}
{% endfor %}
{% endfor %}
And now it is working, thanks to #Vishal

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.

Accessing dictionary keyed by object in django template

I am trying to access dict values in my django template. The dict keys are objects. Here is a snippet of my codebase:
models.py
class ProductCategory(models.Mode):
name = models.CharField(max_length=64, blank=False, null=False, unique=True)
slug = models.SlugField(blank=False, null=False, unique=True)
class Product(models.Model):
category = models.ForeignKey(ProductCategory, on_delete = models.CASCADE)
# other fields
pass
def __str__(self):
return self.title
views.py
def index(request):
products = Product.objects.all()
categorized_products = {}
for p in products:
prod_category = p.category
temp = categorized_products.get(prod_category,[])
temp.append(p)
categorized_products[prod_category] = temp
context = {'products_dict': categorized_products, 'categories': categorized_products.keys() }
return render(request,'product/index.html', context=context)
mytemplate.html (relevant snippet)
<div class="tab-content">
{% for category in categories %}
{% if not forloop.counter0 %}
<div class="tab-pane fade in active" id="{{ category.slug }}">
{% else %}
<div class="tab-pane fade in" id="{{ category.slug }}">
{% endif %}
<div >
<h4>{{ category.description }}</h4>
<div class="container-fluid">
<div class="row">
<div class="col-md-3" style="border:1px solid red;">
{% for product in products_dict.category %}
{{ product }}
{% endfor %}
When I step through the code with a debugger, I can see that the variable products_dict is a dict and correctly populated by the view. However, when I run the code, the for loop code is not executed.
Similarly, when I run the same code I have in views.py in the shell, the dict is correctly populated, so I know that there is data in the database and that the retrieved data is being correctly passed to the django template.
So why am I unable to access the dict value in the template and why is the product not displaying in my template?
The simple answer is that Django templates don't do that. Dot reference in template variables does dictionary lookup (among other things) but not based on the value of another variable name:
Note that “bar” in a template expression like {{ foo.bar }} will be interpreted as a literal string and not using the value of the variable “bar”, if one exists in the template context.
https://docs.djangoproject.com/en/3.0/ref/templates/language/#variables
Generally I would solve this by arranging for something iterable to exist on the category values directly, rather than trying to use the category object as a dict key. That could be a reverse FK manager:
{% for product in category.products_set.all %}
Or something I set on the in-memory category objects in the view, if I need to transform or filter the products per-category:
categories = ProductCategory.objects.all()
for category in categories:
category.products = [transform_product_somehow(product) for product in category.products_set.all()]
(Optionally with use of fetch_related to prefetch the category products rather than doing an additional query per category.)
You can use a custom filter for that. In custom_tags.py:
from django import template
register = template.Library()
#register.filter
def get_obj_field(obj, key):
return obj[key]
Then load the tags in django:
{% load custom_tags %}
And use it like this:
{% for product in products_dict|get_obj_field:category %}
{{ product }}
{% endfor %}

How do I add plain text info to forms in a formset in Django?

I want to show a title and description from a db query in each form, but I don't want it to be in a charfield, I want it to be html-formatted text.
sample template code:
{% for form, data in zipped_data %}
<div class="row">
<div class="first_col">
<span class="title">{{ data.0 }}</span>
<div class="desc">
{{ data.1|default:"None" }}
</div>
</div>
{% for field in form %}
<div class="fieldWrapper" style="float: left; ">
{{ field.errors }}
{{ field }}
</div>
{% endfor %}
{% endfor %}
Is this the most idiomatic way of doing this? Or, is there a way to add text that will not be displayed inside of a textarea or text input to my model:
class ReportForm(forms.Form):
comment = forms.CharField()
?
Instead of zipping your forms with the additional data, you can override the constructor on your form and hold your title/description as instance-level member variables. This is a bit more object-oriented and learning how to do this will help you solve other problems down the road such as dynamic choice fields.
class MyForm (forms.Form):
def __init__ (self, title, desc, *args, **kwargs):
self.title = title
self.desc = desc
super (MyForm, self).__init__ (*args, **kwargs) # call base class
Then in your view code:
form = MyForm ('Title A', 'Description A')
Adjust accordingly if you need these values to come from the database. Then in your template, you access the instance variables just like you do anything else, e.g.:
<h1>{{ form.title }}</h1>
<p>{{ form.desc }}</p>
From the way you phrased your question, I think you probably have some confusion around the way Django uses Python class attributes to provide a declarative form API versus instance-level attributes that you apply to individual instances of a class, in this case your form objects.
Check out this link for a good discussion on the distinction
And this one
I just created a read-only widget by subclassing the text input field one:
class ReadOnlyText(forms.TextInput):
input_type = 'text'
def render(self, name, value, attrs=None):
if value is None:
value = ''
return value
And:
class ReportForm(forms.Form):
comment = forms.CharField(widget=ReadOnlyText, label='comment')
I had to solve a similar problem and like your idea Andrei. I had some issues using it though, as, if there were validation errors, the value of the read-only field would get lost. To solve this, I did something similar but overrode HiddenInput instead and kept the value in a hidden form field. ie:
class ReadOnlyText(forms.HiddenInput):
input_type = 'hidden'
def render(self, name, value, attrs=None):
if value is None:
value = ''
return mark_safe(value + super(ReadOnlyTextWidget, self).render(name, value, attrs))
class ReportForm(forms.Form):
comment = forms.CharField(widget=ReadOnlyText, label='comment')
I think you can get it with "{{ field.value }}". Maybe it's the easier way.
{% for form in formset %}
{% for field in form %}
{% if forloop.counter = 1 %}
<td><img src="{{ MEDIA_URL }}{{ field.value }}"/></td>
{% endif %}
{% if forloop.counter = 2 %}
<td>{{ field.value }}</td>
{% endif %}
{% if forloop.counter > 2 %}
<td>{{ field }}{{ field.errors }}</td>
{% endif %}
{% endfor %}
{% endfor %}

Categories

Resources