迭代模板中的模型实例字段名称和值

Way*_*rts 175 python django django-templates

我正在尝试创建一个基本模板来显示所选实例的字段值及其名称.可以将其视为表格格式中该实例值的标准输出,其中第一列中的字段名称(特别是如果在字段上指定了verbose_name)和第二列中该字段的值.

例如,假设我们有以下模型定义:

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

我希望它在模板中输出如此(假设具有给定值的实例):

Field Name      Field Value
----------      -----------
Name            Wayne Koorts
E-mail          waynes@email.com
Run Code Online (Sandbox Code Playgroud)

我想要实现的是能够将模型的实例传递给模板,并能够在模板中动态迭代它,如下所示:

<table>
    {% for field in fields %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>
Run Code Online (Sandbox Code Playgroud)

是否有一个整洁,"Django批准"的方式来做到这一点?这似乎是一项非常常见的任务,我需要经常为这个特定的项目做这件事.

Ign*_*ams 166

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获取每个字段名称.

  • 我不相信你可以在模板中调用任何_方法. (15认同)
  • 这仍然是非常手动的,我将不得不在视图中构建某种元对象,然后我将其传递给模板,这比我想要的更多.当然必须有一个更简洁的方式? (2认同)
  • 您可以将所有这些封装在一个类中,就像ModelForm一样. (2认同)
  • 这有效,但您不应该依赖私有API(因为它以"_"作为前缀)来实现它.依赖私有API的问题是不保证私有方法在版本之间起作用. (2认同)

Paw*_*iak 69

您可以使用Django的to-python queryset序列化程序.

只需在您的视图中输入以下代码:

from django.core import serializers
data = serializers.serialize( "python", SomeModel.objects.all() )
Run Code Online (Sandbox Code Playgroud)

然后在模板中:

{% for instance in data %}
    {% for field, value in instance.fields.items %}
        {{ field }}: {{ value }}
    {% endfor %}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

它的巨大优势在于它处理关系字段.

对于字段子集,请尝试:

data = serializers.serialize('python', SomeModel.objects.all(), fields=('name','size'))
Run Code Online (Sandbox Code Playgroud)

  • 这应该是明确的答案,处理外键并且没有私有 api 调用。很好的答案,谢谢。 (3认同)
  • 没有必要使用序列化。您可以使用查询集**的 **values() 方法,它返回一个字典。此外,此方法接受要子集的字段列表。请参阅 [链接](https://docs.djangoproject.com/en/dev/ref/models/querysets/#values)。查看我的完整答案。 (3认同)

Rog*_*ger 66

最后在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})
Run Code Online (Sandbox Code Playgroud)

在模板中添加:

{% for field in object %}
    <li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

  • 危险覆盖对象,您应该使用其他一些可变名称. (20认同)
  • 假设这里`FooForm`是一个`ModelForm`,那就不容易做:`FooForm(instance = Foo.objects.get(pk = object_id)))`? (2认同)

Mic*_*l B 22

根据Django 1.8的发布(以及Model _meta API的形式化,我想我会用更新的答案来更新它.

假设相同的型号:

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

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')]
Run Code Online (Sandbox Code Playgroud)

Django 1.8+(形式化模型_meta API)

更改了Django 1.8:

Model _metaAPI一直作为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')]
Run Code Online (Sandbox Code Playgroud)

实际上,我已经注意到上面的内容略微超出需要的范围(我同意!).简单比复杂更好.我将离开以上内容以供参考.但是,要在模板中显示,最好的方法是使用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}
    )
Run Code Online (Sandbox Code Playgroud)

现在,我们渲染模板中的字段:

<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>
Run Code Online (Sandbox Code Playgroud)

  • 如果您调整答案以显示"> 1.8"方式将模型字段放入模板中,那将会很棒.目前你的答案没有直接回答这个问题; 它显示了如何在shell中获取模型的字段. (2认同)

sha*_*ker 17

这是使用模型方法的另一种方法.此版本可解析选项列表/选项字段,跳过空白字段,并允许您排除特定字段.

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
Run Code Online (Sandbox Code Playgroud)

然后在你的模板中:

{% for f in app.get_all_fields %}
  <dt>{{f.label|capfirst}}</dt>
    <dd>
      {{f.value|escape|urlize|linebreaks}}
    </dd>
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

  • 你为什么需要`除了User.DoesNotExist:`? (3认同)

olo*_*fom 13

好吧,我知道这有点晚了,但是因为我在找到正确的答案之前偶然发现了这个,所以可能是其他人.

来自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.'}]
Run Code Online (Sandbox Code Playgroud)


j-a*_*j-a 8

您可以让表单为您完成工作.

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})
Run Code Online (Sandbox Code Playgroud)

然后在模板中:

<table>
    {% for field in form %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>
Run Code Online (Sandbox Code Playgroud)

  • 这个方法(在[`DetailView`](https://docs.djangoproject.com/en/dev/ref/class-based-views/generic-display/#detailview)中使用)对我很有用.但是,您可能希望使用`field.label`而不是`field.name`. (2认同)

use*_*149 8

您可以使用a的values()方法queryset,它返回一个字典.此外,此方法接受要子集的字段列表.该values()方法无法使用get(),因此必须使用filter()(请参阅QuerySet API).

view......

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

detail.html......

<ul>
   {% for key, value in object.items %}
        <li><b>{{ key }}:</b> {{ value }}</li>
   {% endfor %}
</ul>
Run Code Online (Sandbox Code Playgroud)

对于filter返回的实例集合:

   object = Foo.objects.filter(id=object_id).values() # no [0]
Run Code Online (Sandbox Code Playgroud)

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 %}
Run Code Online (Sandbox Code Playgroud)


Ala*_*ars 7

应该有一种内置的方法来做到这一点.我写了这个实用程序build_pretty_data_view,它接受一个模型对象和表单实例(一个基于你的模型的表单)并返回一个SortedDict.

此解决方案的好处包括:

  • 它使用Django的内置保留顺序SortedDict.
  • 尝试获取标签/ verbose_name时,如果未定义,则返回到字段名称.
  • 它还可以选择使用exclude()字段名称列表来排除某些字段.
  • 如果表单类包含a Meta: exclude(),但仍希望返回值,则将这些字段添加到可选append()列表中.

要使用此解决方案,首先在某处添加此文件/函数,然后将其导入到您的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
Run Code Online (Sandbox Code Playgroud)

所以现在views.py你可能会做这样的事情

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,}))
Run Code Online (Sandbox Code Playgroud)

现在在您的my-template.html模板中,您可以迭代数据,如此...

{% for field,value in data.items %}

    <p>{{ field }} : {{value.label}}: {{value.fieldvalue}}</p>

{% endfor %}
Run Code Online (Sandbox Code Playgroud)

祝好运.希望这有助于某人!


won*_*der 7

以下是我的,灵感来自shacker's 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
Run Code Online (Sandbox Code Playgroud)

该函数主要用于将模型实例转储为json数据:

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


Mag*_*son 7

我使用/sf/answers/240177311/但用这个替换了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
Run Code Online (Sandbox Code Playgroud)

请注意,我已经通过删除我不需要的原件的部分简化了它.你可能想把它们放回去.


sel*_*ler 5

我建议不要编辑每个模型,而是编写一个模板标签,它将返回给定的任何模型的所有字段.
每个对象都有字段列表._meta.fields.
每个字段对象都具有name将返回其名称的属性,并且value_to_string()随模型提供的方法object将返回其值.
其余的就像在Django文档中说的那样简单.

以下是此模板标签的示例:

    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>] %}")
Run Code Online (Sandbox Code Playgroud)