選択したインスタンスのフィールド値とその名前を表示する基本的なテンプレートを作成しようとしています。最初の列にフィールド名(フィールドで指定されている場合は特にverbose_name)、2番目の列にそのフィールドの値があるテーブル形式のインスタンスの値の標準出力と考えてください。
たとえば、次のモデル定義があるとします。
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 [email protected]
私が達成しようとしているのは、モデルのインスタンスをテンプレートに渡し、テンプレートで次のように動的に反復処理できることです:
<table>
{% for field in fields %}
<tr>
<td>{{ field.name }}</td>
<td>{{ field.value }}</td>
</tr>
{% endfor %}
</table>
これを行うためのきちんとした「ジャンゴ承認」方法はありますか?これは非常に一般的なタスクのようで、この特定のプロジェクトでは頻繁に行う必要があります。
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
を使用して各フィールド名を取得します。
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'))
最後に 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 %}
Django 1.8のリリース(および Model _meta API の形式化)に照らして、私はこれをより新しい答えで更新すると考えました。
同じモデルを想定:
class Client(Model):
name = CharField(max_length=150)
email = EmailField(max_length=100, verbose_name="E-mail")
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内部として存在していましたが、正式に文書化およびサポートされていませんでした。この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>
モデルメソッドを使用した別のアプローチを次に示します。このバージョンでは、選択リスト/選択フィールドが解決され、空のフィールドがスキップされ、特定のフィールドを除外できます。
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 %}
わかりました、これは少し遅いことを知っていますが、正しい答えを見つける前につまずいたので、誰かがそうするかもしれません。
Django docs から:
# 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.'}]
フォームに作業を任せることができます。
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>
辞書を返すqueryset
のvalues()
メソッドを使用できます。さらに、このメソッドは、サブセット化するフィールドのリストを受け入れます。 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]
Indetail.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 %}
これを行うには、組み込みの方法が本当に必要です。モデルユーティリティとフォームインスタンス(モデルに基づいたフォーム)を取り、SortedDict
を返すこのユーティリティbuild_pretty_data_view
を作成しました。
このソリューションの利点は次のとおりです。
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 %}
がんばろう。これが誰かを助けることを願っています!
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
オリジナルの不要な部分を削除することで、かなり簡略化したことに注意してください。それらを元に戻したいかもしれません。
以下はshacker'sget_all_fields
に触発された私のものです。 1つのモデルインスタンスの辞書を取得し、リレーションフィールドに遭遇した場合、フィールド値に辞書に再帰的に署名します。
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)
すべてのモデルを編集する代わりに1つのテンプレートタグを記述することをお勧めします。これは任意のモデルのすべてのフィールドを返します。
すべてのオブジェクトには、フィールド._meta.fields
のリストがあります。
すべてのフィールドオブジェクトには名前を返す属性name
があり、モデルobject
で提供されるメソッドvalue_to_string()
はその値を返します。
残りは Django documentation で述べられているように簡単です。
このテンプレートタグがどのように見えるかは私の例です:
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>] %}")
これはハックと見なされるかもしれませんが、modelform_factoryを使用してモデルインスタンスをフォームに変換する前にこれを実行しました。
Formクラスには、繰り返し処理するのが非常に簡単な多くの情報が含まれており、オーバーヘッドがわずかに増えますが、同じ目的に役立ちます。セットのサイズが比較的小さい場合、パフォーマンスへの影響は無視できると思います。
もちろん、利便性に加えて1つの利点は、後日、テーブルを簡単に編集可能なデータグリッドに変換できることです。
次のメソッドを思い付きました。これは、どの場合でもモデルに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に渡されたTupleを使用して、フィールド名を指定します。これにより、タプルを介して渡されたフィールド名のみが最終的な辞書に組み込まれるため、特定のフィールド(ユーザー外部キーなど)を除外することもできます。
誰かがもっと「ジャンゴニック」なものを思いつくことができると確信しているので、これを答えとして受け入れるつもりはありません:-)
更新:私はこれが最終的な答えとして選んでいます。なぜなら、それは、私が必要とするものを与えられたものの中で最も簡単だからです。回答を提供してくれたすべての人に感謝します。
ええ、それはきれいではありません、あなたはあなた自身のラッパーを作る必要があります。 builtin databrowse appを見てください。本当に必要なすべての機能を備えています。
私のための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のフィールドにアクセスしました
filters.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 ""
これを使用しています 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>
このアプローチは、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"
これらは私が必要とする唯一のウィジェットでした。ただし、このアイデアを他のウィジェットに拡張することは難しくありません。
@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に関連フィールド以外の他のすべてのフィールドを処理させます。もっと安定していると思う
Django-etc applicationをご覧ください。テンプレートからフィールドの詳細名を取得するmodel_field_verbose_name
テンプレートタグがあります。 http://Django-etc.rtfd.org/en/latest/models.html#model-field-template-tags