Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django templates: loop through and print all available properties of an object?

I have a database object called manor_stats, with around 30 fields. For most rows, most of these fields will be null.

In my template, I'd like to loop through all the fields in the row, and print info for only the fields that aren't null.

For example, there's a field called "name": I'd like to print <li>Name: {{ manor_stats.name }}</li> in the template ONLY for those objects where the field isn't null. Ideally I'd like to pull in "Name: " from somewhere automatically too, rather than specifying it.

I know I could use {% if manor_stats.name %} to check whether each field is null, but I don't want to do that 30 times for all the fields.

Here's what I have in views.py:

manor_stats = Manors.objects.get(idx=id)
return render_to_response('place.html', { 'place' : place, 'manor_stats' : manor_stats }, context_instance = RequestContext(request))

And then in place.html, I'd like to have something that works approximately like this (pseudocode, with ??? indicating the bits that I don't know how to do):

{% if manor_stats %} 
<ul>
 {% for manor_stats.property??? in manor_stats %} 
  {% if manor_stats.property %} 
   <li>{{ manor_stats.property.field_name??? }} {{ manor_stats.property.value??? }}</li>
  {% endif %}
 {% endfor %
{% endif %}

Hope that makes sense...

like image 444
AP257 Avatar asked Feb 07 '10 17:02

AP257


2 Answers

You could add a method to your Manors model that will return all the field values, from there you can just loop over these values in your template checking to see if the value isn't null.

-- models.py

class Manors(models.Model)
  #field declarations

  def get_fields(self):
    return [(field.name, field.value_to_string(self)) for field in Manors._meta.fields]

-- manor_detail.html

{% for name, value in manor_stats.get_fields %}
  {% if value %}
    {{ name }} => {{ value }}
  {% endif %}
{% endfor %}
like image 178
buckley Avatar answered Sep 22 '22 13:09

buckley


The following approach only requires defining a single custom template filter (see the docs) - it's DRY'er than having to write or copy a method into every model you need this functionality for.

-- my_app/templatetags/my_tags.py

from django import template

register = template.Library()

@register.filter
def get_fields(obj):
    return [(field.name, field.value_to_string(obj)) for field in obj._meta.fields]

You'll need to restart your server to get the new tags registered and available in your templates.

-- my_app/templates/my_app/my_template.html

{% load my_tags %}
<ul>
{% for key, val in object|get_fields %}
  <li>{{ key }}: {{ val }}</li>
{% endfor %}
</ul>

Tested in Django 1.11.

NB: Since ._meta is a private attribute it's functionality might well change in the future.

Thanks to the other answers on this post that took me most of the way, particularly W. Perrin.

like image 25
Rob Simpson Avatar answered Sep 25 '22 13:09

Rob Simpson