How can I access fields other than the grouper in a Django group_by function?
class dateEvent(models.Model):
event = models.ForeignKey('Event', on_delete=models.CASCADE)
start_date_time = models.DateTimeField(auto_now=False, auto_now_add=False)
def __str__(self):
return "%s" % (self.event.title)
def description(self):
return "%s" % (self.event.description)
class Event(models.Model):
description = RichTextUploadingField(max_length=200)
view:
def my_view(request):
events = dateEvent.objects.all()
context = {
'events': events,
}
return render(request, 'view.html', context)
template:
<ul>
{% for event in dateEvents_list %}
<li><h5>Event: {{ event.grouper }}</h5>
<h6>Description: {{ event.description }}</h6> #How can access the description of the main event?
<ul>
{% for dateEvent in event.list %}
<li>date: {{ dateEvent.start_date_time }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
I'd like to have the title, which is the grouper so it's fine, but also the description.
You can also access your grouper's list objects to get details about the "first record" in your group. So using:
event.grouper.list.0.<any_model_field_you_want>
Will work just fine. I have this in production and it solved my needs immediatly. I found this in a rejected feature request for Django here: https://code.djangoproject.com/ticket/13452
If you need to traverse your model's relationship tree you can do the following.
event.grouper.list.0.<related_model_name>.<any_model_field_you_want>
You can access model attributes from the grouper directly using event.grouper.<any-model-attribute>.
In your example here is how it would look:
<ul>
{% for event in dateEvents_list %}
<li><h5>Event: {{ event.grouper }}</h5>
<h6>Description: {{ event.grouper.description }}</h6>
<ul>
{% for dateEvent in event.list %}
<li>date: {{ dateEvent.start_date_time }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
I can't find any documentation around this usage.
Related
I have the following template:
{% extends "artdb/base.html" %}
{% block content1 %}
<h4>Persons:</h4>
<ul>
{% for p in ans %}
<h5>First name: {{p.firstName}}</h5>
<h5>Last name: {{p.lastName}}</h5>
<h5>Phone: {{p.phoneNumber}}</h5>
<h5>Adress: {{p.streetAdress}}</h5>
<h5>Zip Code: {{p.zipcode}}</h5>
<h5>City: {{p.city}}</h5>
<hr>
{% endfor %}
</ul>
{% endblock content1 %}
{% block content2 %}
<h4>Roles:</h4>
<ul>
{% for p in ans %}
<h5>Role:{{p.persons.role}}</h5>
<hr>
{% endfor %}
</ul>
{% endblock content2 %}
and the model:
class Person(models.Model):
mail=models.EmailField()
firstName=models.CharField(max_length=200)
lastName=models.CharField(max_length=200)
phoneNumber=PhoneNumberField()
streetAdress=models.CharField(max_length=200)
zipcode=models.CharField(max_length=200)
city=models.CharField(max_length=200,default="Göteborg")
country=models.CharField(max_length=200,default="Sweden")
def __str__(self):
return "%s %s" % (self.firstName,self.lastName)
class Meta:
ordering = ('firstName','lastName')
class Role(models.Model):
role=models.CharField(max_length=200)
person=models.ManyToManyField(Person)
def __str__(self):
return self.role
class Meta:
ordering = ('role',)
But when I run the above code the only output that I get is from the block content1, i.e I cannot access the role content. I thought that role.persons.role would do it but apperantley not. There is a many-to-many relationship between perssons and roles.
Any ideas?
This should work
{% block content2 %}
<h4>Roles:</h4>
<ul>
{% for p in ans %}
{% for role in p.role_set.all %}
<h5>Role:{{ role }}</h5>
<hr>
{% endfor %}
{% endfor %}
</ul>
{% endblock content2 %}
We have to create a second for loop, since a many to many relationship will always return a list. Not a single instance. So essentially it's just like accessing a 2d array.
In Django you only have to define a n:n relationship on one end. Django will then automatically add it to the other model as well. It does this by taking the related model name and suffixing _set. So if we want to reference all of the roles attached to a person, it would be person.role_set. The other way around it would be role.person like you defined in the model.
I've a cycle in my template, which get's all the categories
like this:
<div class="panel">
<h4 class="title1">Категории</h4>
<ul class="clear-list">
{% for categ in categs %}
<li>
{{ categ.name }}
</li>
{% endfor %}
</ul>
</div>
And my views.py looks like:
def adverts(request):
args = {}
args.update(csrf(request))
args['adverts'] = Advert.objects.all().order_by('-advert_date', '-id')
args['sections'] = AdvertSection.objects.all().order_by('-name')
args['categs'] = AdvertCategory.objects.all().order_by('-name')
args['username'] = auth.get_user(request).username
return render_to_response('adverts.html', args)
Now comes question....
How to show total count of objects attached to the category?
Output:
Category1
Category2
Category3
Expected Output:
Category1(115)
Category2(546)
Category3(832)
Please help me...
Assuming the objects attached to your categories are called Adverts:
{% for categ in categs %}
{{ categ.name }} ({{ categ.advert_set.count }})
{% endfor %}
If you changed the attribute related_name of your Adverts in the Category ModelClass in your models.py file you'll have to adjust advert_set to the corresponding related_name.
For more information about how to access related objects take a look at the docs.
so i have a model which is,
class Category(SmartModel):
item=models.ManyToManyField(Item)
title=models.CharField(max_length=64,help_text="Title of category e.g BreakFast")
description=models.CharField(max_length=64,help_text="Describe the category e.g the items included in the category")
#show_description=check box if description should be displayed
#active=check box if category is still avialable
display_order=models.IntegerField(default=0)
def __unicode__(self):
return "%s %s %s %s " % (self.item,self.title, self.description, self.display_order)
and as you may see, it has a manytomany field
item=models.ManyToManyField(Item)
i want to return all the items in a template, here is my views.py for this
def menu(request):
categorys= Category.objects.all()
items= categorys.all().prefetch_related('item')
context={
'items':items,
'categorys':categorys
}
return render_to_response('menu.html',context,context_instance=RequestContext(request))
here is how am doing it in the templates,
<ul>
{% for item in items %}
<li>{{ item.item }}
</li>
</ul>
{% endfor %}
after all this,this is what it is returning in my web page,
<django.db.models.fields.related.ManyRelatedManager object at 0xa298b0c>
what am i doing wrong,I have really looked around but all in vain, hoping you can help me out and thanking you in advance
Exactly, you have a many to many manager. You need to actually query something... like all()
{% for item in items %}
{% for i in item.item.all %}
{{ i }}
{% endfor %}
{% endfor %}
Based on your variable naming, I think you're confusing the results of prefetch_related as a bunch of items. It is in fact returning a QuerySet of Category objects.
So it would be more intuitive to call them categories.
{% for category in categories %}
{% for item in category.item.all %}
{{ item }} {# ...etc #}
Try to use:
categorys= Category.objects.prefetch_related('item').all()
And then in template:
{% for category in categorys %}
{% for item in category.item.all %}
{{ item }}
{% endfor %}
{% endfor %}
I think I am following the doc correctly but still le* weird problem occurs : MPTT template tag displays the tree nicely, indented and all, but not node.name tag (empty)
My Model
class Forme(MPTTModel):
name = models.CharField(max_length=50, unique=True)
source_id = models.IntegerField(max_length=50, unique=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
def __unicode__(self):
return unicode(self.name)
class MPTTMeta:
order_insertion_by = ['name']
My View
def listnode(request):
nodes = Node.objects.using('hmdjango').all()
selectforme = Forme.tree.all()
allnodes = []
for n in Node.objects.using('hmdjango').all() :
try:
intervention = FieldDataFieldItemIntervention.objects.using('hmdjango').get(entity_id__exact=n.id)
except FieldDataFieldItemIntervention.DoesNotExist:
intervention.field_item_intervention_value = 'BOOM'
try:
forme = FieldDataFieldItemForme.objects.using('hmdjango').get(entity_id__exact=n.id)
except FieldDataFieldItemForme.DoesNotExist:
forme.field_item_forme_tid = 'BOOM'
allnodes.append({'id':n.id, 'title':n.title,\
'intervention':intervention.field_item_intervention_value, \
'forme':forme.field_item_forme_tid})
return render_to_response('node_list.html', {'nodes':allnodes, 'formes':selectforme}, context_instance=RequestContext(request))
My template (I did load mptt_tags)
<ul class="root">
{% recursetree formes %}
<li>
{{ forme.name }}
{% if not forme.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
EDIT
A classic loop through "formes" variables shows that I can access name attribute. Thus my guess is recursivetree template tag, provided by django-mptt, is my main suspect here
{% for forme in formes %}
<li>{{ forme.name }}
{% endfor %}
Found the issue.
In the doc we have the example
<ul class="root">
{% recursetree nodes %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
node is an instance of our MPTT Model. But it has to be call "nodes". AND no variable "nodes" should be in your template.
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?