Перебирать имена и значения полей экземпляра модели в шаблоне - PullRequest
168 голосов
/ 31 января 2010

Я пытаюсь создать базовый шаблон для отображения значений полей выбранного экземпляра вместе с их именами. Думайте об этом как о стандартном выводе значений этого экземпляра в формате таблицы с именем поля (особенно verbose_name, если оно указано в поле) в первом столбце и значением этого поля во втором столбце.

Например, допустим, у нас есть следующее определение модели:

class Client(Model):
    name = CharField(max_length=150)
    email = EmailField(max_length=100, verbose_name="E-mail")

Я бы хотел, чтобы он выводился в шаблоне следующим образом (предположим, экземпляр с заданными значениями):

Field Name      Field Value
----------      -----------
Name            Wayne Koorts
E-mail          waynes@email.com

То, чего я пытаюсь достичь, - это возможность передать экземпляр модели в шаблон и иметь возможность динамически перебирать его в шаблоне, что-то вроде этого:

<table>
    {% for field in fields %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>

Есть ли аккуратный "одобренный Джанго" способ сделать это? Кажется, это очень распространенная задача, и мне придется часто ее выполнять для этого конкретного проекта.

Ответы [ 20 ]

163 голосов
/ 31 января 2010

model._meta.get_all_field_names() даст вам все имена полей модели, затем вы можете использовать model._meta.get_field(), чтобы перейти к подробному имени, и getattr(model_instance, 'field_name'), чтобы получить значение из модели.

ПРИМЕЧАНИЕ: model._meta.get_all_field_names() устарела в django 1.9. Вместо этого используйте model._meta.get_fields(), чтобы получить поля модели, и field.name, чтобы получить имя каждого поля.

66 голосов
/ 22 февраля 2010

Вы можете использовать сериализатор наборов запросов Django to-python .

Просто введите следующий код:

from django.core import serializers
data = serializers.serialize( "python", SomeModel.objects.all() )

А потом в шаблоне:

{% for instance in data %}
    {% for field, value in instance.fields.items %}
        {{ field }}: {{ value }}
    {% endfor %}
{% endfor %}

Его большое преимущество заключается в том, что он обрабатывает поля отношений.

Для подмножества полей попробуйте:

data = serializers.serialize('python', SomeModel.objects.all(), fields=('name','size'))
63 голосов
/ 07 августа 2010

Наконец-то нашел хорошее решение для этого в списке рассылки dev :

В представлении добавить:

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

в шаблон добавить:

{% for field in object %}
    <li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
22 голосов
/ 16 мая 2015

В свете выпуска Django 1.8 (и формализации API Model _meta , я подумал, что обновлю это более поздним ответом.

Предполагая, что та же модель:

class Client(Model):
    name = CharField(max_length=150)
    email = EmailField(max_length=100, verbose_name="E-mail")

Джанго <= 1,7 </h2> 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+ (формализованная модель _meta API)

Изменено в Django 1.8:

Модель API _meta всегда существовала как внутренняя часть Django, но официально не документировалась и не поддерживалась. В рамках усилий по обеспечению доступности этого API некоторые из уже существующих точек входа API немного изменились. Было предоставлено руководство по миграции, которое поможет преобразовать ваш код в новый официальный API.

В приведенном ниже примере мы будем использовать формализованный метод для извлечения всех экземпляров полей модели через 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')]

На самом деле, до меня дошло, что вышесказанное немного за борт для того, что было нужно (я согласен!). Простое лучше, чем сложное. Я оставляю выше для справки. Однако для отображения в шаблоне наилучшим способом будет использование ModelForm и передача экземпляра. Вы можете выполнить итерацию по форме (эквивалент итерации по каждому из полей формы) и использовать атрибут label для получения verbose_name поля модели, а также использовать метод 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}
    )

Теперь мы визуализируем поля в шаблоне:

<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>
17 голосов
/ 09 февраля 2010

Вот еще один подход с использованием метода модели. Эта версия разрешает поля выбора / выбора, пропускает пустые поля и позволяет исключать определенные поля.

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

Тогда в вашем шаблоне:

{% for f in app.get_all_fields %}
  <dt>{{f.label|capfirst}}</dt>
    <dd>
      {{f.value|escape|urlize|linebreaks}}
    </dd>
{% endfor %}
13 голосов
/ 30 марта 2012

Хорошо, я знаю, что это немного поздно, но, поскольку я наткнулся на это, прежде чем нашел правильный ответ, может и кто-то другой.

Из документов django :

# 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.'}]
8 голосов
/ 03 февраля 2013

У вас может быть форма, которая сделает всю работу за вас.

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

Тогда в шаблоне:

<table>
    {% for field in form %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>
8 голосов
/ 27 апреля 2016

Вы можете использовать values() метод queryset, который возвращает словарь. Кроме того, этот метод принимает список полей для поднабора. Метод values() не будет работать с get(), поэтому необходимо использовать filter() (см. API QuerySet ).

In view ...

def show(request, object_id):
   object = Foo.objects.filter(id=object_id).values()[0]
   return render_to_response('detail.html', {'object': object})

В detail.html ...

<ul>
   {% for key, value in object.items %}
        <li><b>{{ key }}:</b> {{ value }}</li>
   {% endfor %}
</ul>

Для коллекции экземпляров , возвращаемых фильтром:

   object = Foo.objects.filter(id=object_id).values() # no [0]

В 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 %}
7 голосов
/ 31 января 2013

Я использовал https://stackoverflow.com/a/3431104/2022534, но заменил Django model_to_dict () этим, чтобы иметь возможность обрабатывать 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

Пожалуйста, обратите внимание, что я немного упростил это, удалив части оригинала, которые мне не нужны. Возможно, вы захотите вернуть их обратно.

7 голосов
/ 26 апреля 2012

Ниже мое, вдохновленное Шакером get_all_fields. Он получает диктат одного экземпляра модели, если встречается поле отношения, а затем рекурсивно присваивается значение поля дикту.

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

Эта функция в основном используется для выгрузки экземпляра модели в данные json:

def to_json(self):
    tree = to_dict(self, exclude=('id', 'User.password'))
    return json.dumps(tree, ensure_ascii=False)
...