tutorial template examples bootstrap python django django-templates

python - template - django views



Iteramos los nombres y valores de los campos de instancias del modelo en la plantilla (20)

A continuación está el mío, inspirado en get_all_fields de get_all_fields . Obtiene un dict de una instancia de modelo, si el campo de relación de encuentro, luego asigna el valor de campo a dict recursivamente.

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

Esta función se usa principalmente para volcar una instancia modelo a datos json:

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

Intento crear una plantilla básica para mostrar los valores de campo de la instancia seleccionada, junto con sus nombres. Piense en ello como solo un resultado estándar de los valores de esa instancia en formato de tabla, con el nombre del campo (verbose_name específicamente si se especifica en el campo) en la primera columna y el valor de ese campo en la segunda columna.

Por ejemplo, supongamos que tenemos la siguiente definición de modelo:

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

Me gustaría que se muestre en la plantilla como tal (suponga una instancia con los valores dados):

Field Name Field Value ---------- ----------- Name Wayne Koorts E-mail [email protected]

Lo que estoy tratando de lograr es poder pasar una instancia del modelo a una plantilla y ser capaz de iterar dinámicamente en la plantilla, algo como esto:

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

¿Hay una manera clara y "aprobada por Django" de hacer esto? Parece una tarea muy común, y tendré que hacerlo a menudo para este proyecto en particular.


A la luz del lanzamiento de Django 1.8 (y la formalización de la API de Model _meta , pensé que actualizaría esto con una respuesta más reciente.

Asumiendo el mismo modelo:

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+ (API de modelo _meta formalizada)

Cambiado en Django 1.8:

La API Model _meta siempre existió como interna de Django, pero no fue formalmente documentada y admitida. Como parte del esfuerzo para hacer que esta API sea pública, algunos de los puntos de entrada API ya existentes han cambiado ligeramente. Se proporcionó una guía de migración para ayudarlo a convertir su código para usar la nueva API oficial.

En el siguiente ejemplo, utilizaremos el método formalizado para recuperar todas las instancias de campo de un modelo a través de 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'')]

En realidad, me ha llamado la atención que lo anterior está ligeramente por fuera de lo que se necesitaba (¡estoy de acuerdo!). Simple es mejor que complejo. Estoy dejando lo de arriba para referencia. Sin embargo, para mostrar en la plantilla, el mejor método sería usar un ModelForm y pasar en una instancia. Puede iterar sobre el formulario (equivalente a iterar sobre cada uno de los campos del formulario) y usar el atributo de etiqueta para recuperar el nombre detallado del campo modelo, y usar el método de valor para recuperar el valor:

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

Ahora, renderizamos los campos en la plantilla:

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


Aquí hay otro enfoque usando un método modelo. Esta versión resuelve los campos de lista de selección / elección, salta los campos vacíos y le permite excluir campos específicos.

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

Luego en tu plantilla:

{% for f in app.get_all_fields %} <dt>{{f.label|capfirst}}</dt> <dd> {{f.value|escape|urlize|linebreaks}} </dd> {% endfor %}



En lugar de editar cada modelo, recomendaría escribir una etiqueta de plantilla que devolverá todos los campos de cualquier modelo .
Cada objeto tiene una lista de campos ._meta.fields .
Cada objeto de campo tiene un name atributo que devolverá su nombre y el método value_to_string() proporcionado con el object modelo devolverá su valor.
El resto es tan simple como se dice en la documentación de Django .

Aquí está mi ejemplo de cómo se vería esta plantilla:

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>] %}")


Este enfoque muestra cómo usar una clase como ModelForm de django y una etiqueta de plantilla como {{form.as_table}}, pero hacer que toda la tabla parezca salida de datos, no un formulario.

El primer paso fue subclasificar el widget TextInput de django:

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

Luego clasifiqué el ModelForm de django para cambiar los widgets predeterminados por versiones de solo lectura:

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"

Esos eran los únicos artilugios que necesitaba. Pero no debería ser difícil extender esta idea a otros widgets.


Esto puede considerarse un truco, pero lo he hecho antes de usar modelform_factory para convertir una instancia de modelo en un formulario.

La clase Form tiene mucha más información interna que es súper fácil de iterar y servirá para el mismo propósito a expensas de un poco más de sobrecarga. Si los tamaños de conjunto son relativamente pequeños, creo que el impacto en el rendimiento sería insignificante.

La única ventaja, además de la conveniencia, es que puede convertir fácilmente la tabla en una cuadrícula de datos editable en una fecha posterior.


Estoy usando esto, 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>


Finalmente encontré una buena solución para esto en la lista de correo de dev :

En la vista agregar:

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

en la plantilla agrega:

{% for field in object %} <li><b>{{ field.label }}:</b> {{ field.data }}</li> {% endfor %}


He creado el siguiente método, que funciona para mí porque en todos los casos el modelo tendrá un ModelForm asociado.

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)

Aquí hay un extracto de la plantilla que estoy usando para esta vista en particular:

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

Lo bueno de este método es que puedo elegir en una plantilla, por plantilla, el orden en el que me gustaría mostrar las etiquetas de campo, usando la tupla pasada a GetModelData y especificando los nombres de los campos. Esto también me permite excluir ciertos campos (por ejemplo, una clave externa de usuario) ya que solo los nombres de campo pasados ​​a través de la tupla están integrados en el diccionario final.

No voy a aceptar esto como la respuesta porque estoy seguro de que alguien puede proponer algo más "Djangonic" :-)

Actualización: estoy eligiendo esto como la respuesta final porque es el más simple de los que hace lo que necesito. Gracias a todos los que contribuyeron con las respuestas.


La solución Django 1.7 para mí:

Las variables son exactas para la pregunta, pero definitivamente debes ser capaz de diseccionar este ejemplo

La clave aquí es usar más o menos el .__dict__ del modelo
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)

plantilla :

{% for field in gen_house %} {% if field != ''_state'' %} {{ gen_house|getattribute:field }} {% endif %} {% endfor %}

en la plantilla utilicé un filtro para acceder al campo en el 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 ""


Ok, sé que esto es un poco tarde, pero desde que me encontré con esto antes de encontrar la respuesta correcta, podría ser otra persona.

De los documentos 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.''}]


Puede hacer que una forma haga el trabajo por usted.

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

Luego en la plantilla:

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


Puede usar el método values() de un queryset , que devuelve un diccionario. Además, este método acepta una lista de campos para subconjuntos. El método values() no funcionará con get() , por lo que debe usar filter() (consulte la link ).

A la view ...

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

En detail.html ...

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

Para una colección de instancias devueltas por filtro:

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

En 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 %}


Puede usar el serializador de conjunto de consultas de python de Django.

Simplemente ponga el siguiente código en su vista:

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

Y luego en la plantilla:

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

Su gran ventaja es el hecho de que maneja campos de relación.

Para el subconjunto de campos pruebe:

data = serializers.serialize(''python'', SomeModel.objects.all(), fields=(''name'',''size''))


Realmente debería haber una forma integrada de hacer esto. Escribí esta utilidad build_pretty_data_view que toma un objeto modelo y una instancia de formulario (un formulario basado en su modelo) y devuelve un SortedDict .

Los beneficios de esta solución incluyen:

  • Conserva el orden usando SortedDict incorporado de SortedDict .
  • Cuando intenta obtener la etiqueta / verbose_name, pero vuelve al nombre del campo si no está definido.
  • También tomará opcionalmente una lista exclude() de nombres de campo para excluir ciertos campos.
  • Si su clase de formulario incluye un Meta: exclude() , pero aún desea devolver los valores, agregue esos campos a la lista opcional append() .

Para utilizar esta solución, primero agregue este archivo / función en algún lugar, luego impórtelo en su 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

Así que ahora en su views.py podría hacer algo como esto

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

Ahora en tu my-template.html puedes repetir los datos como ...

{% for field,value in data.items %} <p>{{ field }} : {{value.label}}: {{value.fieldvalue}}</p> {% endfor %}

Buena suerte. ¡Espero que esto ayude a alguien!


Sí, no es bonito, tendrás que hacer tu propia envoltura. Eche un vistazo a la aplicación de databrowse , que tiene toda la funcionalidad que realmente necesita.


Solo una edición de @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

Deje que Django maneje todos los demás campos además de los campos relacionados. Siento que es más estable


Usé https://.com/a/3431104/2022534 pero sustituí el model_to_dict () de Django con esto para poder manejar 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

Tenga en cuenta que lo he simplificado bastante al quitar las partes del original que no necesitaba. Es posible que desee ponerlos de vuelta.


model._meta.get_all_field_names() le dará todos los nombres de campo del modelo, luego puede usar model._meta.get_field() para trabajar con el nombre detallado, y getattr(model_instance, ''field_name'') para obtener el valor de el modelo.

NOTA: model._meta.get_all_field_names() está en desuso en django 1.9. En su lugar, use model._meta.get_fields() para obtener los campos del modelo y field.name para obtener cada nombre de campo.