208 Stimmen

Iterieren Sie über die Feldnamen und Werte der Modellinstanz in der Vorlage

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

Nehmen wir zum Beispiel an, 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 in der Vorlage wie folgt ausgegeben wird (nehmen Sie eine Instanz mit den angegebenen Werten an):

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

Was ich zu erreichen versuche, ist in der Lage, eine Instanz des Modells an eine Vorlage zu übergeben und in der Lage sein, über es dynamisch in der Vorlage, etwas wie dieses zu wiederholen:

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

Gibt es eine saubere, "Django-genehmigte" Weise, dies zu tun? Es scheint wie eine sehr häufige Aufgabe, und ich muss es oft für dieses bestimmte Projekt zu tun.

7voto

Alan Viars Punkte 2842

Es sollte wirklich eine eingebaute Möglichkeit 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) annimmt und ein SortedDict .

Zu den Vorteilen dieser Lösung gehören:

  • Es bewahrt die Ordnung mit Djangos eingebauter SortedDict .
  • Wenn versucht wird, das Label/verbose_name zu erhalten, aber auf den Feldnamen zurückgreift, wenn dieser nicht definiert ist.
  • Optional wird auch ein exclude() Liste der Feldnamen, um bestimmte Felder auszuschließen.
  • Wenn Ihre Formularklasse eine Meta: exclude() aber Sie wollen trotzdem die Werte zurückgeben, dann fügen Sie diese Felder zu den optionalen append() Liste.

Um diese Lösung zu verwenden, fügen Sie zunächst diese Datei/Funktion irgendwo hinzu und importieren 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

Jetzt also in Ihrem views.py könnten Sie etwa so vorgehen

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 in Ihrem my-template.html Vorlage können Sie über die Daten wie folgt iterieren...

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

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

{% endfor %}

Viel Glück! Hoffentlich hilft das jemandem!

5voto

seler Punkte 8097

Anstatt jedes Modell zu bearbeiten, würde ich empfehlen, Folgendes zu schreiben ein Template-Tag die alle Felder von jedes Modell gegeben.
Jedes Objekt hat eine Liste von Feldern ._meta.fields .
Jedes Feldobjekt hat das Attribut name die den Namen und die Methode zurückgibt value_to_string() die mit Ihrem Modell geliefert wurde object seinen Wert zurück.
Der Rest ist so einfach, wie es in Django-Dokumentation .

Hier ist mein Beispiel, wie diese Vorlage 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>] %}")

4voto

Dmitry Shevchenko Punkte 30176

Ja, es ist nicht schön, du musst deine eigene Verpackung machen. Werfen Sie einen Blick auf builtin Datenabfrage App, die alle Funktionen bietet, die Sie wirklich brauchen.

4voto

Jeremy Punkte 312

Django 1.7 Lösung für mich:

Es gibt Variablen, die genau auf die Frage zutreffen, aber Sie sollten auf jeden Fall in der Lage sein, dieses Beispiel zu sezieren

Der Schlüssel dazu ist die Verwendung der .__dict__ des Modells
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)

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 im Diktat zuzugreifen
filter.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 ""

4voto

Xealot Punkte 1609

Dies kann als ein Hack, aber ich habe dies getan, bevor mit modelform_factory, um ein Modell Instanz in ein Formular zu machen.

Die Form-Klasse hat eine Menge mehr Informationen, die super einfach zu iterieren ist, und es wird den gleichen Zweck auf Kosten von etwas mehr Overhead dienen. Wenn Ihre Set-Größen relativ klein sind, denke ich, dass die Auswirkungen auf die Leistung vernachlässigbar sein würde.

Der einzige Vorteil neben der Bequemlichkeit ist natürlich, dass Sie die Tabelle zu einem späteren Zeitpunkt einfach in eine bearbeitbare Datentabelle umwandeln können.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X