Durchlaufen Sie die Feldnamen und Werte der Modellinstanz in der Vorlage

183

Ich versuche, eine grundlegende Vorlage zu erstellen, um die Feldwerte der ausgewählten Instanz zusammen mit ihren Namen anzuzeigen. Stellen Sie sich das nur als Standardausgabe der Werte dieser Instanz im Tabellenformat vor, mit dem Feldnamen (Verbose_name speziell, falls im Feld angegeben) in der ersten Spalte und dem Wert dieses Felds in der zweiten Spalte.

Angenommen, wir haben die folgende Modelldefinition:

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

Ich möchte, dass es wie folgt in der Vorlage ausgegeben wird (nehmen Sie eine Instanz mit den angegebenen Werten an):

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

Ich versuche zu erreichen, dass eine Instanz des Modells an eine Vorlage übergeben und in der Vorlage dynamisch durchlaufen werden kann.

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

Gibt es eine nette, von Django genehmigte Möglichkeit, dies zu tun? Es scheint eine sehr häufige Aufgabe zu sein, und ich werde es für dieses spezielle Projekt oft tun müssen.

Wayne Koorts
quelle

Antworten:

171

model._meta.get_all_field_names()Wenn Sie alle Feldnamen des Modells erhalten, können model._meta.get_field()Sie sich zum ausführlichen Namen durcharbeiten und getattr(model_instance, 'field_name')den Wert aus dem Modell abrufen.

HINWEIS: model._meta.get_all_field_names()ist in Django 1.9 veraltet. Verwenden Sie stattdessen model._meta.get_fields(), um die Felder des Modells abzurufen und field.namejeden Feldnamen abzurufen.

Ignacio Vazquez-Abrams
quelle
2
Dies ist immer noch sehr manuell, und ich müsste in der Ansicht eine Art Metaobjekt erstellen, das ich dann in die Vorlage übergebe, was eher ein Hack ist, als ich möchte. Sicherlich muss es einen saubereren Weg geben?
Wayne Koorts
2
Sie können dies alles in einer Klasse zusammenfassen, ähnlich wie ModelForm.
Ignacio Vazquez-Abrams
18
Ich glaube nicht, dass Sie _ Methoden in den Vorlagen aufrufen können.
Issac Kelly
2
Dies funktioniert, aber Sie sollten nicht auf eine private API angewiesen sein (da "_" vorangestellt ist), um dies zu erreichen. Das Problem bei der Verwendung einer privaten API besteht darin, dass private Methoden nicht garantiert von Version zu Version funktionieren.
Devy
1
Ich denke, diese Methode sollte nicht bevorzugt werden, da wir nicht auf die Attribute zugreifen sollten, die mit dem Unterstrich aus Vorlagen beginnen
GP92
72

Sie können den To -Python- Queryset-Serializer von Django verwenden .

Fügen Sie einfach den folgenden Code in Ihre Ansicht ein:

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

Und dann in der Vorlage:

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

Sein großer Vorteil ist die Tatsache, dass es Beziehungsfelder behandelt.

Versuchen Sie für die Teilmenge der Felder:

data = serializers.serialize('python', SomeModel.objects.all(), fields=('name','size'))
Pawel Furmaniak
quelle
Das ist cool - aber wie würde man mit dieser Methode die Ergebnisse auf bestimmte Felder beschränken?
Herman Schaaf
2
Dies sollte die endgültige Antwort sein, Fremdschlüssel und keine privaten API-Anrufe verarbeiten. Tolle Antwort, danke.
Yunti
3
Es ist nicht erforderlich, serialize zu verwenden. Sie können die Methode values ​​() eines Abfragesatzes verwenden , der ein Wörterbuch zurückgibt. Ferner akzeptiert diese Methode eine Liste von Feldern, für die eine Teilmenge erstellt werden soll. Siehe Link . Siehe meine vollständige Antwort.
user3062149
Können wir dies aktualisieren, um nur die .fields zu senden, anstatt es in einer Schleife verarbeiten zu müssen? Ich möchte die Modell- / Tabellennamen nicht offenlegen
Loser Coder
Ermöglicht diese Methode die Übergabe verbose_namedes Feldes?
alias51
70

Endlich eine gute Lösung dafür auf der Entwickler-Mailingliste gefunden :

In der Ansicht hinzufügen:

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

in der Vorlage hinzufügen:

{% for field in object %}
    <li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
Roger
quelle
1
Gute Lösung, aber nicht sehr allgemein, da es kein model_to_dict für ForeignKey-Felder, sondern ein Unicode- Ergebnis zurückgibt , sodass Sie komplexe Objekte nicht einfach in dict
Vestel
22
Es ist gefährlich, ein Objekt zu überschreiben. Sie sollten einen anderen variablen Namen verwenden.
Emil Stenström
Danke dir! Ich habe Djangos model_to_dict () ersetzt, um mit ForeignKey umgehen zu können. Bitte lesen Sie meine separate Antwort (Ich habe meinen vorherigen Kommentar gelöscht, da Kommentare die Code-Formatierung nicht unterstützen. Entschuldigung, das wusste ich nicht.)
Magnus Gustavsson
2
Angenommen, das FooFormist ein ModelForm, wäre es nicht einfacher, einfach zu tun : FooForm(instance=Foo.objects.get(pk=object_id)))?
Berühmte
Haben Sie eine Idee, wie Sie mit dieser Methode nur bearbeitbare Felder anzeigen würden?
alias51
22

Angesichts der Veröffentlichung von Django 1.8 (und der Formalisierung der Model _meta-API) dachte ich, ich würde dies mit einer neueren Antwort aktualisieren.

Unter der Annahme des gleichen Modells:

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+ (formalisierte Model _meta API)

In Django 1.8 geändert:

Die Modell- _metaAPI war schon immer als Django-intern vorhanden, wurde jedoch nicht offiziell dokumentiert und unterstützt. Im Rahmen der Bemühungen, diese API öffentlich zu machen, haben sich einige der bereits vorhandenen API-Einstiegspunkte geringfügig geändert. Es wurde ein Migrationshandbuch bereitgestellt, das Sie bei der Konvertierung Ihres Codes zur Verwendung der neuen offiziellen API unterstützt.

Im folgenden Beispiel verwenden wir die formalisierte Methode zum Abrufen aller Feldinstanzen eines Modells über 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')]

Eigentlich wurde ich darauf aufmerksam gemacht, dass das oben Genannte für das, was benötigt wurde, etwas über Bord geht (da stimme ich zu!). Einfach ist besser als komplex. Ich lasse das oben genannte als Referenz. Für die Anzeige in der Vorlage ist es jedoch am besten, eine ModelForm zu verwenden und eine Instanz zu übergeben. Sie können über das Formular iterieren (entspricht dem Iterieren über jedes Feld des Formulars) und das Attribut label verwenden, um den ausführlichen Namen des Modellfelds abzurufen, und die Wertmethode verwenden, um den Wert abzurufen:

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

Jetzt rendern wir die Felder in der Vorlage:

<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.
quelle
2
Es wäre großartig, wenn Sie Ihre Antwort anpassen würden, um die "> 1.8" -Methode zum Einfügen der Modellfelder in eine Vorlage anzuzeigen. Im Moment beantwortet Ihre Antwort die Frage nicht direkt. Es zeigt, wie die Felder des Modells in der Shell abgerufen werden.
Escher
@Escher - Antwort aktualisiert! Danke für den Vorschlag. Lassen Sie mich wissen, wenn ich etwas verpasst / durcheinander gebracht habe!
Michael B
Upvoted. Ich habe bearbeitet, um das Drucken der Werte sowie der Feldnamen einzuschließen. Sehen Sie, was Sie denken.
Escher
Wo drucken Sie die Werte? Ich sehe es nur den Namen und den ausführlichen Namen drucken?
Dr. Ernie
@ MichaelB Hmm. Ich konnte "field.value" nicht zum Laufen bringen. Die Felder scheinen Datenbankfelder zu sein, nicht die tatsächlichen Spaltendaten. Ich musste einen Filter verwenden, der getattr (Objekt, Name) hieß. Welche Version von Django funktioniert das für Sie?
Dr. Ernie
19

Hier ist ein anderer Ansatz unter Verwendung einer Modellmethode. Diese Version löst Auswahllisten- / Auswahlfelder auf, überspringt leere Felder und ermöglicht das Ausschließen bestimmter Felder.

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

Dann in Ihrer Vorlage:

{% for f in app.get_all_fields %}
  <dt>{{f.label|capfirst}}</dt>
    <dd>
      {{f.value|escape|urlize|linebreaks}}
    </dd>
{% endfor %}
Shacker
quelle
3
Warum brauchen Sie das except User.DoesNotExist:?
Sevenearths
Ich würde gerne AttributeError anstelle von User.DoesNotExist verwenden - Ich kann nicht sehen, warum User.DoesNotExist ausgelöst wird.
Askvictor
Es könnte auch besser sein, self._meta.get_fields () zu verwenden, da dies in Django 1.8+ offiziell verfügbar gemacht wird. Dann erhalten Sie jedoch Beziehungen im Code, die Sie herausfiltern
müssten
Ich habe die Antwort so bearbeitet, dass AttributeError anstelle von User.DoesNotExist verwendet wird (was ein Überbleibsel meiner ursprünglichen Implementierung war). Vielen Dank. Ich halte mich zurück, _meta.get_fields()bis ich es testen kann.
Shacker
13

Ok, ich weiß, dass dies etwas spät ist, aber da ich darauf gestoßen bin, bevor ich die richtige Antwort gefunden habe, könnte es auch jemand anderes tun.

Aus den Django-Dokumenten :

# 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
quelle
Ich mag diese Antwort. Wenn Ihre Abfrage mehr als einen Datensatz zurückgibt und Sie nur den neuesten möchten, gehen Sie wie folgt vor. 1. Stellen Sie sicher , dass Sie ordering = ['-id']in class Meta:Ihrer Aufgabe in models.py. 2. dann benutzeBlog.objects.filter(name__startswith='Beatles').values()[0]
Sevenearths
Clevere Idee. Wenn Sie jedoch bereits ein modelObjekt haben, rufen Sie die Datenbank erneut auf, um die Felder abzurufen. Wie kann man das umgehen?
Freitag,
@ user1763732 Überprüfen Sie einfach die Dokumentationen für das QuerySet: docs.djangoproject.com/de/dev/ref/models/querysets
olofom
9

Sie können die values()Methode a verwenden queryset, die ein Wörterbuch zurückgibt. Ferner akzeptiert diese Methode eine Liste von Feldern, für die eine Teilmenge erstellt werden soll. Die values()Methode funktioniert nicht mit get(), daher müssen Sie verwenden filter()(siehe QuerySet-API ).

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>

Für eine Sammlung von Instanzen, die vom Filter zurückgegeben werden:

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

In 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 %}
user3062149
quelle
Das ist unglaublich, danke! Ich habe eine Frage, ob Sie mir helfen könnten. Ich füge alle Objektdaten in a ein table, also brauche ich jedes keys in a th. Wie mache ich das ohne Schleifen? Nehmen Sie einfach eine Objektinstanz und durchlaufen Sie sie für keys? Zur Zeit bin ich vorbei separat model_to_dict(Model())für das th, aber ich denke , es ist eine unnötige Objektinstanziierung.
Oxwivi
Fantastische Antwort. Persönlich habe ich dies sowohl in einer Listenansicht als auch in einer Detailansicht verwendet. Die Listenansicht ist weitgehend offensichtlich zu implementieren, aber mit der Detailansicht überschreibe ich get_objectdie Detailansicht (aufgrund der Einschränkung des Inline-Codes für Kommentare verstümmelt und nicht der Meinung, dass dies für die eigene Antwort ausreicht, wenn man bedenkt, wie gesättigt dieser Thread ist): def get_object(self, **kwargs): obj = super().get_object(**kwargs) obj = obj.__class__.objects.filter(pk=obj.pk).values()[0] return obj
sdconrox
Wie würden Sie obj.get_absolute_urldieser Liste hinzufügen , ohne die Zeilen zu duplizieren?
alias51
8

Ich habe https://stackoverflow.com/a/3431104/2022534 verwendet, aber Djangos model_to_dict () durch dieses ersetzt, um ForeignKey verarbeiten zu können:

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

Bitte beachten Sie, dass ich es ein wenig vereinfacht habe, indem ich die Teile des Originals entfernt habe, die ich nicht brauchte. Vielleicht möchten Sie diese zurücklegen.

Magnus Gustavsson
quelle
8

Sie können ein Formular die Arbeit für Sie erledigen lassen.

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

Dann in der Vorlage:

<table>
    {% for field in form %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>
ja
quelle
3
Diese Methode (angewendet innerhalb von a DetailView) funktioniert gut für mich. Möglicherweise möchten Sie jedoch field.labelanstelle von verwenden field.name.
David Cain
7

Es sollte wirklich einen eingebauten Weg geben, dies zu tun. Ich habe dieses Dienstprogramm geschrieben build_pretty_data_view, das ein Modellobjekt und eine Formularinstanz (ein auf Ihrem Modell basierendes Formular) verwendet und a zurückgibtSortedDict .

Zu den Vorteilen dieser Lösung gehören:

  • Es bewahrt die Ordnung mit Djangos eingebautem SortedDict .
  • Wenn versucht wird, die Bezeichnung / verbose_name zu erhalten, aber auf den Feldnamen zurückgegriffen wird, wenn einer nicht definiert ist.
  • Optional wird auch ein exclude() Liste von Feldnamen verwendet, um bestimmte Felder auszuschließen.
  • Wenn Ihre Formularklasse a enthält Meta: exclude(), Sie aber dennoch die Werte zurückgeben möchten, fügen Sie diese Felder der optionalen append()Liste hinzu.

Um diese Lösung zu verwenden, fügen Sie diese Datei / Funktion zuerst irgendwo hinzu und importieren Sie sie dann in Ihre 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

Also views.pykönnten Sie jetzt in Ihrem so etwas tun

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

Jetzt können Sie in Ihrer my-template.htmlVorlage die Daten wie folgt durchlaufen ...

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

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

{% endfor %}

Viel Glück. Hoffe das hilft jemandem!

Alan Viars
quelle
7

Unten ist meine, inspiriert von Shackers get_all_fields . Es wird ein Diktat einer Modellinstanz abgerufen. Wenn ein Begegnungsbeziehungsfeld vorhanden ist, wird dem Feldwert ein Diktat rekursiv zugewiesen.

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

Diese Funktion wird hauptsächlich verwendet, um eine Modellinstanz in JSON-Daten zu sichern:

def to_json(self):
    tree = to_dict(self, exclude=('id', 'User.password'))
    return json.dumps(tree, ensure_ascii=False)
Wunder
quelle
Gute Arbeit! Schlagen Sie vor, Auswahlunterstützung hinzuzufügen ... elif hasattr (Feld, 'Auswahlmöglichkeiten'): tree [Feldname] = dict (field.choices) .get (Wert, Wert)
oden
5

Anstatt jedes Modell zu bearbeiten, würde ich empfehlen, ein Vorlagen-Tag zu schreiben, das alle Felder eines bestimmten Modells zurückgibt.
Jedes Objekt hat eine Liste von Feldern ._meta.fields.
Jedes Feldobjekt verfügt über ein Attribut name, das seinen Namen zurückgibt value_to_string(), und die mit Ihrem Modell gelieferte Methode objectgibt seinen Wert zurück.
Der Rest ist so einfach wie in der Django-Dokumentation angegeben .

Hier ist mein Beispiel, wie dieses Templatetag aussehen könnte:

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

Ja, es ist nicht schön, du musst deinen eigenen Wrapper machen. Schauen Sie sich die integrierte Datenbank- App an, die alle Funktionen bietet , die Sie wirklich benötigen.

Dmitry Shevchenko
quelle
Ich wollte sagen ... Databrowse macht genau das, obwohl ich fand, dass es eine völlig nutzlose App ist.
Mpen
4

Dies kann als Hack angesehen werden, aber ich habe dies getan, bevor ich modelform_factory verwendet habe, um eine Modellinstanz in ein Formular umzuwandeln.

Die Form-Klasse enthält viel mehr Informationen, die sehr einfach zu durchlaufen sind, und sie erfüllt denselben Zweck auf Kosten eines etwas höheren Overheads. Wenn Ihre eingestellten Größen relativ klein sind, sind die Auswirkungen auf die Leistung meiner Meinung nach vernachlässigbar.

Der einzige Vorteil neben der Bequemlichkeit besteht natürlich darin, dass Sie die Tabelle zu einem späteren Zeitpunkt problemlos in ein bearbeitbares Datagrid verwandeln können.

Xealot
quelle
4

Ich habe die folgende Methode entwickelt, die für mich funktioniert, da dem Modell in jedem Fall eine ModelForm zugeordnet ist.

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)

Hier ist ein Auszug aus der Vorlage, die ich für diese bestimmte Ansicht verwende:

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

Das Schöne an dieser Methode ist, dass ich vorlagenweise die Reihenfolge auswählen kann, in der die Feldbezeichnungen angezeigt werden sollen, wobei das an GetModelData übergebene Tupel verwendet und die Feldnamen angegeben werden. Dadurch kann ich auch bestimmte Felder (z. B. einen Benutzer-Fremdschlüssel) ausschließen, da nur die über das Tupel übergebenen Feldnamen in das endgültige Wörterbuch integriert sind.

Ich werde das nicht als Antwort akzeptieren, weil ich sicher bin, dass sich jemand etwas mehr "Djangonic" einfallen lassen kann :-)

Update: Ich wähle dies als endgültige Antwort, weil es die einfachste unter den gegebenen ist, die das tut, was ich brauche. Vielen Dank an alle, die Antworten beigesteuert haben.

Wayne Koorts
quelle
3

Django 1.7 Lösung für mich:

Dort sind die Variablen genau auf die Frage abgestimmt, aber Sie sollten dieses Beispiel auf jeden Fall analysieren können

Der Schlüssel hier ist, .__dict__die Modellansichten so
ziemlich zu verwenden .

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)

Vorlage :

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

In der Vorlage habe ich einen Filter verwendet, um auf das Feld in der
Datei dict filter.py zuzugreifen :

@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
quelle
2

Ich benutze dies, 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
quelle
2

Dieser Ansatz zeigt, wie eine Klasse wie ModelForm von django und ein Vorlagen-Tag wie {{form.as_table}} verwendet werden, wobei jedoch die gesamte Tabelle wie eine Datenausgabe und nicht wie ein Formular aussieht.

Der erste Schritt bestand darin, das TextInput-Widget von django in Unterklassen zu unterteilen:

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

Dann habe ich die ModelForm von django untergeordnet, um die Standard-Widgets gegen schreibgeschützte Versionen auszutauschen:

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" 

Das waren die einzigen Widgets, die ich brauchte. Es sollte jedoch nicht schwierig sein, diese Idee auf andere Widgets auszudehnen.

Futter
quelle
1

Nur eine Bearbeitung von @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

Lassen Sie Django alle anderen Felder außer den zugehörigen Feldern behandeln. Ich finde das stabiler

Shivam Goyal
quelle
0

Ich habe so etwas gerade in der Shell getestet und scheint seinen Job zu machen:

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

Wenn Sie eine str () - Darstellung für Fremdobjekte wünschen, sollten Sie diese in ihrer str- Methode definieren. Daraus haben Sie ein Diktat von Werten für das Objekt. Dann können Sie eine Art Vorlage oder was auch immer rendern.

Adam
quelle
0

Django> = 2,0

Fügen Sie get_fields()Ihrem hinzu 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]

Dann nenne es wie object.get_fieldsauf deinem template.html:

<table>
    {% for label, value in object.get_fields %}
        <tr>
            <td>{{ label }}</td>
            <td>{{ value }}</td>
        </tr>
    {% endfor %}
</table>
JV conseil
quelle
-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.
quelle
1
Hallo und willkommen bei SO. Bitte posten Sie nicht nur Antworten. Auch diese Frage hat bereits eine akzeptierte Antwort, Ihre Code-Formatierung ist nicht korrekt, Sie verwenden veraltete HTML-Attribute und das Wichtigste: Sie erklären nicht, wie Ihr Code eine bessere Lösung bietet als die akzeptierte.
Frieder