问题:遍历模板中的模型实例字段名称和值
我正在尝试创建一个基本模板以显示所选实例的字段值及其名称。可以将其视为表格式的该实例的值的标准输出,第一列具有字段名称(如果在字段上指定,则为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>
有没有一种经过Django批准的简洁方法?这似乎是一项非常普通的任务,对于这个特定项目,我将需要经常执行。
回答 0
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
获取每个字段名称。
回答 1
您可以使用Django的to-python queryset序列化程序。
只需将以下代码放入您的视图中:
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'))
回答 2
终于在开发邮件列表中找到了一个很好的解决方案:
在视图中添加:
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 %}
回答 3
鉴于Django 1.8的发布(以及Model _meta API的形式化,我认为我将使用更新的答案对此进行更新。
假设模型相同:
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+(正式的Model _meta API)
在Django 1.8中进行了更改:
Model
_meta
API一直是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>
回答 4
这是使用模型方法的另一种方法。此版本可解析选择列表/选择字段,跳过空字段,并允许您排除特定字段。
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 %}
回答 5
好的,我知道这有点晚了,但是由于我在找到正确答案之前偶然发现了这个,其他人也可能会发现。
# 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.'}]
回答 6
您可以使用的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})
在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
我使用了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
请注意,我已经删除了不需要的部分,从而大大简化了它。您可能要放回去。
回答 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})
然后在模板中:
<table>
{% for field in form %}
<tr>
<td>{{ field.name }}</td>
<td>{{ field.value }}</td>
</tr>
{% endfor %}
</table>
回答 9
确实应该有一种内置方法来执行此操作。我编写了此实用程序build_pretty_data_view
,该实用程序接受模型对象和表单实例(基于您的模型的表单)并返回SortedDict
。
该解决方案的优势包括:
- 它使用Django的内置命令保留命令
SortedDict
。 - 尝试获取标签/详细名称时,但如果未定义则回退到字段名称。
- 也可以选择
exclude()
字段名称列表以排除某些字段。 - 如果您的表单类包含一个
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
因此,现在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,}))
现在,您my-template.html
可以在模板中像这样遍历数据…
{% for field,value in data.items %}
<p>{{ field }} : {{value.label}}: {{value.fieldvalue}}</p>
{% endfor %}
祝好运。希望这对某人有帮助!
回答 10
下面是我的,受到黑客的 启发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)
回答 11
我建议不要编写每个模型,而是建议编写一个模板标签,该标签将返回给定模型的所有字段。
每个对象都有字段列表._meta.fields
。
每个字段对象都具有name
返回其名称的属性,而value_to_string()
模型随附的方法object
将返回其值。
其余的就像Django文档中所说的那样简单。
这是我的示例,此templatetag可能如下所示:
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>] %}")
回答 12
是的,这并不漂亮,您必须自己制作包装纸。看一看内置的数据浏览应用程序,它具有您真正需要的所有功能。
回答 13
这可能被认为是黑客,但是在使用modelform_factory将模型实例转换为表单之前,我已经做到了。
Form类内部有很多信息,这些信息非常容易迭代,并且可以达到相同的目的,但开销会稍有增加。如果您的设备尺寸相对较小,我认为对性能的影响可以忽略不计。
当然,除了方便之外,一个优点是您可以在以后轻松地将表变成可编辑的数据网格。
回答 14
我想出了以下方法,该方法对我有用,因为在每种情况下,模型都会有一个与之关联的ModelForm。
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)
这是我用于此特定视图的模板的摘录:
<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>
关于此方法的好处是,我可以使用传递给GetModelData的元组并指定字段名称,在逐个模板的基础上选择要显示字段标签的顺序。这也使我可以排除某些字段(例如,用户外键),因为只有通过元组传递的字段名称才内置在最终字典中。
我不会接受这个答案,因为我敢肯定有人可以提出更多“ Djangonic”的概念:-)
更新:我选择此作为最终答案,因为这是我所需要的,是最简单的答案。感谢所有贡献答案的人。
回答 15
我的Django 1.7解决方案:
这里有确切的变量,但是您肯定可以剖析此示例
这里的关键是几乎使用.__dict__
模型
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)
模板:
{% for field in gen_house %}
{% if field != '_state' %}
{{ gen_house|getattribute:field }}
{% endif %}
{% endfor %}
在模板中,我使用了过滤器来访问dict
filter.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 ""
回答 16
我正在使用它,https://github.com/miracle2k/django-tables。
<table>
<tr>
{% for column in table.columns %}
<th><a href="?sort={{ column.name_toggled }}">{{ column }}</a></th>
{% endfor %}
</tr>
{% for row in table.rows %}
<tr>
{% for value in row %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
回答 17
这种方法显示了如何使用django的ModelForm之类的类和{{form.as_table}}之类的模板标签,但是如何使所有表看起来像数据输出,而不是表单。
第一步是继承django的TextInput小部件:
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))
然后,我将django的ModelForm子类化,以将默认小部件换成只读版本:
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"
这些是我唯一需要的小部件。但是将这个想法扩展到其他小部件并不难。
回答 18
只需编辑@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
让Django处理除相关字段以外的所有其他字段。我觉得比较稳定
回答 19
看一下django-etc应用程序。它具有model_field_verbose_name
用于从模板获取字段详细名称的模板标记:http : //django-etc.rtfd.org/en/latest/models.html#model-field-template-tags
回答 20
我只是在shell中测试了类似的东西,似乎可以做到这一点:
my_object_mapped = {attr.name: str(getattr(my_object, attr.name)) for attr in MyModel._meta.fields}
请注意,如果要用str()表示异物,则应在其str方法中对其进行定义。由此,您可以确定对象的值。然后,您可以渲染某种模板或其他任何模板。
回答 21
Django> = 2.0
添加get_fields()
到您的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]
然后object.get_fields
按您的名称template.html
:
<table>
{% for label, value in object.get_fields %}
<tr>
<td>{{ label }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
</table>
回答 22
<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