Iterate over model instance field names and values in template

Solution 1:

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.

Solution 2:

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'))

Solution 3:

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 %}

Solution 4:

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 %}

Solution 5:

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>