问题
I need to display a table from my database with Django. The obvious way is to manually type in the table headings and loop through query results of model.objects.all()
. However, being quite lazy, I want to do this automatically, i.e. load all fields from model through introspection to display as column headings and load all field values to display as rows. This approach can also save me some time later because I don't have to update my template code when my model changes. I got it to work but there are two problems:
- I can't find away to load the AutoField field (id) value so I have to slice off the ID column.
- The code looks quite messy especially with the use of random template tags.
Here is my code. Please note that the code works fine so I'll skip all the imports as they are correct:
views.py I use serializers to serialize the data, a trick I read somewhere on stackoverflow
def index(request):
fields = MyModel._meta.fields
data = serializers.serialize("python", MyModel.objects.all())
context_instance = RequestContext(request, {
'data' : data,
'fields' : fields,
})
return TemplateResponse(request, 'index.html', context_instance)
template/index.html: note that I have to slice off the ID column by slicing off the first element of the fields list
{% with fields|slice:"1:" as cached_fields %}
<table>
<thead>
<tr>
{% for field in cached_fields %}
<th>{% get_verbose_name field %}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for instance in data %}
<tr>
{% for field in cached_fields %}
<td>{% get_value_from_key instance.fields field %}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endwith %}
templatetags/extra_tags.py
# tag to get field's verbose name in template
@register.simple_tag
def get_verbose_name(object):
return object.verbose_name
# tag to get the value of a field by name in template
@register.simple_tag
def get_value_from_key(object, key):
# is it necessary to check isinstance(object, dict) here?
return object[key.name]
回答1:
serializers.serialize("json
or xml", Model.objects.all())
formats return the id field; probably not what you are looking for but some of the jQuery grid plugins can further automate the task.
回答2:
Have a look at this should automate a lot of the this for you from your models. http://django-tables2.readthedocs.org/en/latest/
回答3:
Yay! I found a work around thanks to Видул Петров's suggestion about serializing the data to json, which allows me to load the pk field as well. It still feels too manual and hackish (and verbose) but I think I'm getting close. Please help me refactor this code further.
views.py Serialize data into a list of JSON objects and parse it into a list of dictionaries to pass it to template
from django.utils import simplejson as json
def index(request):
fields = MyModel._meta.fields
data = json.loads(serializers.serialize("json", MyModel.objects.all()))
def parse_data(data):
result = []
# flatten the dictionary
def flatten_dict(d):
"""
Because the only nested dict here is the fields, let's just
remove the 'fields' suffix so that the fields can be loaded in
template by name
"""
def items():
for key, value in d.items():
if isinstance(value, dict):
for subkey, subvalue in flatten_dict(value).items():
yield subkey, subvalue
else:
yield key, value
return dict(items())
for d in data:
# change the 'pk' key name into its actual name in the database
d[Employee._meta.pk.name] = d.pop('pk')
# append the flattend dict of each object's field-value to the result
result.append(flatten_dict(d))
return result
context_instance = RequestContext(request, {
'data' : parse_data(data),
'fields' : fields,
})
return TemplateResponse(request, 'index.html', context_instance)
template/index.html The template is much nicer now
<table>
<thead>
<tr>
{% for field in cached_fields %}
<th>{% get_verbose_name field %}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for d in data %}
<tr>
{% for field in fields %}
<td>{% get_value_from_key d field %}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
来源:https://stackoverflow.com/questions/14615847/display-table-of-objects-django