Iterar sobre nombres y valores de campo de instancia de modelo en plantilla

183

Estoy tratando de crear una plantilla básica para mostrar los valores de campo de la instancia seleccionada, junto con sus nombres. Piense en ello como solo una salida 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 de esta manera (suponga una instancia con los valores dados):

Field Name      Field Value
----------      -----------
Name            Wayne Koorts
E-mail          waynes@email.com

Lo que intento lograr es poder pasar una instancia del modelo a una plantilla y poder iterar sobre ella 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 forma ordenada y "aprobada por Django" de hacer esto? Parece una tarea muy común, y tendré que hacerlo a menudo para este proyecto en particular.

Wayne Koorts
fuente

Respuestas:

171

model._meta.get_all_field_names()le dará todos los nombres de campo del modelo, luego puede usar model._meta.get_field()para llegar al nombre detallado y getattr(model_instance, 'field_name')obtener el valor del 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.namepara obtener el nombre de cada campo.

Ignacio Vazquez-Abrams
fuente
2
Esto todavía es muy manual, y tendría que construir algún tipo de metaobjeto en la vista que luego paso a la plantilla, que es más un truco de lo que me gustaría. Seguramente debe haber una manera más ordenada?
Wayne Koorts el
2
Podría encapsular todo esto en una clase, al igual que ModelForm.
Ignacio Vazquez-Abrams
18
No creo que pueda llamar a ningún método _ en las plantillas.
Issac Kelly
2
Esto funciona, pero no debería depender de una API privada (ya que tiene el prefijo "_") para lograrlo. El problema de confiar en una API privada es que no se garantiza que los métodos privados funcionen de una versión a otra.
Devy
1
Creo que este método no debería preferirse ya que no deberíamos acceder a los atributos que comienzan con un guión bajo de las plantillas
GP92
72

Puede usar el serializador de conjuntos de consultas a 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 intente:

data = serializers.serialize('python', SomeModel.objects.all(), fields=('name','size'))
Pawel Furmaniak
fuente
Eso es genial, pero con este método, ¿cómo limitaría uno los resultados a solo ciertos campos?
Herman Schaaf
2
Esta debería ser la respuesta definitiva, maneja claves externas y no llamadas privadas API. Gran respuesta, gracias.
Yunti
3
No es necesario usar serializar. Puede usar el método values ​​() de un conjunto de consultas , que devuelve un diccionario. Además, este método acepta una lista de campos para subconjuntos. Ver enlace . Mira mi respuesta completa.
user3062149
¿Podemos actualizar esto para enviar solo los campos. En lugar de tener que procesarlo en un bucle? No quiero exponer los nombres de modelo / tabla
Loser Coder
¿Este método permite verbose_namepasar el campo?
alias51
71

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

En la vista agregue:

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

{% for field in object %}
    <li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
Roger
fuente
1
Buena solución, pero no muy genérica, ya que no devuelve un model_to_dict para los campos ForeignKey, sino un resultado unicode , por lo que no puede serializar fácilmente objetos complejos en dict
Vestel
22
Peligroso para anular el objeto, debe usar algún otro nombre variable.
Emil Stenström
¡Gracias! Reemplacé model_to_dict () de Django para poder manejar ForeignKey. Vea mi respuesta por separado (eliminé mi comentario anterior ya que los comentarios no admiten el formato de código. Lo siento, no lo sabía).
Magnus Gustavsson
2
Asumiendo aquí que FooFormes un ModelForm, ¿no sería más fácil hacer simplemente FooForm(instance=Foo.objects.get(pk=object_id))):?
beruic
¿Alguna idea de cómo mostraría solo campos editables con este método?
alias51
22

A la luz del lanzamiento de Django 1.8 (y la formalización de la API Modelo _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+ (Modelo formalizado _meta API)

Cambiado en Django 1.8:

El modelo _metaAPI siempre ha existido como un Django interno, pero no fue documentado y respaldado formalmente. Como parte del esfuerzo para hacer pública esta API, algunos de los puntos de entrada de API ya existentes han cambiado ligeramente. Se ha proporcionado 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, se me ha señalado que lo anterior está un poco por la borda para lo que se necesitaba (¡estoy de acuerdo!). Simple es mejor que complejo. Estoy dejando lo anterior como 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 label para recuperar el verbose_name del campo modelo, y usar el método value 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, representamos 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>
 
Michael B
fuente
2
Sería genial si ajustara su respuesta para mostrar la forma "> 1.8" de colocar los campos del modelo en una plantilla. Por el momento, su respuesta no responde directamente a la pregunta; muestra cómo obtener los campos del modelo en el shell.
Escher
@Escher - ¡Se actualizó la respuesta! Gracias por la sugerencia. ¡Avíseme si me perdí algo / si arruiné algo!
Michael B
Votado Edité para incluir la impresión de los valores, así como los nombres de los campos. Mira lo que piensas.
Escher
¿Dónde imprime los valores? ¿Solo lo veo imprimiendo el nombre y verbose_name?
Dr. Ernie
@MichaelB Hmm. No he podido hacer que "field.value" funcione; los campos parecen ser campos de la base de datos, no los datos de la columna real. Tuve que usar un filtro que llamaba getattr (objeto, nombre). ¿Qué versión de Django funciona para ti?
Dr. Ernie
19

Aquí hay otro enfoque utilizando un método modelo. Esta versión resuelve los campos de lista de selección / elección, omite 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 %}
shacker
fuente
3
¿Por qué necesitas el except User.DoesNotExist:?
Sevenearths
Me inclinaría a usar AttributeError en lugar de User.DoesNotExist: no puedo ver por qué arrojaría User.DoesNotExist.
askvictor
Además, podría ser mejor usar self._meta.get_fields () ya que esto está oficialmente expuesto en django 1.8+. Sin embargo, terminas con relaciones en el código, que tendrías que filtrar marcando f.is_relation
askvictor
Edité la respuesta para usar AttributeError en lugar de User.DoesNotExist (que era un residuo de mi implementación original). Gracias. Espero _meta.get_fields()hasta que pueda probarlo.
Shacker
13

Ok, sé que es un poco tarde, pero como me topé con esto antes de encontrar la respuesta correcta, alguien más podría hacerlo.

De los documentos de 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.'}]
olofom
fuente
Me gusta esta respuesta Si su consulta devuelve más de un registro y solo desea el último, haga lo siguiente. 1. Asegúrese de que dispone ordering = ['-id']en class Meta:su objeto en models.py. 2. luego useBlog.objects.filter(name__startswith='Beatles').values()[0]
Sevenearths
Idea inteligente. Pero si ya tiene un modelobjeto, volvería a presionar la base de datos solo para obtener los campos. ¿Alguna forma de evitar eso?
Viernes
@ user1763732 solo verifique la documentación del QuerySet: docs.djangoproject.com/en/dev/ref/models/querysets
olofom
9

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

En 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 detalle.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 %}
usuario3062149
fuente
Esto es asombroso, gracias! Tengo una pregunta, si me pueden ayudar; Estoy poniendo todos los datos de objetos en a table, así que necesito cada keys en a th. ¿Cómo hago eso sin bucles? Simplemente tome cualquier instancia de objeto e iterar a través de él para keys? Actualmente estoy pasando model_to_dict(Model())por separado para el th, pero creo que es una instanciación de objeto innecesaria.
Oxwivi
Fantástica respuesta. Personalmente, usé esto tanto en una vista de lista como en una vista detallada. La implementación de la vista de lista es en gran medida obvia, pero con la vista de detalle anulo get_objecten la vista de detalle (destrozada debido a la limitación del código en línea en los comentarios, y no creo que esto sea suficiente para su propia respuesta teniendo en cuenta cuán saturado es este hilo): def get_object(self, **kwargs): obj = super().get_object(**kwargs) obj = obj.__class__.objects.filter(pk=obj.pk).values()[0] return obj
sdconrox
¿Cómo agregaría obj.get_absolute_urla esta lista sin duplicar las filas?
alias51
8

Solía https://stackoverflow.com/a/3431104/2022534 pero sustituye model_to_dict de Django () con este para ser capaz de 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 eliminando las partes del original que no necesitaba. Es posible que desee volver a ponerlos.

Magnus Gustavsson
fuente
8

Puede hacer que un formulario 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>
ja
fuente
3
Este método (empleado dentro de a DetailView) funciona bien para mí. Sin embargo, es posible que desee utilizar en field.labellugar de field.name.
David Cain
7

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

Los beneficios de esta solución incluyen:

  • Conserva el orden utilizando el Django incorporado SortedDict.
  • Cuando intenta obtener la etiqueta / verbose_name, pero recurre al nombre del campo si no está definido.
  • También opcionalmente tomará una exclude()lista 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 append()lista opcional .

Para usar esta solución, primero agregue este archivo / función en algún lugar, luego impórtelo a 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 tu views.pypodrías 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 su my-template.htmlplantilla puede iterar sobre los datos así ...

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

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

{% endfor %}

Buena suerte. ¡Espero que esto ayude a alguien!

Alan Viars
fuente
7

Debajo está el mío, inspirado en el shacker's get_all_fields . Obtiene un dict de una instancia de modelo, si encuentra un campo de relación, entonces asigne el valor del campo un 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 utiliza principalmente para volcar una instancia de modelo en datos json:

def to_json(self):
    tree = to_dict(self, exclude=('id', 'User.password'))
    return json.dumps(tree, ensure_ascii=False)
preguntarse
fuente
¡Buen trabajo! Se sugiere agregar opciones apoyan ... hasattr elif (campo, 'opciones'): Árbol [field.name] = dict (field.choices) .get (valor, valor)
Oden
5

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

Aquí está mi ejemplo de cómo podría ser esta etiqueta de 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>] %}")
seler
fuente
4

Sí, no es bonito, tendrás que hacer tu propio envoltorio. Eche un vistazo a la aplicación de búsqueda de datos integrada , que tiene todas las funcionalidades que realmente necesita.

Dmitry Shevchenko
fuente
Iba a decir ... databrowse hace exactamente eso, aunque encontré que es una aplicación completamente inútil.
mpen
4

Esto puede considerarse un hack, 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 dentro que es muy fácil de recorrer y servirá para el mismo propósito a expensas de un poco más de gastos generales. Si los tamaños de su 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.

Xealot
fuente
4

Se me ocurrió 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 plantilla por plantilla el orden en que me gustaría mostrar las etiquetas de campo, utilizando la tupla que se pasa a GetModelData y especificando los nombres de campo. 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 se integran en el diccionario final.

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

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

Wayne Koorts
fuente
3

Solución Django 1.7 para mí:

Hay variables que son exactas a la pregunta, pero definitivamente deberías poder diseccionar este ejemplo

La clave aquí es usar .__dict__el 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
filtros.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 ""
Jeremy
fuente
2

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>
Renyi
fuente
2

Este enfoque muestra cómo usar una clase como ModelForm de django y una etiqueta de plantilla como {{form.as_table}}, pero 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 subclasifiqué 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 fueron los únicos widgets que necesitaba. Pero no debería ser difícil extender esta idea a otros widgets.

Arrojar
fuente
1

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 otros campos que no sean los campos relacionados. Siento que es mas estable

Shivam Goyal
fuente
0

Acabo de probar algo como esto en shell y parece hacer su trabajo:

my_object_mapped = {attr.name: str(getattr(my_object, attr.name)) for attr in MyModel._meta.fields}

Tenga en cuenta que si desea una representación str () para objetos extraños, debe definirla en su método str . A partir de eso, tiene un dict de valores para el objeto. Entonces puedes renderizar algún tipo de plantilla o lo que sea.

Adán
fuente
0

Django> = 2.0

Añadir get_fields()a su 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]

Entonces llámalo como object.get_fieldsen tu template.html:

<table>
    {% for label, value in object.get_fields %}
        <tr>
            <td>{{ label }}</td>
            <td>{{ value }}</td>
        </tr>
    {% endfor %}
</table>
JV conseil
fuente
-1

<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

Raj Kushwha R
fuente
1
Hola y bienvenidos a SO. Por favor, no publique el código solo las respuestas. Además, esta pregunta ya tiene una respuesta aceptada, el formato de su código no es correcto, está utilizando atributos html obsoletos y lo más importante: no está explicando cómo su código proporciona una solución mejor que la aceptada.
Frieder