Iteracja nazw i wartości pól instancji modelu w szablonie

183

Próbuję utworzyć podstawowy szablon do wyświetlania wartości pól wybranej instancji wraz z ich nazwami. Pomyśl o tym jak o standardowym wyjściu wartości tego wystąpienia w formacie tabeli, z nazwą pola (pełna nazwa, jeśli jest określona w polu) w pierwszej kolumnie i wartością tego pola w drugiej kolumnie.

Załóżmy na przykład, że mamy następującą definicję modelu:

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

Chciałbym, aby był wyprowadzany w szablonie w taki sposób (załóżmy instancję o podanych wartościach):

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

Staram się przekazać instancję modelu do szablonu i dynamicznie iterować go w szablonie, mniej więcej tak:

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

Czy istnieje ciekawy sposób na „zatwierdzenie przez Django”? Wydaje się, że jest to bardzo częste zadanie i będę musiał to robić często w przypadku tego konkretnego projektu.

Wayne Koorts
źródło

Odpowiedzi:

171

model._meta.get_all_field_names()poda ci wszystkie nazwy pól modelu, a następnie możesz użyć, model._meta.get_field()aby przejść do pełnej nazwy i getattr(model_instance, 'field_name')uzyskać wartość z modelu.

UWAGA: model._meta.get_all_field_names()jest przestarzałe w django 1.9. Zamiast tego użyj, model._meta.get_fields()aby uzyskać pola modelu i field.nameuzyskać nazwę każdego pola.

Ignacio Vazquez-Abrams
źródło
2
Jest to nadal bardzo ręczne i musiałbym zbudować jakiś obiekt meta w widoku, który następnie przekazuję do szablonu, co jest bardziej hackem niż chciałbym. Z pewnością musi być lepszy sposób?
Wayne Koorts
2
Możesz zawrzeć to wszystko w klasie, podobnie jak robi to ModelForm.
Ignacio Vazquez-Abrams
18
Nie sądzę, że można wywoływać dowolne metody _ w szablonach.
Issac Kelly,
2
Działa to, ale nie powinno być zależne od prywatnego interfejsu API (ponieważ ma on prefiks „_”), aby go osiągnąć. Problem polegający na korzystaniu z prywatnego interfejsu API polega na tym, że nie gwarantuje się, że prywatne metody będą działać w różnych wersjach.
Devy
1
Myślę, że ta metoda nie powinna być preferowana, ponieważ nie powinniśmy uzyskiwać dostępu do atrybutów zaczynających się od podkreślenia z szablonów
GP92
72

Możesz użyć serializatora zestawu zapytań Django do zapytania.

Wystarczy umieścić następujący kod w swoim widoku:

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

A następnie w szablonie:

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

Jego wielką zaletą jest to, że obsługuje pola relacji.

Dla podzbioru pól spróbuj:

data = serializers.serialize('python', SomeModel.objects.all(), fields=('name','size'))
Paweł Furmaniak
źródło
To fajnie - ale dzięki tej metodzie, jak ograniczyć wyniki tylko do niektórych pól?
Herman Schaaf
2
To powinna być ostateczna odpowiedź, obsługuje klucze obce i nie ma prywatnych połączeń API. Świetna odpowiedź, dzięki.
Yunti
3
Nie ma potrzeby używania serializacji. Możesz użyć metody wartości () zestawu zapytań , który zwraca słownik. Ponadto ta metoda akceptuje listę pól do podzbioru. Zobacz link . Zobacz moją pełną odpowiedź.
user3062149 27.04.16
Czy możemy to zaktualizować, aby wysyłać tylko pola. Zamiast przetwarzać je w pętli? Nie chcę ujawniać nazw modeli / tabel
Loser Coder,
Czy ta metoda pozwala na verbose_nameprzekazanie pola?
alias51
71

Wreszcie znalazłem dobre rozwiązanie tego problemu na liście dyskusyjnej deweloperów :

W widoku dodaj:

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

w szablonie dodaj:

{% for field in object %}
    <li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
zrozumiałem
źródło
1
Dobre rozwiązanie, ale niezbyt ogólne, ponieważ nie zwraca parametru model_to_dict dla pól ForeignKey, ale wynik Unicode , więc nie można łatwo serializować złożonego obiektu w dict
Vestel
22
Niebezpieczne jest przesłonięcie obiektu, należy użyć innej zmiennej nazwy.
Emil Stenström,
Dziękuję Ci! Zastąpiłem model_to_dict () Django, aby móc obsługiwać ForeignKey. Proszę zobaczyć moją oddzielną odpowiedź (usunąłem poprzedni komentarz, ponieważ komentarze nie obsługują formatowania kodu. Przepraszam, nie wiedziałem o tym.)
Magnus Gustavsson
2
Zakładając, że FooFormjest to ModelForm, czy nie łatwiej byłoby po prostu zrobić FooForm(instance=Foo.objects.get(pk=object_id))):?
beruic
Masz pomysł, jak wyświetlać za pomocą tej metody tylko pola edytowalne?
alias51
22

W świetle wydania Django 1.8 (i sformalizowania API Model _meta , pomyślałem, że zaktualizuję to, dodając nowszą odpowiedź.

Zakładając ten sam model:

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+ (sformalizowany model _meta API)

Zmieniono w Django 1.8:

Model _metaAPI zawsze istniał jako wewnętrzny Django, ale nie był formalnie udokumentowany i obsługiwany. W ramach działań mających na celu upublicznienie tego interfejsu API niektóre z już istniejących punktów wejścia API zmieniły się nieznacznie. Udostępniono przewodnik migracji ułatwiający konwersję kodu do nowego, oficjalnego interfejsu API.

W poniższym przykładzie wykorzystamy sformalizowaną metodę pobierania wszystkich instancji pola modelu za pomocą 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')]

W rzeczywistości zwrócono mi uwagę, że powyższe jest nieco przesadzone, jeśli chodzi o to, co było potrzebne (zgadzam się!). Prosty jest lepszy niż złożony. Zostawiam powyższe w celach informacyjnych. Jednak do wyświetlenia w szablonie najlepszą metodą byłoby użycie ModelForm i przekazanie w instancji. Możesz iterować po formularzu (odpowiednik iteracji po każdym z pól formularza) i użyć atrybutu label, aby pobrać pełną nazwę modelu pola, i użyć metody value, aby pobrać wartość:

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

Teraz renderujemy pola w szablonie:

<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.
źródło
2
Byłoby wspaniale, gdybyś dopasował swoją odpowiedź, aby pokazać sposób „> 1.8” umieszczania pól modelu w szablonie. W tej chwili twoja odpowiedź nie odpowiada bezpośrednio na pytanie; pokazuje, jak uzyskać pola modelu w powłoce.
Escher,
@Escher - Zaktualizowano odpowiedź! Dzieki za sugestie. Daj mi znać, jeśli coś przeoczyłem / coś pomieszałem!
Michael B
Pozytywne. Edytowałem, aby uwzględnić drukowanie wartości oraz nazw pól. Zobacz co myślisz.
Escher,
Gdzie drukujesz wartości? Widzę tylko, że drukuje imię i pełną nazwę?
Dr Ernie
@MichaelB Hmm. Nie udało mi się uruchomić „field.value” do pracy; pola wydają się być polami bazy danych, a nie rzeczywistymi danymi kolumn. Musiałem użyć filtra, który wywołał getattr (obiekt, nazwa). Która wersja Django działa dla Ciebie?
Dr Ernie,
19

Oto inne podejście przy użyciu metody modelowej. Ta wersja rozwiązuje pola listy wyboru / wyboru, pomija puste pola i pozwala wykluczyć określone pola.

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

Następnie w swoim szablonie:

{% for f in app.get_all_fields %}
  <dt>{{f.label|capfirst}}</dt>
    <dd>
      {{f.value|escape|urlize|linebreaks}}
    </dd>
{% endfor %}
shacker
źródło
3
Dlaczego potrzebne except User.DoesNotExist:?
Sevenearths
Byłbym skłonny użyć AttributeError zamiast User.DoesNotExist - nie rozumiem, dlaczego miałoby to powodować User.DoesNotExist.
askvictor,
Ponadto może być lepiej użyć self._meta.get_fields (), ponieważ jest to oficjalnie ujawnione w django 1.8+. Jednak w efekcie powstają relacje w kodzie, które należy odfiltrować, sprawdzając f.is_relation
askvictor
Zredagowałem odpowiedź, aby użyć AttributeError zamiast User.DoesNotExist (który był pozostałością po mojej oryginalnej implementacji). Dzięki. Wstrzymuję się, _meta.get_fields()aż będę mógł to przetestować.
shacker
13

Ok, wiem, że to trochę za późno, ale ponieważ natknąłem się na to, zanim znalazłem poprawną odpowiedź, może ktoś inny.

Z dokumentacji 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
źródło
Podoba mi się ta odpowiedź. Jeśli zapytanie zwraca więcej niż jeden rekord, a chcesz tylko najnowszy, wykonaj następujące czynności. 1. Upewnij się, że masz ordering = ['-id']w class Meta:swoim obiekcie models.py. 2. następnie użyjBlog.objects.filter(name__startswith='Beatles').values()[0]
Sevenearths
Sprytny pomysł. Ale jeśli masz już modelobiekt, trafiłbyś ponownie do bazy danych tylko po to, aby uzyskać pola. Jakiś sposób na to?
frnhr
@ user1763732 wystarczy sprawdzić dokumentację dla QuerySet: docs.djangoproject.com/en/dev/ref/models/querysets
olofom
9

Możesz użyć values()metody a queryset, która zwraca słownik. Ponadto ta metoda akceptuje listę pól do podzbioru. Ta values()metoda nie będzie działać get(), dlatego należy jej użyć filter()(patrz interfejs API QuerySet ).

W view...

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

W detail.html...

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

W przypadku zbioru instancji zwróconych przez filtr:

   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 %}
użytkownik3062149
źródło
To jest niesamowite, dziękuję! Mam pytanie, czy możesz mi pomóc; Umieszczam wszystkie dane obiektów w table, więc potrzebuję każdego z nich keyw th. Jak to zrobić bez pętli? Po prostu weź dowolną instancję obiektu i powtórz ją przez keys? Obecnie osobno przechodzę model_to_dict(Model())do th, ale myślę, że jest to niepotrzebna instancja obiektu.
Oxwivi,
Fantastyczna odpowiedź. Osobiście użyłem tego w widoku listy i widoku szczegółowym. Widok listy jest w dużej mierze oczywisty do wdrożenia, ale w widoku szczegółów zastępuję get_objectwidok szczegółów (zniekształcony ze względu na ograniczenia kodu w komentarzach i nie wydaje mi się, że to wystarczy do własnej odpowiedzi, biorąc pod uwagę nasycenie tego wątku): def get_object(self, **kwargs): obj = super().get_object(**kwargs) obj = obj.__class__.objects.filter(pk=obj.pk).values()[0] return obj
sdconrox,
jak dodałbyś obj.get_absolute_urldo tej listy bez powielania wierszy?
alias51
8

Użyłem https://stackoverflow.com/a/3431104/2022534, ale zastąpiłem model_to_dict () Django tym, aby móc obsługiwać 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

Pamiętaj, że nieco uprościłem to, usuwając części oryginału, których nie potrzebowałem. Możesz je odłożyć.

Magnus Gustavsson
źródło
8

Możesz mieć formularz, który wykona pracę za Ciebie.

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

Następnie w szablonie:

<table>
    {% for field in form %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>
ja
źródło
3
Ta metoda (zastosowana w a DetailView) działa dla mnie dobrze. Możesz jednak użyć field.labelzamiast field.name.
David Cain
7

Naprawdę powinien istnieć wbudowany sposób, aby to zrobić. Napisałem to narzędzie, build_pretty_data_viewktóre pobiera obiekt modelu i instancję formularza (formularz oparty na modelu) i zwraca a SortedDict.

Korzyści z tego rozwiązania obejmują:

  • Utrzymuje porządek za pomocą wbudowanego Django SortedDict.
  • Kiedy próbuje uzyskać etykietę / verbose_name, ale wraca do nazwy pola, jeśli nie jest zdefiniowana.
  • Opcjonalnie zajmie również exclude()listę nazw pól, aby wykluczyć niektóre pola.
  • Jeśli klasa formularza zawiera Meta: exclude(), ale nadal chcesz zwrócić wartości, dodaj te pola do opcjonalnej append()listy.

Aby skorzystać z tego rozwiązania, najpierw dodaj gdzieś ten plik / funkcję, a następnie zaimportuj go do swojego 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

Więc teraz views.pymożesz zrobić coś takiego

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

Teraz w my-template.htmlszablonie możesz iterować dane w ten sposób ...

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

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

{% endfor %}

Powodzenia. Mam nadzieję, że to komuś pomoże!

Alan Viars
źródło
7

Poniżej jest moja, zainspirowana shackerem get_all_fields . Otrzymuje dykt jednej instancji modelu, jeśli napotka pole relacji, to rekursywnie przypisz wartość pola dyktowi.

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

Ta funkcja jest używana głównie do zrzutu instancji modelu do danych JSON:

def to_json(self):
    tree = to_dict(self, exclude=('id', 'User.password'))
    return json.dumps(tree, ensure_ascii=False)
cud
źródło
Świetna robota! Sugeruj dodanie obsługi opcji wyboru ... elif hasattr (pole, 'wybory'): drzewo [nazwa_pola] = dict (pole.choices) .get (wartość, wartość)
oden
5

Zamiast edytować każdy model polecam napisanie jednego znacznika szablonu, który zwróci wszystkie podane pola dowolnego modelu .
Każdy obiekt ma listę pól ._meta.fields.
Każdy obiekt pola ma atrybut name, który zwróci jego nazwę, a metoda value_to_string()dostarczona z modelem objectzwróci jego wartość.
Reszta jest tak prosta, jak napisano w dokumentacji Django .

Oto mój przykład, jak może wyglądać ten szablon:

    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
źródło
4

Tak, to nie jest ładne, musisz zrobić własne opakowanie. Spójrz na wbudowane databrowse aplikacji, która ma wszystkie funkcje potrzebne naprawdę.

Dmitrij Szewczenko
źródło
Miałem powiedzieć… databrowse właśnie to robi, chociaż uznałem, że jest to całkowicie bezużyteczna aplikacja.
mpen
4

Można to uznać za włamanie, ale zrobiłem to, zanim użyłem modelform_factory do przekształcenia instancji modelu w formę.

Klasa Form zawiera o wiele więcej informacji, które są bardzo łatwe do powtórzenia i będzie służyć temu samemu celowi kosztem nieco większego obciążenia. Jeśli twoje ustawione rozmiary są stosunkowo małe, myślę, że wpływ na wydajność byłby znikomy.

Jedną z zalet oprócz wygody jest oczywiście to, że możesz łatwo zmienić tabelę w edytowalny datagrid w późniejszym terminie.

Xealot
źródło
4

Wymyśliłem następującą metodę, która działa dla mnie, ponieważ w każdym przypadku model będzie miał związany z nim 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)

Oto wyciąg z szablonu, którego używam do tego konkretnego widoku:

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

Zaletą tej metody jest to, że mogę wybrać na podstawie szablonu po kolejności, w jakiej chciałbym wyświetlić etykiety pól, używając krotki przekazanej do GetModelData i określając nazwy pól. Pozwala mi to również wykluczyć niektóre pola (np. Klucz obcy użytkownika), ponieważ tylko nazwy pól przekazywane przez krotkę są wbudowane w ostateczny słownik.

Nie przyjmuję tego jako odpowiedzi, ponieważ jestem pewien, że ktoś może wymyślić coś więcej „Djangonic” :-)

Aktualizacja: wybieram to jako ostateczną odpowiedź, ponieważ jest to najprostszy z tych, który robi to, czego potrzebuję. Dziękujemy wszystkim, którzy udzielili odpowiedzi.

Wayne Koorts
źródło
3

Rozwiązanie Django 1.7 dla mnie:

Istnieją zmienne, które dokładnie odpowiadają pytaniu, ale zdecydowanie powinieneś być w stanie przeanalizować ten przykład

Kluczem jest tutaj użycie .__dict__modelu
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)

szablon :

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

w szablonie użyłem filtra, aby uzyskać dostęp do pola w
pliku dict 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 ""
Jeremy
źródło
2

Używam tego, 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
źródło
2

To podejście pokazuje, jak używać klasy, takiej jak ModelForm django i znacznik szablonu, taki jak {{form.as_table}}, ale wszystkie tabele wyglądają jak dane wyjściowe, a nie formularz.

Pierwszym krokiem było podklasowanie widgetu TextInput 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))

Następnie podklasowałem ModelForm django, aby zamienić domyślne widżety dla wersji tylko do odczytu:

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" 

To były jedyne widżety, których potrzebowałem. Jednak rozszerzenie tego pomysłu na inne widżety nie powinno być trudne.

Głaskanie pod brodę
źródło
1

Po prostu edycja @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

Pozwól Django obsłużyć wszystkie inne pola oprócz powiązanych pól. Czuję, że to jest bardziej stabilne

Shivam Goyal
źródło
0

Właśnie przetestowałem coś takiego w powłoce i wydaje się, że wykonuje swoją pracę:

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

Zauważ, że jeśli chcesz reprezentacji str () dla obcych obiektów, powinieneś zdefiniować ją w ich metodzie str . Z tego masz dyktę wartości obiektu. Następnie możesz renderować szablon lub cokolwiek innego.

Adam
źródło
0

Django> = 2.0

Dodaj get_fields()do 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]

Następnie nazwij to tak jak object.get_fieldsna swoim template.html:

<table>
    {% for label, value in object.get_fields %}
        <tr>
            <td>{{ label }}</td>
            <td>{{ value }}</td>
        </tr>
    {% endfor %}
</table>
JV conseil
źródło
-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.
źródło
1
Witam i witam w SO. Proszę nie pisać kodu tylko odpowiedzi. Również to pytanie ma już zaakceptowaną odpowiedź, twoje formatowanie kodu jest nieprawidłowe, używasz przestarzałych atrybutów HTML i najważniejsze: Nie wyjaśniasz, w jaki sposób twój kod zapewnia lepsze rozwiązanie niż zaakceptowany.
Frieder