I have 4 models
Class A
name
Class B
fk1 = FK(Class A)
Class C
fk = FK(Class B)
Class D
fk = FK(Class C)
And in templates i want to use reverse relationship over all these models
I tried this
{% for que in Class A items %}
{% for item in que.b.c.d_set.all %}
but note getting the result. Any suggestions?
If I understood the question will be something like:
{% for que in a_times %}
{% for b_item in que.b_set.all %}
{% for c_item in b_item.c_set.all %}
{% for d_item in c_item.d_set.all %}
# stuff with d_item
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
That is because you are using ForeignKey instead of OneToOneField, and I suggest you use related_name
In the view you can do this:
from app.models import A, D
from django.shortcuts import render
def view(request):
data = {}
a_itemsqs = A.objects.all() # here you filter A objects
d_items = D.objects.filter(c__b__a__in=a_itemsqs)
data['d_items'] = d_items
return render(request, data, 'template.html')
Related
What I want to do :
Display the human readable value of a charfield with choices via get_F00_display or other in views.py and then in template.
I have a Leave model for leaves management and want to display a template with all the leaves associated with the authenticated user.
What I have done :
Of course, I've read Django documentation (4.1) and find something interesting with get_F00_display but cannot make it works fine.
model.py (simplified)
class Leave(CommonFields):
LEAVES_TYPES = [
('10', _('Type 1')),
('20', _('Type 2')),
('30', _('Type 3')),
]
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
type = models.CharField(max_length=3, choices=LEAVES_TYPES, null=True, default="10")
def __str__(self):
return self.owner.first_name + " " + self.owner.last_name + " : du " + self.begin_date.strftime("%d-%m-%Y") + " au " + self.end_date.strftime("%d-%m-%Y")
views.py
from django.shortcuts import render
from django.utils.translation import gettext as _
from .models import Leave
from django.views import generic
class LeaveListView(generic.ListView):
model = Leave
context_object_name = 'leave_list'
def get_queryset(self):
return Leave.objects.filter(is_active=True).values('type', 'begin_date','end_date','range','comment','status')
def get_context_data(self, **kwargs):
# Call the base implementation first to get the context
context = super(LeaveListView, self).get_context_data(**kwargs)
# Create any data and add it to the context
context['colHeaders'] = ['Type',
'From',
'To',
'Range',
'Comment',
'Status',]
return context
leave_list.html
{% extends "main/datatables.html" %}
<!-- TABLE TITLE -->
{% block tableTitle %}Leaves{% endblock %}
<!-- TABLE HEADER -->
{% block tableHeader %}
{% if colHeaders %}
{% for header in colHeaders %}
<th>{{header}}</th>
{% endfor %}
{% else %}
<p>No results</p>
{% endif %}
{% endblock %}
<!-- TABLE BODY -->
{% block tableBody %}
{% if leave_list %}
{% for leave in leave_list %}
<tr>
<td>{{leave.type}}</td>
<td>{{leave.begin_date}}</td>
<td>{{leave.end_date}}</td>
<td>{{leave.range}}</td>
<td>{{leave.comment}}</td>
<td>{{leave.status}}</td>
</tr>
{% endfor %}
{% else %}
<p>No results</p>
{% endif %}
{% endblock %}
<!-- TABLE FOOTER -->
{% block tableFooter %}
{% if colHeaders %}
{% for header in colHeaders %}
<th>{{header}}</th>
{% endfor %}
{% else %}
<p>No results</p>
{% endif %}
{% endblock %}
Problem :
{{leave.type}}
returns the key of the choices but I'm trying to display the human readable name in LEAVES_TYPES like "Type 1"...
What I've tried :
Using get_F00_display this way : get_type_display in my views.py :
def get_queryset(self):
return Leave.objects.filter(is_active=True).values(get_type_display, 'begin_date','end_date','range','comment','status')
Same thing in the template but no readable name displayed...
I have two very simple classes for products and photos. I would like to present products on the main page of my store along with photos related to a foreign key. But I do not know how to do this, I found many answers to get this in the view using a join like 'prefetch_related' but my ID changes for each case. So how do you present your photos for each product? are there any Django tags about which I do not know?
my models.py
class Product(models.Model)
name = models.CharField(max_length=10)
class Image(models.Model)
image = models.ImageField()
name_related = models.ForeignKay(Product, on_delate=models.CASCADE)
views.py
def home(request):
product_list = Product.objects.all()[:12]
#img = ??
context = {'product_list': product_list,
}
return render(request, 'home.html', context)
home.html
{% for product in product_list %}
{{ product.name }}
<!-- {{ product.imge }} ' element presenting the first photo for each 'product' model'???-->
{% endfor %}
Any help will be appreciated.
Since a foreign relation has been established you can iterate through the related models from the parent model.
The related models are already accessible from the parent model without doing anything explicit.
In your template you can do this:
{% for product in product_list %}
{{ product.name }}
{% for product_image in product.image.all %}
<!-- Iterate through this products related images -->
{{ product_image.image.url }}
{% endfor %}
{% endfor %}
For performance reasons, you will want to prefetch the relations, or else for every product you will do an additional query, so, in your view you will want to add this:
product_list = Product.objects.all().prefetch_related('image')[:12]
Just do
{% for product in product_list %}
{{ product.name }}
{% for image in product.image.all %}
<!-- {{ image.image.url }} -->?
{% endfor %}
{% endfor %}
I have this models:
class House(models.Model):
name = models.CharField()
class BedRoom(models.Model):
name = models.CharField()
class Catalog(models.Model):
name = models.CharField()
house = models.ForeignKey(House)
bedroom = models.ForeignKey(BedRoom)
at admin.py Catalog is inline to House
class CatalogInline(admin.TabularInline):
model = Catalog
class HomeAdmin(admin.ModelAdmin):
inline = [CatalogInline]
then I need obtain at view the list of Bedrooms from House Catalog, at Home model I have:
def bedrooms(self):
return self.catalog_set.all()
but when I do at template obtaining "houses" from a view:
{% for house in houses %}
{% for h in house %}
<p>{{h.name}}</p>
{% endfor %}
{% endfor %}
I get an error:
'Catalog' object is not iterable
what I'm doing wrong?
I should define the model in a different way?
As I mentioned, you don't seem to actually be calling the bedrooms method. I guess (and it is just a guess) you mean this:
{% for house in houses %}
{% for h in house.bedrooms %}
<p>{{h.name}}</p>
{% endfor %}
{% endfor %}
The bedrooms method is pointless though, as you can just as easily do this:
{% for house in houses %}
{% for h in house.catalog_set.all %}
<p>{{h.name}}</p>
{% endfor %}
{% endfor %}
I have a object that I'm trying to iterate over within my template.
My issue is that one of the fields response contains json data and I get this error message
transaction' object is not iterable
{% for item in transaction %}
{{ item.notjson_fields}}
{% for jsonItem in item.response %}
{{jsonItem}}
{% endfor %}
{% endfor %}
Model:
date_created = models.DateTimeField(auto_now_add=True)
request = JSONField()
response = JSONField()
You are trying to iterate over a Transaction object which is not iterable.
Try something like
{{ transaction.notjson_fields}}
{% for jsonItem in transaction.response %}
{{ jsonItem }}
{% endfor %}
I can't be sure though without knowing what Transaction looks like
Edit:
Since response is a JSONField, you can access it like a dict. Just do
{{ transaction.response.amount }}
If like Ngenator said, your field is not a JSON object but a string, you may need to load it first. First register a new template tag
from django import template
register = template.Library()
import json
#register.filter
def load_json(value):
return json.loads(value)
Then to get (just) the amount in your template
{% with transaction.response|load_json as jsondict %}
{% for k, v in jsondict.items %}
{% if k == "amount" %}
<td>{{ v }}</td>
{% endif %}
{% endfor %}
{% endwith %}
Good Luck.
I have a model called Subtopic. One of my templates runs a forloop on an object, returning a different field for each cell of a table row.
Two of the table cells look up a field which is a ManytoMany foreign key, both to the same foreign model, Resource. I want each to display different results, based on the value of a boolean field within the Resource model.
What you see below is currently working fine, but doesn't attempt to filter by the boolean field.
models.py:
class ICTResourceManager(models.Manager):
def get_query_set(self):
return super(ICTResourceManager, self).get_query_set().filter('is_ict': True)
class NonICTResourceManager(models.Manager):
def get_query_set(self):
return super(NonICTResourceManager, self).get_query_set().filter('is_ict': False)
class Resource(models.Model):
subtopics = models.ManyToManyField(Subtopic)
external_site = models.ForeignKey(ExternalSite)
link_address = models.URLField(max_length=200, unique=True, verify_exists=False)
requires_login = models.BooleanField()
is_ict = models.BooleanField()
flags = models.ManyToManyField(Flag, blank=True)
comment = models.TextField()
def __unicode__(self):
return u'%s %s' % (self.external_site, self.link_address)
objects = models.Manager()
ict_objects = ICTResourceManager()
nonict_objects = NonICTResourceManager()
class Meta:
ordering = ['external_site', 'link_address']
views.py:
def view_ks5topic(request, modulecode, topicshortname):
listofsubtopics = Subtopic.objects.filter(topic__module__code__iexact = modulecode, topic__shortname__iexact = topicshortname)
themodule = Module.objects.get(code__iexact = modulecode)
thetopic = Topic.objects.get(module__code__iexact = modulecode, shortname__iexact = topicshortname)
return render_to_response('topic_page.html', locals())
My template:
{% for whatever in listofsubtopics %}
<tr>
<td>
{{ whatever.objective_html|safe }}
<p>
{% if request.user.is_authenticated %}
{% with 'objective' as column %}
{% include "edit_text.html" %}
{% endwith %}
{% else %}
{% endif %}
</td>
<td>
{% regroup whatever.resource_set.all by external_site.name as resource_list %}
{% for external_site in resource_list %}
<h4>{{ external_site.grouper }}</h4>
<ul>
{% for item in external_site.list %}
<li>{{ item.comment }}</li>
{% endfor %}
</ul>
{% endfor %}
</td>
</tr>
{% endfor %}
As you can see, I've added extra managers to the model to do the filtering for me, but when I replace the appropriate lines in the template, I just get blanks. I have tried: for external_site.ict_objects in resource_list and for item.ict_objects in resource_list and <a href="{{ item.ict_objects.link_address }}">. If this were in the view I could probably do the filter just by .filter('is_ict': True), but with this being inside a forloop I don't know where to do the filtering.
I also tried writing regroup whatever.resource_set.filter('is_ict': True) in the template, but the syntax for regrouping seems to use resource_set.all rather than resource_set.all() (and I don't know why) so the filter text doesn't work here.
Turns out it was possible to do it using a custom template filter. The original efforts to filter within the template weren't working, given that as is well documented the template language is not a fully-fledged python environment. My original question remains open for anyone who knows an alternative method that more directly addresses the question I was originally asking, but here's how I did it:
myapp_extras.py:
from django import template
register = template.Library()
def ict(value, arg):
"filters on whether the is_ict Boolean is true"
return value.filter(is_ict=arg)
register.filter('ict', ict)
My template, note the use of the custom filter in line 2:
<td>
{% regroup whatever.resource_set.all|ict:1 by external_site.name as resource_list %}
{% for external_site in resource_list %}
<h4>{{ external_site.grouper }}</h4>
<ul>
{% for item in external_site.list %}
<li>{{ item.comment }}</li>
{% endfor %}
</ul>
{% endfor %}
</td>
After this I was able to remove the additional custom managers from the model.
One further question, when filtering for the boolean is_ict field, I found that I had to use filter(is_ict=1) and filter(is_ict=0). Is that the only way to refer to a True or False value?