Print key/value object in a template - django

I'm using the django(v.1.2) generic view "list_detail" to print some objects (Model Article) stored in a db.
In urls.py I added the following line
urlpatterns += patterns('django.views.generic.list_detail',
url(r'^article/(?P<slug>[\-\d\w]+)/$', 'object_detail', {'slug_field': 'title_slug', 'queryset': Article.objects.filter(is_public=True)}, name='article'),
)
In the respective template (article_detail.html) I would like to print the article iterating over all its fields. Actually I wrote:
{% for k,v in object.fields %}
<p>{{k}}:{{v}}<p>
{% endfor %}
but it doesn't work. Any suggestions?

'queryset': Article.objects.filter(is_public=True) sends a list to your template.
but in template you are treating it like an Article object.
'article': Article.objects.filter(is_public=True)[0]
then you can access all items of Article. However, I don't understand what you are trying to do with it. is Article.fields a list or dict?

Let us solve this by an example-
I have a model as-
class item(models.Model):
item_number = models.AutoField(primary_key=True)
item_name = models.CharField(max_length=200)
lifetime_rating = models.DecimalField(max_digits=3,decimal_places=1, default=0)
current_rating = models.DecimalField(max_digits=3,decimal_places=1, default=0)
lifefeedbacknum = models.IntegerField(default=0)
currentfeedbacknum = models.IntegerField(default=0)
def __unicode__(self):
return self.item_name
def retlifetime_rating(self):
return self.lifetime_rating
Note method-retlifetime_rating which returns the value of lifetimerating for an object instance.
Now we wish to display lifetime ratings for all products
in views.py-
def showrating(request):
itemslist= models.item.objects.all()
return render(request, 'showrating.html', 'sitems' : itemslist)
the showrating.html file contains the following code snippet-
{% for element in sitems %}
<tr>
<td>{{ element }}</td>
<td>{{ element.retlifetime_rating }}</td>
</tr>
{% endfor %}
basically if you wish to display different fields of an object, you need to have a corresponding method call to return that field
off-course there are other ways to do it but this is most likely the simplest and easiest to implement

Related

How to target a specific object with Django templates

I have a class called Features in my models.py. In my html, I am displaying a list on the right that excludes two of these Features, one is the active feature that has been selected, the other is the most recently added since they are the main content of my page. The remaining Features in the list are displayed by date and do show what I am expecting.
Now, I want to single out the first, second and third Features (title only) in THAT list so I can place them in their own separate divs - because each has unique css styling. There are probably numerous ways of doing this, but I can't seem to figure any of them out.
This is a link to my project to give a better idea of what I want (basically trying to get the content in those colored boxes on the right.)
I'm just learning Django (and Python really), so thanks for your patience and help!
HTML
{% for f in past_features %}
{% if f.title != selected_feature.title %}
{% if f.title != latest_feature.title %}
<h1>{{ f.title }}</h1>
{% endif %}
{% endif %}
{% endfor %}
VIEWS
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
past_features = Feature.objects.order_by('-pub_date')
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
MODELS
class Feature(models.Model):
title = models.CharField(db_index=True, max_length=100, default='')
content = models.TextField(default='')
pub_date = models.DateTimeField(db_index=True, default=datetime.now, blank=True)
def __str__(self):
return self.title
def __iter__(self):
return [
self.id,
self.title ]
You can either store the first three Features in separate variables in your context or add checks to your template loop like {% if forloop.first %} or {% if forloop.counter == 2 %}.
If all you want is to not have the
selected_feature
latest_feature
these two records out of the past_features queryset, then you can use exclude on the past_features query and pass the id's of the selected_features and latest_feature objects.
The views.py would look like:
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
# Collect all the id's present in the latest_feature
excluded_ids = [record.pk for record in latest_feature]
excluded_ids.append(selected_feature.pk)
#This would only return the objects excluding the id present in the list
past_features = Feature.objects.order_by('-pub_date').exclude(id__in=excluded_ids)
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
Django provides a rich ORM and well documented, go through the Queryset options for further information.
For access to a specific object in Django templates see following example:
For access to first object you can use {{ students.0 }}
For access to second object you can use {{ students.1 }}
For access to a specific field for example firstname in object 4 you can use {{ students.3.firstname }}
For access to image field in second object you can use {{ students.1.photo.url }}
For access to id in first object you can use {{ students.0.id }}

How to auto-generate simple HTML with model fields? [duplicate]

I'm trying to create a basic template to display the selected instance's field values, along with their names. Think of it as just a standard output of the values of that instance in table format, with the field name (verbose_name specifically if specified on the field) in the first column and the value of that field in the second column.
For example, let's say we have the following model definition:
class Client(Model):
name = CharField(max_length=150)
email = EmailField(max_length=100, verbose_name="E-mail")
I would want it to be output in the template like so (assume an instance with the given values):
Field Name Field Value
---------- -----------
Name Wayne Koorts
E-mail waynes#email.com
What I'm trying to achieve is being able to pass an instance of the model to a template and be able to iterate over it dynamically in the template, something like this:
<table>
{% for field in fields %}
<tr>
<td>{{ field.name }}</td>
<td>{{ field.value }}</td>
</tr>
{% endfor %}
</table>
Is there a neat, "Django-approved" way to do this? It seems like a very common task, and I will need to do it often for this particular project.
model._meta.get_all_field_names() will give you all the model's field names, then you can use model._meta.get_field() to work your way to the verbose name, and getattr(model_instance, 'field_name') to get the value from the model.
NOTE: model._meta.get_all_field_names() is deprecated in django 1.9. Instead use model._meta.get_fields() to get the model's fields and field.name to get each field name.
You can use Django's to-python queryset serializer.
Just put the following code in your view:
from django.core import serializers
data = serializers.serialize( "python", SomeModel.objects.all() )
And then in the template:
{% for instance in data %}
{% for field, value in instance.fields.items %}
{{ field }}: {{ value }}
{% endfor %}
{% endfor %}
Its great advantage is the fact that it handles relation fields.
For the subset of fields try:
data = serializers.serialize('python', SomeModel.objects.all(), fields=('name','size'))
Finally found a good solution to this on the dev mailing list:
In the view add:
from django.forms.models import model_to_dict
def show(request, object_id):
object = FooForm(data=model_to_dict(Foo.objects.get(pk=object_id)))
return render_to_response('foo/foo_detail.html', {'object': object})
in the template add:
{% for field in object %}
<li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
Here's another approach using a model method. This version resolves picklist/choice fields, skips empty fields, and lets you exclude specific fields.
def get_all_fields(self):
"""Returns a list of all field names on the instance."""
fields = []
for f in self._meta.fields:
fname = f.name
# resolve picklists/choices, with get_xyz_display() function
get_choice = 'get_'+fname+'_display'
if hasattr(self, get_choice):
value = getattr(self, get_choice)()
else:
try:
value = getattr(self, fname)
except AttributeError:
value = None
# only display fields with values and skip some fields entirely
if f.editable and value and f.name not in ('id', 'status', 'workshop', 'user', 'complete') :
fields.append(
{
'label':f.verbose_name,
'name':f.name,
'value':value,
}
)
return fields
Then in your template:
{% for f in app.get_all_fields %}
<dt>{{f.label|capfirst}}</dt>
<dd>
{{f.value|escape|urlize|linebreaks}}
</dd>
{% endfor %}
In light of Django 1.8's release (and the formalization of the Model _meta API, I figured I would update this with a more recent answer.
Assuming the same model:
class Client(Model):
name = CharField(max_length=150)
email = EmailField(max_length=100, verbose_name="E-mail")
Django <= 1.7
fields = [(f.verbose_name, f.name) for f in Client._meta.fields]
>>> fields
[(u'ID', u'id'), (u'name', u'name'), (u'E-mail', u'email')]
Django 1.8+ (formalized Model _meta API)
Changed in Django 1.8:
The Model _meta API has always existed as a Django internal, but wasn’t formally documented and supported. As part of the effort to make this API public, some of the already existing API entry points have changed slightly. A migration guide has been provided to assist in converting your code to use the new, official API.
In the below example, we will utilize the formalized method for retrieving all field instances of a model via Client._meta.get_fields():
fields = [(f.verbose_name, f.name) for f in Client._meta.get_fields()]
>>> fields
[(u'ID', u'id'), (u'name', u'name'), (u'E-mail', u'email')]
Actually, it has been brought to my attention that the above is slightly overboard for what was needed (I agree!). Simple is better than complex. I am leaving the above for reference. However, to display in the template, the best method would be to use a ModelForm and pass in an instance. You can iterate over the form (equivalent of iterating over each of the form's fields) and use the label attribute to retrieve the verbose_name of the model field, and use the value method to retrieve the value:
from django.forms import ModelForm
from django.shortcuts import get_object_or_404, render
from .models import Client
def my_view(request, pk):
instance = get_object_or_404(Client, pk=pk)
class ClientForm(ModelForm):
class Meta:
model = Client
fields = ('name', 'email')
form = ClientForm(instance=instance)
return render(
request,
template_name='template.html',
{'form': form}
)
Now, we render the fields in the template:
<table>
<thead>
{% for field in form %}
<th>{{ field.label }}</th>
{% endfor %}
</thead>
<tbody>
<tr>
{% for field in form %}
<td>{{ field.value|default_if_none:'' }}</td>
{% endfor %}
</tr>
</tbody>
</table>
Ok, I know this is a bit late, but since I stumbled upon this before finding the correct answer so might someone else.
From the django docs:
# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
[<Blog: Beatles Blog>]
# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]
You can use the values() method of a queryset, which returns a dictionary. Further, this method accepts a list of fields to subset on. The values() method will not work with get(), so you must use filter() (refer to the QuerySet API).
In view...
def show(request, object_id):
object = Foo.objects.filter(id=object_id).values()[0]
return render_to_response('detail.html', {'object': object})
In detail.html...
<ul>
{% for key, value in object.items %}
<li><b>{{ key }}:</b> {{ value }}</li>
{% endfor %}
</ul>
For a collection of instances returned by filter:
object = Foo.objects.filter(id=object_id).values() # no [0]
In detail.html...
{% for instance in object %}
<h1>{{ instance.id }}</h1>
<ul>
{% for key, value in instance.items %}
<li><b>{{ key }}:</b> {{ value }}</li>
{% endfor %}
</ul>
{% endfor %}
I used https://stackoverflow.com/a/3431104/2022534 but replaced Django's model_to_dict() with this to be able to handle ForeignKey:
def model_to_dict(instance):
data = {}
for field in instance._meta.fields:
data[field.name] = field.value_from_object(instance)
if isinstance(field, ForeignKey):
data[field.name] = field.rel.to.objects.get(pk=data[field.name])
return data
Please note that I have simplified it quite a bit by removing the parts of the original I didn't need. You might want to put those back.
You can have a form do the work for you.
def my_model_view(request, mymodel_id):
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
model = get_object_or_404(MyModel, pk=mymodel_id)
form = MyModelForm(instance=model)
return render(request, 'model.html', { 'form': form})
Then in the template:
<table>
{% for field in form %}
<tr>
<td>{{ field.name }}</td>
<td>{{ field.value }}</td>
</tr>
{% endfor %}
</table>
Below is mine, inspired by shacker's get_all_fields.
It gets a dict of one model instance, if encounter relation field, then asign the field value a dict recursively.
def to_dict(obj, exclude=[]):
"""生成一个 dict, 递归包含一个 model instance 数据.
"""
tree = {}
for field in obj._meta.fields + obj._meta.many_to_many:
if field.name in exclude or \
'%s.%s' % (type(obj).__name__, field.name) in exclude:
continue
try :
value = getattr(obj, field.name)
except obj.DoesNotExist:
value = None
if type(field) in [ForeignKey, OneToOneField]:
tree[field.name] = to_dict(value, exclude=exclude)
elif isinstance(field, ManyToManyField):
vs = []
for v in value.all():
vs.append(to_dict(v, exclude=exclude))
tree[field.name] = vs
elif isinstance(field, DateTimeField):
tree[field.name] = str(value)
elif isinstance(field, FileField):
tree[field.name] = {'url': value.url}
else:
tree[field.name] = value
return tree
This function is mainly used to dump a model instance to json data:
def to_json(self):
tree = to_dict(self, exclude=('id', 'User.password'))
return json.dumps(tree, ensure_ascii=False)
There should really be a built-in way to do this. I wrote this utility build_pretty_data_view that takes a model object and form instance (a form based on your model) and returns a SortedDict.
Benefits to this solution include:
It preserves order using Django's built-in SortedDict.
When tries to get the label/verbose_name, but falls back to the field name if one is not defined.
It will also optionally take an exclude() list of field names to exclude certain fields.
If your form class includes a Meta: exclude(), but you still want to return the values, then add those fields to the optional append() list.
To use this solution, first add this file/function somewhere, then import it into your views.py.
utils.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4
from django.utils.datastructures import SortedDict
def build_pretty_data_view(form_instance, model_object, exclude=(), append=()):
i=0
sd=SortedDict()
for j in append:
try:
sdvalue={'label':j.capitalize(),
'fieldvalue':model_object.__getattribute__(j)}
sd.insert(i, j, sdvalue)
i+=1
except(AttributeError):
pass
for k,v in form_instance.fields.items():
sdvalue={'label':"", 'fieldvalue':""}
if not exclude.__contains__(k):
if v.label is not None:
sdvalue = {'label':v.label,
'fieldvalue': model_object.__getattribute__(k)}
else:
sdvalue = {'label':k,
'fieldvalue': model_object.__getattribute__(k)}
sd.insert(i, k, sdvalue)
i+=1
return sd
So now in your views.py you might do something like this
from django.shortcuts import render_to_response
from django.template import RequestContext
from utils import build_pretty_data_view
from models import Blog
from forms import BlogForm
.
.
def my_view(request):
b=Blog.objects.get(pk=1)
bf=BlogForm(instance=b)
data=build_pretty_data_view(form_instance=bf, model_object=b,
exclude=('number_of_comments', 'number_of_likes'),
append=('user',))
return render_to_response('my-template.html',
RequestContext(request,
{'data':data,}))
Now in your my-template.html template you can iterate over the data like so...
{% for field,value in data.items %}
<p>{{ field }} : {{value.label}}: {{value.fieldvalue}}</p>
{% endfor %}
Good Luck. Hope this helps someone!
Instead of editing every model I would recommend to write one template tag which will return all field of any model given.
Every object has list of fields ._meta.fields.
Every field object has attribute name that will return it's name and method value_to_string() that supplied with your model object will return its value.
The rest is as simple as it's said in Django documentation.
Here is my example how this templatetag might look like:
from django.conf import settings
from django import template
if not getattr(settings, 'DEBUG', False):
raise template.TemplateSyntaxError('get_fields is available only when DEBUG = True')
register = template.Library()
class GetFieldsNode(template.Node):
def __init__(self, object, context_name=None):
self.object = template.Variable(object)
self.context_name = context_name
def render(self, context):
object = self.object.resolve(context)
fields = [(field.name, field.value_to_string(object)) for field in object._meta.fields]
if self.context_name:
context[self.context_name] = fields
return ''
else:
return fields
#register.tag
def get_fields(parser, token):
bits = token.split_contents()
if len(bits) == 4 and bits[2] == 'as':
return GetFieldsNode(bits[1], context_name=bits[3])
elif len(bits) == 2:
return GetFieldsNode(bits[1])
else:
raise template.TemplateSyntaxError("get_fields expects a syntax of "
"{% get_fields <object> [as <context_name>] %}")
Yeah it's not pretty, you'll have to make your own wrapper. Take a look at builtin databrowse app, which has all the functionality you need really.
This may be considered a hack but I've done this before using modelform_factory to turn a model instance into a form.
The Form class has a lot more information inside that's super easy to iterate over and it will serve the same purpose at the expense of slightly more overhead. If your set sizes are relatively small I think the performance impact would be negligible.
The one advantage besides convenience of course is that you can easily turn the table into an editable datagrid at a later date.
I've come up with the following method, which works for me because in every case the model will have a ModelForm associated with it.
def GetModelData(form, fields):
"""
Extract data from the bound form model instance and return a
dictionary that is easily usable in templates with the actual
field verbose name as the label, e.g.
model_data{"Address line 1": "32 Memory lane",
"Address line 2": "Brainville",
"Phone": "0212378492"}
This way, the template has an ordered list that can be easily
presented in tabular form.
"""
model_data = {}
for field in fields:
model_data[form[field].label] = eval("form.data.%s" % form[field].name)
return model_data
#login_required
def clients_view(request, client_id):
client = Client.objects.get(id=client_id)
form = AddClientForm(client)
fields = ("address1", "address2", "address3", "address4",
"phone", "fax", "mobile", "email")
model_data = GetModelData(form, fields)
template_vars = RequestContext(request,
{
"client": client,
"model_data": model_data
}
)
return render_to_response("clients-view.html", template_vars)
Here is an extract from the template I am using for this particular view:
<table class="client-view">
<tbody>
{% for field, value in model_data.items %}
<tr>
<td class="field-name">{{ field }}</td><td>{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
The nice thing about this method is that I can choose on a template-by-template basis the order in which I would like to display the field labels, using the tuple passed in to GetModelData and specifying the field names. This also allows me to exclude certain fields (e.g. a User foreign key) as only the field names passed in via the tuple are built into the final dictionary.
I'm not going to accept this as the answer because I'm sure someone can come up with something more "Djangonic" :-)
Update: I'm choosing this as the final answer because it is the simplest out of those given that does what I need. Thanks to everyone who contributed answers.
Django 1.7 solution for me:
There variables are exact to the question, but you should definitely be able to dissect this example
The key here is to pretty much use the .__dict__ of the model
views.py:
def display_specific(request, key):
context = {
'question_id':question_id,
'client':Client.objects.get(pk=key).__dict__,
}
return render(request, "general_household/view_specific.html", context)
template:
{% for field in gen_house %}
{% if field != '_state' %}
{{ gen_house|getattribute:field }}
{% endif %}
{% endfor %}
in the template I used a filter to access the field in the dict
filters.py:
#register.filter(name='getattribute')
def getattribute(value, arg):
if value is None or arg is None:
return ""
try:
return value[arg]
except KeyError:
return ""
except TypeError:
return ""
I'm using this, https://github.com/miracle2k/django-tables.
<table>
<tr>
{% for column in table.columns %}
<th>{{ column }}</th>
{% endfor %}
</tr>
{% for row in table.rows %}
<tr>
{% for value in row %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
This approach shows how to use a class like django's ModelForm and a template tag like {{ form.as_table }}, but have all the table look like data output, not a form.
The first step was to subclass django's TextInput widget:
from django import forms
from django.utils.safestring import mark_safe
from django.forms.util import flatatt
class PlainText(forms.TextInput):
def render(self, name, value, attrs=None):
if value is None:
value = ''
final_attrs = self.build_attrs(attrs)
return mark_safe(u'<p %s>%s</p>' % (flatatt(final_attrs),value))
Then I subclassed django's ModelForm to swap out the default widgets for readonly versions:
from django.forms import ModelForm
class ReadOnlyModelForm(ModelForm):
def __init__(self,*args,**kwrds):
super(ReadOnlyModelForm,self).__init__(*args,**kwrds)
for field in self.fields:
if isinstance(self.fields[field].widget,forms.TextInput) or \
isinstance(self.fields[field].widget,forms.Textarea):
self.fields[field].widget=PlainText()
elif isinstance(self.fields[field].widget,forms.CheckboxInput):
self.fields[field].widget.attrs['disabled']="disabled"
Those were the only widgets I needed. But it should not be difficult to extend this idea to other widgets.
Just an edit of #wonder
def to_dict(obj, exclude=[]):
tree = {}
for field in obj._meta.fields + obj._meta.many_to_many:
if field.name in exclude or \
'%s.%s' % (type(obj).__name__, field.name) in exclude:
continue
try :
value = getattr(obj, field.name)
except obj.DoesNotExist as e:
value = None
except ObjectDoesNotExist as e:
value = None
continue
if type(field) in [ForeignKey, OneToOneField]:
tree[field.name] = to_dict(value, exclude=exclude)
elif isinstance(field, ManyToManyField):
vs = []
for v in value.all():
vs.append(to_dict(v, exclude=exclude))
tree[field.name] = vs
else:
tree[field.name] = obj.serializable_value(field.name)
return tree
Let Django handle all the other fields other than the related fields. I feel that is more stable
Take a look at django-etc application. It has model_field_verbose_name template tag to get field verbose name from templates: http://django-etc.rtfd.org/en/latest/models.html#model-field-template-tags
I just tested something like this in shell and seems to do it's job:
my_object_mapped = {attr.name: str(getattr(my_object, attr.name)) for attr in MyModel._meta.fields}
Note that if you want str() representation for foreign objects you should define it in their str method. From that you have dict of values for object. Then you can render some kind of template or whatever.
Django >= 2.0
Add get_fields() to your models.py:
class Client(Model):
name = CharField(max_length=150)
email = EmailField(max_length=100, verbose_name="E-mail")
def get_fields(self):
return [(field.verbose_name, field.value_from_object(self)) for field in self.__class__._meta.fields]
Then call it as object.get_fields on your template.html:
<table>
{% for label, value in object.get_fields %}
<tr>
<td>{{ label }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
</table>
If you model name is Client and you are getting client object by id then proceed like this
client = Client.objects.get(id=id)
fields = Client._meta.get_fields()
for field in fields:
value = getattr(client, field.name)
print(field.name)
print(value)
<table border='1'>
<tr>
{% for mfild in fields%}
<td>{{mfild}}</td>
{% endfor%}
</tr>
{%for v in records%}
<tr>
<td>{{v.id}}</td>
<td>{{v.title}}</td>
<td class="">{{v.desc}}</td>
</tr>
{% endfor%}
</table>
enter code here

django: managing data to show on templates

Is there a way to somehow pre-select data in the queryset before it's gone to templates? There is this 'project' models that connects to 'project_phase' and there are 'project_phase_history' records that keep track of all status changes.
I am building reports on top of this data and show all them projects on one page and want to display only project_phase_history that were submitted for the last few days OR one most recent.
I tried doing this in the view somehow like this:
projects_to_report_on = project.objects.filter(Q(current_phase__phase__id__in=[1,2,3,4]) & Q(role_sponsor__id = sponsor_id))
projects_to_report_on.current_phase.project_phase_history_set = projects_to_report_on.current_phase.project_phase_history.filter(...)
but this doesn't help, really - all data seems to jump into the template.
I ended up passing the date into the template and generating report as something like this:
{% for s in p.current_phase.project_phase_history_set.all %}
{% if s.date_submitted >= status_start_date %}
<tr>
<td>{{ s.date_submitted }}</td>
<td>{{ s.date_end_fact|default_if_none:"-" }}</td>
</tr>
{% endif %}
{% endfor %}
but it don't not have the flexibility I am looking for.
class project(models.Model):
name = models.CharField(max_length=100, null=False, unique=True)
description = models.CharField(max_length=1024,null=True,blank=True)
current_phase = models.ForeignKey('project_phase', null=True, blank=True, related_name="current_phase")
class project_phase(models.Model):
phase = models.ForeignKey('phases')
project = models.ForeignKey('project')
is_finished = models.BooleanField(default=False)
class project_phase_history(models.Model):
project_phase = models.ForeignKey('project_phase')
date_start_plan = models.DateField(null=True, blank=False)
date_start_fact = models.DateField(null=True, blank=True)
Here's your problem:
projects_to_report_on.current_phase.project_phase_history_set = projects_to_report_on.current_phase.project_phase_history.filter(...)
You are setting the Manager of the reverse end of the relationship to a QuerySet. That does not work. Here's an example with blog and entry models:
>>> print(Entry.objects.all())
[<Entry: Today>, <Entry: Hello>]
>>> today = datetime.date.today()
>>> print(Entry.objects.filter(pub_date__lt=today).all())
[<Entry: Hello>]
>>> blog.entry_set = Entry.objects.filter(pub_date__lt=today())
>>> print(blog.entry_set.all())
[<Entry: Today>, <Entry: Hello>]
>>> blog.entry_set = Entry.objects.none()
>>> print(blog.entry_set.all())
[<Entry: Today>, <Entry: Hello>]
Because:
>>> blog.entry_set = Entry.objects.all()
>>> type(blog.entry_set)
<class 'django.db.models.fields.related.RelatedManager'>
This means blog.entry_set is still a Manager and calling all on a Manager will simply return all records. So don't do that. Pass the QuerySet to your template and use that:
history_set = projects_to_report_on.current_phase.project_phase_history_set.filter(...)
# add it to the context as 'history_set'
{% for s in history_set.all %}
Since you need to loop over both projects and the history of that project, I'd pass a dictionary to the template:
from django.utils.datastructures import SortedDict
# ...
project_map = SortedDict()
for project in projects_to_report_on:
project_map[project] = history_query_set_goes_here
# ...
return render(request, template, {'project_map': project_map})
And then in your template:
{% for project, history_set in project_map.items %}
<h2>{{ project }}</h2>
<ol>
{% for history in history_set.all %}
<li>{{ history }}</li>
{% endfor %}
</ol>
{% endfor %}
SortedDict is documented on the Django wiki. It maintains the order of insertion so if you retrieve the projects from the database in the correct order, they will display in the same order.

Iterating over model attributes when creating a template in Django

I'm using Django in Google App Engine. If I have the class
class Person():
first_name = StringProperty()
last_name = StringProperty()
and I have an instance where Person.first_name = Bob and Person.last_name = Vance, can I create a template that iterates over the Person attributes to produce:
<tr>
<td>First</td>
<td>Bob</td>
</tr>
<tr>
<td>Last</td>
<td>Vance</td>
</tr>
Perhaps more succinctly, is there a model.as_table() method that will print out the attributes of my instance, Bob Vance?
In template you cannot access __underscored__ attributes or functions.
I suggest instead you create a function in your model/class:
class Person(models.Model):
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
def attrs(self):
for attr, value in self.__dict__.iteritems():
yield attr, value
def sorted_attrs(self):
# Silly example of sorting
return [(key, self.__dict__[key]) for key in sorted(self.__dict__)]
In template it's just:
<tr>
{% for name, value in person.attrs %}
<td>{{name}}</td>
<td>{{value}}</td>
{% endfor %}
</tr>
Now this will give you "first_name" instead of "First", but you get the idea. You can extend the method to be a mixin, or be present in a parent-class etc..
Similarly you can use this if you have a few person objects you want to iterate over:
{% for person in persons %}
<tr>
{% for name, value in person.attrs %}
<td>{{name}}</td>
<td>{{value}}</td>
{% endfor %}
</tr>
{% endfor %}
Finally found a good solution to this on the dev mailing list (http://groups.google.com/group/django-developers/browse_thread/thread/44cd834438cfda77/557f53697658ab04?lnk=gst&q=template+model#557f53697658ab04):
In the view add:
from django.forms.models import model_to_dict
def show(request, object_id):
object = FooForm(data=model_to_dict(Foo.objects.get(pk=object_id)))
return render_to_response('foo/foo_detail.html', {'object': object})
in the template add:
{% for field in object %}
<li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
Change:
for attr, value in a.__dict__.iteritems():
to:
for attr, value in self.__dict__.iteritems():
def model_to_dict(instance, fields=None, exclude=None):
"""
Returns a dict containing the data in the ``instance`` where:
data = {'lable': 'verbose_name', 'name':name, 'value':value,}
Verbose_name is capitalized, order of fields is respected.
``fields`` is an optional list of field names. If provided, only the named
fields will be included in the returned dict.
``exclude`` is an optional list of field names. If provided, the named
fields will be excluded from the returned dict, even if they are listed in
the ``fields`` argument.
"""
data = []
if instance:
opts = instance._meta
for f in opts.fields:
if not f.editable:
continue
if fields and not f.name in fields:
continue
if exclude and f.name in exclude:
continue
value = f.value_from_object(instance)
# load the display name of choice fields
get_choice = 'get_'+f.name+'_display'
if hasattr(instance, get_choice):
value = getattr(instance, get_choice)()
# only display fields with values and skip the reset
if value:
if fields:
data.insert(fields.index(f.name), {'lable': f.verbose_name.capitalize(), 'name':f.name, 'value':value,})
else:
data.append({'lable': f.verbose_name.capitalize(), 'name':f.name, 'value':value,})
return data
TODO
Add support for #property decorated functions

Django templates: verbose version of a choice

I have a model:
from django.db import models
CHOICES = (
('s', 'Glorious spam'),
('e', 'Fabulous eggs'),
)
class MealOrder(models.Model):
meal = models.CharField(max_length=8, choices=CHOICES)
I have a form:
from django.forms import ModelForm
class MealOrderForm(ModelForm):
class Meta:
model = MealOrder
And I want to use formtools.preview. The default template prints the short version of the choice ('e' instead of 'Fabulous eggs'), becuase it uses
{% for field in form %}
<tr>
<th>{{ field.label }}:</th>
<td>{{ field.data }}</td>
</tr>
{% endfor %}.
I'd like a template as general as the mentioned, but printing 'Fabulous eggs' instead.
[as I had doubts where's the real question, I bolded it for all of us :)]
I know how to get the verbose version of a choice in a way that is itself ugly:
{{ form.meal.field.choices.1.1 }}
The real pain is I need to get the selected choice, and the only way coming to my mind is iterating through choices and checking {% ifequals currentChoice.0 choiceField.data %}, which is even uglier.
Can it be done easily? Or it needs some template-tag programming? Shouldn't that be available in django already?
In Django templates you can use the "get_FOO_display()" method, that will return the readable alias for the field, where 'FOO' is the name of the field.
Note: in case the standard FormPreview templates are not using it, then you can always provide your own templates for that form, which will contain something like {{ form.get_meal_display }}.
The best solution for your problem is to use helper functions.
If the choices are stored in the variable CHOICES and the model field storing the selected choice is 'choices' then you can directly use
{{ x.get_choices_display }}
in your template. Here, x is the model instance.
Hope it helps.
My apologies if this answer is redundant with any listed above, but it appears this one hasn't been offered yet, and it seems fairly clean. Here's how I've solved this:
from django.db import models
class Scoop(models.Model):
FLAVOR_CHOICES = [
('c', 'Chocolate'),
('v', 'Vanilla'),
]
flavor = models.CharField(choices=FLAVOR_CHOICES)
def flavor_verbose(self):
return dict(Scoop.FLAVOR_CHOCIES)[self.flavor]
My view passes a Scoop to the template (note: not Scoop.values()), and the template contains:
{{ scoop.flavor_verbose }}
Basing on Noah's reply, here's a version immune to fields without choices:
#annoyances/templatetags/data_verbose.py
from django import template
register = template.Library()
#register.filter
def data_verbose(boundField):
"""
Returns field's data or it's verbose version
for a field with choices defined.
Usage::
{% load data_verbose %}
{{form.some_field|data_verbose}}
"""
data = boundField.data
field = boundField.field
return hasattr(field, 'choices') and dict(field.choices).get(data,'') or data
I'm not sure wether it's ok to use a filter for such purpose. If anybody has a better solution, I'll be glad to see it :) Thank you Noah!
We can extend the filter solution by Noah to be more universal in dealing with data and field types:
<table>
{% for item in query %}
<tr>
{% for field in fields %}
<td>{{item|human_readable:field}}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
Here's the code:
#app_name/templatetags/custom_tags.py
def human_readable(value, arg):
if hasattr(value, 'get_' + str(arg) + '_display'):
return getattr(value, 'get_%s_display' % arg)()
elif hasattr(value, str(arg)):
if callable(getattr(value, str(arg))):
return getattr(value, arg)()
else:
return getattr(value, arg)
else:
try:
return value[arg]
except KeyError:
return settings.TEMPLATE_STRING_IF_INVALID
register.filter('human_readable', human_readable)
I don't think there's any built-in way to do that. A filter might do the trick, though:
#register.filter(name='display')
def display_value(bf):
"""Returns the display value of a BoundField"""
return dict(bf.field.choices).get(bf.data, '')
Then you can do:
{% for field in form %}
<tr>
<th>{{ field.label }}:</th>
<td>{{ field.data|display }}</td>
</tr>
{% endfor %}
You have Model.get_FOO_display() where FOO is the name of the field that has choices.
In your template do this :
{{ scoop.get_flavor_display }}
Add to your models.py one simple function:
def get_display(key, list):
d = dict(list)
if key in d:
return d[key]
return None
Now, you can get verbose value of choice fields like that:
class MealOrder(models.Model):
meal = models.CharField(max_length=8, choices=CHOICES)
def meal_verbose(self):
return get_display(self.meal, CHOICES)
Upd.: I'm not sure, is that solution “pythonic” and “django-way” enough or not, but it works. :)
The extended-extended version of Noah's and Ivan's solution. Also fixed Noah's solution for Django 3.1, as ModelChoiceIteratorValue is now unhashable.
#register.filter
def display_value(value: Any, arg: str = None) -> str:
"""Returns the display value of a BoundField or other form fields"""
if not arg: # attempt to auto-parse
# Returning regular field's value
if not hasattr(value.field, 'choices'): return value.value()
# Display select value for BoundField / Multiselect field
# This is used to get_..._display() for a read-only form-field
# which is not rendered as Input, but instead as text
return list(value.field.choices)[value.value()][1]
# usage: {{ field|display_value:<arg> }}
if hasattr(value, 'get_' + str(arg) + '_display'):
return getattr(value, 'get_%s_display' % arg)()
elif hasattr(value, str(arg)):
if callable(getattr(value, str(arg))):
return getattr(value, arg)()
return getattr(value, arg)
return value.get(arg) or ''
<select class="form-select">
{% for key, value in form.meal.field.choices %}
{% if form.meal.value == key %}
<option value="{{ form.key }}" selected>{{ value }}</option>
{% else %}
<option value="{{ key }}">{{ value }}</option>
{% endif %}
{% endfor %}
</select>
Hey what about that way?
in models.py
class MealOrder(models.Model):
CHOICES = (
('s', 'Glorious spam'),
('e', 'Fabulous eggs'),
)
meal = models.CharField(max_length=8, choices=CHOICES)
meal_value = models.CharField(max_length=1, blank=True, null=True,
editable=False)
def save(self, *args, **kwargs):
if self.meal == "s":
self.meal_value = "Glorious spam"
elif self.meal == "e":
self.meal_value = "Fabulous eggs"
super(MealOrder, self).save(*args, **kwargs)
in views.py
from .models import MealOrder
def meal_order(request):
meals = MealOrder.objects.all()
return render(request, "meals.html", {
"meals": meals,
})
in meals.html
{% for meal in meals %}
{{meal.meal_value }}
{%endfor%}