Scorrere i nomi e i valori dei campi dell'istanza del modello nel modello


183

Sto cercando di creare un modello di base per visualizzare i valori dei campi dell'istanza selezionata, insieme ai loro nomi. Pensalo come un semplice output dei valori di quell'istanza in formato tabella, con il nome del campo (verbose_name specificamente se specificato nel campo) nella prima colonna e il valore di quel campo nella seconda colonna.

Ad esempio, supponiamo di avere la seguente definizione del modello:

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

Vorrei che fosse emesso nel modello in questo modo (supponiamo un'istanza con i valori indicati):

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

Quello che sto cercando di ottenere è riuscire a passare un'istanza del modello a un modello ed essere in grado di scorrere su di esso dinamicamente nel modello, qualcosa del genere:

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

Esiste un modo pulito e "approvato da Django" per farlo? Sembra un compito molto comune e dovrò farlo spesso per questo particolare progetto.

Risposte:


171

model._meta.get_all_field_names()ti darà tutti i nomi dei campi del modello, quindi puoi usarlo model._meta.get_field()per arrivare al nome dettagliato e getattr(model_instance, 'field_name')ottenere il valore dal modello.

NOTA: model._meta.get_all_field_names()è obsoleto in Django 1.9. Utilizzare invece model._meta.get_fields()per ottenere i campi del modello e field.nameottenere il nome di ciascun campo.


2
Questo è ancora molto manuale e dovrei creare una sorta di meta oggetto nella vista che poi passo nel modello, che è più un hack di quanto vorrei. Sicuramente ci deve essere un modo più ordinato?
— Wayne Koorts,

2
Potresti incapsulare tutto ciò in una classe, proprio come ModelForm.
— Ignacio Vazquez-Abrams,

18
Non credo che tu possa chiamare alcun metodo _ nei template.
— Issac Kelly,

2
Funziona ma non dovresti dipendere da un'API privata (in quanto ha il prefisso "_") per raggiungerla. Il problema di affidarsi all'API privata è che i metodi privati ​​non sono garantiti per funzionare da una versione all'altra.
— Devy,

1
Penso che questo metodo non dovrebbe essere preferito in quanto non dovremmo accedere agli attributi a partire dal trattino basso dai modelli
— GP92

72

Puoi usare il serializzatore di queryset to-python di Django .

Basta mettere il seguente codice nella tua vista:

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

E poi nel modello:

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

Il suo grande vantaggio è il fatto che gestisce i campi di relazione.

Per il sottoinsieme di campi provare:

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

È fantastico, ma con questo metodo, come si limiterebbero i risultati a determinati campi?
— Herman Schaaf,

2
Questa dovrebbe essere la risposta definitiva, gestisce le chiavi esterne e nessuna chiamata API privata. Ottima risposta, grazie.
— Yunti

3
Non è necessario utilizzare la serializzazione. È possibile utilizzare il metodo value () di un queryset , che restituisce un dizionario. Inoltre, questo metodo accetta un elenco di campi su cui eseguire il sottoinsieme. Vedi link . Vedi la mia risposta completa.
— user3062149

Possiamo aggiornare questo per inviare solo i .fields invece di doverlo elaborare in un ciclo? Non voglio esporre i nomi di modello / tabella
— Loser Coder,

Questo metodo consente il passaggio verbose_namedel campo?
— alias51

71

Finalmente ho trovato una buona soluzione a questo sulla mailing list degli sviluppatori :

Nella vista aggiungi:

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

nel modello aggiungere:

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

1
Buona soluzione, ma non molto generica in quanto non restituisce un model_to_dict per i campi ForeignKey, ma un risultato unicode , quindi non è possibile serializzare facilmente oggetti complessi in dict
— Vestel

22
Pericoloso sovrascrivere un oggetto, dovresti usare qualche altro nome variabile.
— Emil Stenström,

Grazie! Ho sostituito model_to_dict () di Django per essere in grado di gestire ForeignKey. Si prega di vedere la mia risposta separata (ho eliminato il mio commento precedente poiché i commenti non supportano la formattazione del codice. Mi dispiace, non lo sapevo.)
— Magnus Gustavsson

2
Supponendo che qui FooFormè un ModelForm, Non sarebbe più facile solo per fare: FooForm(instance=Foo.objects.get(pk=object_id)))?
— beruic,

Hai idea di come visualizzeresti solo campi modificabili con questo metodo?
— alias51

22

Alla luce del rilascio di Django 1.8 (e della formalizzazione dell'API Model _meta , ho pensato di aggiornarlo con una risposta più recente.

Supponendo lo stesso modello:

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 formalizzata Model _meta)

Modificato in Django 1.8:

L' _metaAPI modello è sempre esistita come interno di Django, ma non è stata formalmente documentata e supportata. Come parte dello sforzo di rendere pubblica questa API, alcuni dei punti di ingresso API già esistenti sono leggermente cambiati. È stata fornita una guida alla migrazione per aiutare a convertire il codice per utilizzare la nuova API ufficiale.

Nell'esempio seguente, utilizzeremo il metodo formalizzato per recuperare tutte le istanze di campo di un modello tramite 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')]

In realtà, è stato portato alla mia attenzione che quanto sopra è leggermente esagerato per ciò che era necessario (sono d'accordo!). Semplice è meglio di complesso. Lascio quanto sopra per riferimento. Tuttavia, per visualizzare nel modello, il metodo migliore sarebbe utilizzare un ModelForm e passare un'istanza. È possibile iterare sul modulo (equivalente di iterare su ciascuno dei campi del modulo) e utilizzare l'attributo label per recuperare il verbose_name del campo modello e utilizzare il metodo value per recuperare il valore:

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

Ora, rendiamo i campi nel modello:

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

2
Sarebbe bello se modifichi la tua risposta per mostrare il modo "> 1.8" per inserire i campi del modello in un modello. Al momento la tua risposta non risponde direttamente alla domanda; mostra come ottenere i campi del modello nella shell.
— Escher,

@Escher - Aggiornata la risposta! Grazie per il suggerimento Fammi sapere se ho perso qualcosa / incasinato qualcosa!
— Michael B

Upvoted. Ho modificato per includere la stampa dei valori e dei nomi dei campi. Vedi cosa ne pensi.
— Escher,

Dove stampi i valori? Lo vedo solo stampando il nome e verbose_name?
— Dr. Ernie

@MichaelB Hmm. Non sono stato in grado di far funzionare "field.value"; i campi sembrano essere campi del database, non i dati effettivi della colonna. Ho dovuto usare un filtro chiamato getattr (oggetto, nome). Quale versione di Django funziona per te?
— Dr. Ernie

19

Ecco un altro approccio che utilizza un metodo modello. Questa versione risolve i campi elenco / scelta, salta i campi vuoti e consente di escludere campi specifici.

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

Quindi nel tuo modello:

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

3
perché hai bisogno del except User.DoesNotExist:?
— Sevenearths,

Sarei propenso a usare AttributeError invece di User.DoesNotExist: non vedo perché genererebbe User.DoesNotExist.
— askvictor

Inoltre, potrebbe essere meglio usare self._meta.get_fields () poiché questo è ufficialmente esposto in django 1.8+. Tuttavia, poi finisci con le relazioni nel codice, che dovresti filtrare controllando f.is_relation
— askvictor

Ho modificato la risposta per utilizzare AttributeError anziché User.DoesNotExist (che era rimasto dalla mia implementazione originale). Grazie. Sto resistendo _meta.get_fields()finché non riesco a provarlo.
— shacker,

13

Ok, lo so che è un po 'tardi, ma dal momento che mi sono imbattuto in questo prima di trovare la risposta corretta, anche qualcun altro potrebbe farlo.

Dai documenti di 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.'}]

Mi piace questa risposta. Se la tua query restituisce più di un record e desideri solo l'ultimo, procedi come segue. 1. Assicurarsi di avere ordering = ['-id']in class Meta:del vostro oggetto in models.py. 2. quindi utilizzareBlog.objects.filter(name__startswith='Beatles').values()[0]
— Sevenearths,

Idea intelligente. Ma se hai già un modeloggetto, allora colpiresti di nuovo il database solo per ottenere i campi. Qualche modo per aggirare questo?
— dal

@ user1763732 basta controllare le documentazioni per il QuerySet: docs.djangoproject.com/en/dev/ref/models/querysets
— olofom

9

È possibile utilizzare il values()metodo di a queryset, che restituisce un dizionario. Inoltre, questo metodo accetta un elenco di campi su cui eseguire il sottoinsieme. Il values()metodo non funzionerà get(), quindi è necessario utilizzare filter()(fare riferimento all'API QuerySet ).

In view...

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

In detail.html...

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

Per una raccolta di istanze restituite dal filtro:

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

In dettaglio.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 %}

Questo è fantastico, grazie! Ho una domanda, se mi potresti aiutare; Sto inserendo tutti i dati degli oggetti in a table, quindi ho bisogno di ognuno di essi keyin a th. Come posso farlo senza loop? Basta prendere qualsiasi istanza di oggetto e scorrere attraverso di essa per keys? Attualmente sto passando separatamente model_to_dict(Model())per il th, ma penso che sia un'istanza di oggetto non necessaria.
— Oxwivi,

Risposta fantastica. Personalmente, l'ho usato sia in una vista elenco che in una vista dettagliata. La vista elenco è in gran parte ovvia da implementare, ma con la vista di dettaglio ho la precedenza get_objectsulla vista di dettaglio (alterata a causa della limitazione del codice in linea sui commenti, e non mi sembra che questo sia sufficiente per la sua risposta considerando quanto sia saturo questo thread): def get_object(self, **kwargs): obj = super().get_object(**kwargs) obj = obj.__class__.objects.filter(pk=obj.pk).values()[0] return obj
— sdconrox,

come aggiungeresti obj.get_absolute_urla questo elenco senza duplicare le righe?
— alias51

8

Ho usato https://stackoverflow.com/a/3431104/2022534 ma ho sostituito il model_to_dict () di Django con questo per essere in grado di gestire 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

Si noti che l'ho semplificato un po 'rimuovendo le parti dell'originale che non mi servivano. Potresti voler rimetterli.


8

Puoi avere un modulo per fare il lavoro per te.

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

Quindi nel modello:

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

3
Questo metodo (impiegato all'interno di a DetailView) funziona bene per me. Tuttavia, potresti voler utilizzare field.labelinvece di field.name.
— David Cain,

7

Dovrebbe esserci davvero un modo integrato per farlo. Ho scritto questa utility build_pretty_data_viewche accetta un oggetto modello e un'istanza del modulo (un modulo basato sul modello) e restituisce a SortedDict.

I vantaggi di questa soluzione includono:

  • Conserva l'ordine usando il Django integrato SortedDict.
  • Quando tenta di ottenere l'etichetta / verbose_name, ma torna al nome del campo se non è definito.
  • Opzionalmente prenderà anche un exclude()elenco di nomi di campi per escludere determinati campi.
  • Se la classe del modulo include a Meta: exclude(), ma si desidera comunque restituire i valori, aggiungere tali campi append()all'elenco facoltativo .

Per utilizzare questa soluzione, prima aggiungi questo file / funzione da qualche parte, quindi importalo nel tuo 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

Quindi ora nel tuo views.pypotresti fare qualcosa del genere

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

Ora nel tuo my-template.htmlmodello puoi scorrere i dati in questo modo ...

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

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

{% endfor %}

In bocca al lupo. Spero che questo aiuti qualcuno!


7

Di seguito è il mio, ispirato a Shacker's get_all_fields . Ottiene un dict di un'istanza del modello, se incontra un campo relazione, quindi assegna ricorsivamente il valore del campo a un dict.

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

Questa funzione viene utilizzata principalmente per scaricare un'istanza del modello in dati json:

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

Ottimo lavoro! Suggerisci l'aggiunta del supporto delle scelte ... elif hasattr (field, 'options'): tree [field.name] = dict (field.choices) .get (value, value)
— oden

5

Invece di modificare ogni modello, consiglierei di scrivere un tag modello che restituirà tutti i campi di qualsiasi modello dato.
Ogni oggetto ha un elenco di campi ._meta.fields.
Ogni oggetto campo ha un attributo nameche restituirà il suo nome e il metodo value_to_string()fornito con il modello objectrestituirà il suo valore.
Il resto è semplice come si dice nella documentazione di Django .

Ecco il mio esempio di come potrebbe apparire questo 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>] %}")

4

Sì, non è carino, dovrai creare il tuo involucro. Dai un'occhiata all'app databrowse integrata , che ha tutte le funzionalità di cui hai davvero bisogno.


Stavo per dire ... databrowse fa proprio questo, anche se l'ho trovato un'app completamente inutile.
— apre il

4

Questo può essere considerato un trucco, ma l'ho già fatto prima di utilizzare modelform_factory per trasformare un'istanza del modello in un modulo.

La classe Form ha molte più informazioni al suo interno che è super facile da ripetere e servirà allo stesso scopo a spese di un po 'più di spese generali. Se le dimensioni del tuo set sono relativamente ridotte, penso che l'impatto sulle prestazioni sarebbe trascurabile.

L'unico vantaggio oltre alla praticità ovviamente è che puoi facilmente trasformare la tabella in un datagrid modificabile in un secondo momento.


4

Ho escogitato il seguente metodo, che funziona per me perché in ogni caso al modello sarà associato un 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)

Ecco un estratto dal modello che sto usando per questa vista particolare:

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

La cosa bella di questo metodo è che posso scegliere su base modello per modello l'ordine in cui vorrei visualizzare le etichette dei campi, usando la tupla passata a GetModelData e specificando i nomi dei campi. Ciò mi consente anche di escludere determinati campi (ad es. Una chiave esterna dell'utente) poiché solo i nomi dei campi passati tramite la tupla sono integrati nel dizionario finale.

Non accetterò questa come risposta perché sono sicuro che qualcuno può inventare qualcosa di più "Djangonic" :-)

Aggiornamento: sto scegliendo questa come risposta finale perché è la più semplice tra quelle date che fa quello di cui ho bisogno. Grazie a tutti coloro che hanno contribuito con le risposte.


3

Soluzione Django 1.7 per me:

Ci sono variabili esatte alla domanda, ma dovresti sicuramente essere in grado di sezionare questo esempio

La chiave qui è di usare praticamente .__dict__il modello
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)

modello :

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

nel modello ho usato un filtro per accedere al campo in 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 ""

2

Sto usando questo, 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>

2

Questo approccio mostra come utilizzare una classe come ModelForm di django e un tag modello come {{form.as_table}}, ma avere tutta la tabella come output di dati, non un modulo.

Il primo passo è stato la sottoclasse del widget TextInput di 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))

Quindi ho eseguito la sottoclasse di ModelForm di Django per scambiare i widget predefiniti con le versioni di sola lettura:

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" 

Erano gli unici widget di cui avevo bisogno. Ma non dovrebbe essere difficile estendere questa idea ad altri widget.


1

Solo una modifica di @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

Lascia che Django gestisca tutti gli altri campi diversi dai campi correlati. Sento che è più stabile



0

Ho appena provato qualcosa di simile in shell e sembra fare il suo lavoro:

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

Notare che se si desidera la rappresentazione str () per oggetti estranei, è necessario definirla nel loro metodo str . Da questo hai dettato i valori per l'oggetto. Quindi puoi eseguire il rendering di un tipo di modello o altro.


0

Django> = 2.0

Aggiungi get_fields()a 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]

Quindi chiamalo come object.get_fieldssul tuo template.html:

<table>
    {% for label, value in object.get_fields %}
        <tr>
            <td>{{ label }}</td>
            <td>{{ value }}</td>
        </tr>
    {% endfor %}
</table>

-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


1
Ciao e benvenuto in SO. Si prega di non inviare solo risposte al codice. Anche questa domanda ha già una risposta accettata, la formattazione del codice non è corretta, stai utilizzando attributi html obsoleti e, cosa più importante: non stai spiegando come il tuo codice offre una soluzione migliore di quella accettata.
— Frieder,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.