Iterate atas contoh nama lapangan dan nilai-nilai dalam template

183

Saya mencoba membuat templat dasar untuk menampilkan nilai bidang instance yang dipilih, bersama dengan nama mereka. Anggap saja sebagai output standar dari nilai instance dalam format tabel, dengan nama bidang (verbose_name khusus jika ditentukan pada bidang) di kolom pertama dan nilai bidang itu di kolom kedua.

Misalnya, katakanlah kita memiliki definisi model berikut:

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

Saya ingin itu menjadi output dalam template seperti itu (asumsikan sebuah instance dengan nilai yang diberikan):

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

Apa yang saya coba capai adalah mampu mengirimkan contoh model ke templat dan dapat mengulanginya secara dinamis dalam templat, sesuatu seperti ini:

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

Apakah ada cara rapi, "disetujui Django" untuk melakukan ini? Sepertinya ini tugas yang sangat umum, dan saya harus sering melakukannya untuk proyek khusus ini.

Wayne Koort
sumber

Jawaban:

171

model._meta.get_all_field_names()akan memberi Anda semua nama bidang model, maka Anda dapat menggunakan model._meta.get_field()cara kerja Anda untuk nama verbose, dan getattr(model_instance, 'field_name')untuk mendapatkan nilai dari model.

CATATAN: model._meta.get_all_field_names()sudah usang dalam Django 1.9. Alih-alih gunakan model._meta.get_fields()untuk mendapatkan bidang model dan field.nameuntuk mendapatkan setiap nama bidang.

Ignacio Vazquez-Abrams
sumber
2
Ini masih sangat manual, dan saya harus membangun semacam objek meta di tampilan yang kemudian saya masukkan ke dalam templat, yang lebih merupakan peretasan daripada yang saya inginkan. Tentunya harus ada cara yang lebih rapi?
Wayne Koorts
2
Anda bisa merangkum semua ini di kelas, seperti halnya ModelForm.
Ignacio Vazquez-Abrams
18
Saya tidak percaya Anda dapat memanggil metode _ apa pun di template.
Issac Kelly
2
Ini berfungsi tetapi Anda tidak harus bergantung pada API pribadi (karena awalan dengan "_") untuk mencapainya. Masalah dengan mengandalkan API pribadi adalah bahwa metode pribadi tidak dijamin berfungsi dari versi ke versi.
Devy
1
Saya pikir metode ini tidak boleh lebih disukai karena kita tidak boleh mengakses atribut yang dimulai dengan garis bawah dari templat
GP92
72

Anda dapat menggunakan serializer to-python Django to-python .

Masukkan saja kode berikut ke dalam pandangan Anda:

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

Dan kemudian di templat:

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

Keuntungan besarnya adalah kenyataan bahwa ia menangani bidang hubungan.

Untuk subset bidang coba:

data = serializers.serialize('python', SomeModel.objects.all(), fields=('name','size'))
Pawel Furmaniak
sumber
Itu keren - tetapi dengan metode ini, bagaimana seseorang membatasi hasil hanya untuk bidang tertentu?
Herman Schaaf
2
Ini harus menjadi jawaban yang pasti, menangani kunci asing dan tidak ada panggilan api pribadi. Jawaban yang bagus, terima kasih.
Yunti
3
Tidak perlu menggunakan serialisasi. Anda bisa menggunakan metode values ​​() dari queryset , yang mengembalikan kamus. Lebih lanjut, metode ini menerima daftar bidang yang akan di-subset. Lihat tautan . Lihat jawaban lengkap saya.
user3062149
Bisakah kita perbarui ini hanya mengirim .fields daripada harus memprosesnya dalam satu lingkaran? Saya tidak ingin mengekspos nama model / tabel
Loser Coder
Apakah metode ini memungkinkan verbose_namebidang dilewatkan?
alias51
71

Akhirnya menemukan solusi yang bagus untuk ini di milis dev :

Dalam tampilan tambahkan:

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

dalam templat tambahkan:

{% for field in object %}
    <li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
Roger
sumber
1
Solusi yang bagus, tetapi tidak terlalu generik karena mengembalikan bukan model_to_dict untuk bidang ForeignKey, tetapi hasil unicode , jadi Anda tidak dapat dengan mudah membuat serialisasi objek kompleks menjadi dict
Vestel
22
Berbahaya untuk menimpa objek, Anda harus menggunakan beberapa nama yang mudah berubah lainnya.
Emil Stenström
Terima kasih! Saya mengganti model_to_dict () Django untuk dapat menangani ForeignKey. Silakan lihat jawaban saya yang terpisah (saya menghapus komentar saya sebelumnya karena komentar tidak mendukung pemformatan kode. Maaf, saya tidak tahu itu.)
Magnus Gustavsson
2
Dengan asumsi di sini FooFormadalah ModelForm, bukankah lebih mudah untuk hanya melakukan FooForm(instance=Foo.objects.get(pk=object_id))):?
Beruic
Adakah yang tahu bagaimana Anda hanya akan menampilkan bidang yang dapat diedit dengan metode ini?
alias51
22

Mengingat rilis Django 1.8 (dan formalisasi API Model _meta , saya pikir saya akan memperbarui ini dengan jawaban yang lebih baru.

Dengan asumsi model yang sama:

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

Diubah dalam Django 1.8:

Model _metaAPI selalu ada sebagai internal Django, tetapi tidak didokumentasikan dan didukung secara formal. Sebagai bagian dari upaya menjadikan API ini publik, beberapa titik masuk API yang sudah ada sedikit berubah. Panduan migrasi telah disediakan untuk membantu mengonversi kode Anda untuk menggunakan API resmi yang baru.

Dalam contoh di bawah ini, kami akan menggunakan metode formal untuk mengambil semua instance lapangan dari model melalui 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')]

Sebenarnya, telah menarik perhatian saya bahwa di atas sedikit berlebihan untuk apa yang dibutuhkan (saya setuju!). Sederhana lebih baik daripada kompleks. Saya meninggalkan referensi di atas untuk. Namun, untuk ditampilkan dalam templat, metode terbaik adalah menggunakan ModelForm dan mengirimkan instance. Anda bisa mengulangi formulir (setara dengan mengulangi setiap bidang formulir) dan menggunakan atribut label untuk mengambil verbose_name dari bidang model, dan menggunakan metode nilai untuk mengambil nilai:

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

Sekarang, kami merender bidang dalam templat:

<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
sumber
2
Akan lebih bagus jika Anda menyesuaikan jawaban untuk menunjukkan cara "> 1.8" untuk menempatkan bidang model ke dalam templat. Saat ini jawaban Anda tidak langsung menjawab pertanyaan; ini menunjukkan cara mendapatkan bidang model di shell.
Escher
@Escher - Memperbarui jawabannya! Terima kasih untuk sarannya. Beritahu saya jika saya melewatkan sesuatu / mengacaukan semuanya!
Michael B
Terpilih. Saya diedit untuk menyertakan pencetakan nilai-nilai serta nama bidang. Lihat apa yang Anda pikirkan.
Escher
Di mana Anda mencetak nilai? Saya hanya melihatnya mencetak nama dan verbose_name?
Dr. Ernie
@MichaelB Hmm. Saya belum bisa membuat "field.value" bekerja; bidang tampaknya bidang database, bukan data kolom yang sebenarnya. Saya harus menggunakan filter yang disebut getattr (objek, nama). Versi Django apa yang cocok untuk Anda?
Dr. Ernie
19

Berikut pendekatan lain menggunakan metode model. Versi ini menyelesaikan bidang daftar pilihan / pilihan, melompati bidang kosong, dan memungkinkan Anda mengecualikan bidang tertentu.

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

Kemudian di templat Anda:

{% for f in app.get_all_fields %}
  <dt>{{f.label|capfirst}}</dt>
    <dd>
      {{f.value|escape|urlize|linebreaks}}
    </dd>
{% endfor %}
shacker
sumber
3
mengapa anda membutuhkannya except User.DoesNotExist:?
Sevenearths
Saya akan cenderung menggunakan AttributeError alih-alih User.DoesNotExist - Saya tidak bisa melihat mengapa itu akan membuang User.DoesNotExist.
askvictor
Juga, mungkin lebih baik menggunakan self._meta.get_fields () karena ini secara resmi diekspos di django 1.8+. Namun, kemudian Anda berakhir dengan relasi dalam kode, yang harus Anda saring dengan memeriksa f.is_relation
askvictor
Saya telah mengedit jawaban untuk menggunakan AttributeError alih-alih User.DoesNotExist (yang merupakan sisa dari implementasi asli saya). Terima kasih. Saya menunda _meta.get_fields()sampai saya bisa mengujinya.
shacker
13

Ok, saya tahu ini agak terlambat, tetapi karena saya menemukan ini sebelum menemukan jawaban yang benar, maka mungkin orang lain.

Dari django docs :

# 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
sumber
Saya suka jawaban ini. Jika kueri Anda mengembalikan lebih dari satu catatan dan Anda hanya menginginkan yang terbaru, lakukan yang berikut ini. 1. Pastikan Anda memiliki ordering = ['-id']di class Meta:objek Anda di models.py. 2. kemudian gunakanBlog.objects.filter(name__startswith='Beatles').values()[0]
Sevenearths
Ide pintar Tetapi jika Anda sudah memiliki modelobjek, maka Anda akan menekan database lagi hanya untuk mendapatkan bidang. Ada jalan lain?
frnhr
@ user1763732 cukup periksa dokumentasi untuk QuerySet: docs.djangoproject.com/en/dev/ref/models/querysets
olofom
9

Anda dapat menggunakan values()metode a queryset, yang mengembalikan kamus. Lebih lanjut, metode ini menerima daftar bidang yang akan di-subset. The values()Metode tidak akan bekerja dengan get(), sehingga Anda harus menggunakan filter()(mengacu pada API QuerySet ).

Dalam view...

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

Dalam detail.html...

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

Untuk koleksi instance yang dikembalikan oleh filter:

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

Dalam 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 %}
pengguna3062149
sumber
Ini luar biasa, terima kasih! Saya punya pertanyaan, jika Anda bisa membantu saya; Saya meletakkan semua data objek dalam table, jadi saya perlu masing-masing keydalam th. Bagaimana saya melakukannya tanpa loop? Ambil saja instance objek dan lakukan iterasi untuk keyitu? Saat ini saya secara terpisah lulus model_to_dict(Model())untuk th, tetapi saya pikir itu adalah objek objek yang tidak perlu.
Oxwivi
Jawaban yang fantastis. Secara pribadi, saya menggunakan ini dalam tampilan daftar dan tampilan detail. Tampilan daftar sebagian besar jelas untuk diterapkan, tetapi dengan tampilan detail saya menimpa get_objecttampilan detail (rusak karena keterbatasan kode sebaris pada komentar, dan tidak merasa seperti ini sudah cukup untuk jawabannya sendiri mengingat seberapa jenuh utas ini): def get_object(self, **kwargs): obj = super().get_object(**kwargs) obj = obj.__class__.objects.filter(pk=obj.pk).values()[0] return obj
sdconrox
bagaimana Anda menambahkan obj.get_absolute_urlke daftar ini tanpa menduplikasi baris?
alias51
8

Saya menggunakan https://stackoverflow.com/a/3431104/2022534 tetapi menggantikan model_to_dict Django () dengan ini untuk dapat menangani 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

Harap dicatat bahwa saya telah menyederhanakannya sedikit dengan menghapus bagian asli yang tidak saya butuhkan. Anda mungkin ingin mengembalikannya.

Magnus Gustavsson
sumber
8

Anda dapat meminta formulir melakukan pekerjaan untuk Anda.

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

Kemudian di templat:

<table>
    {% for field in form %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>
ja
sumber
3
Metode ini (digunakan dalam a DetailView) bekerja dengan baik untuk saya. Namun, Anda mungkin ingin menggunakan field.labelbukan field.name.
David Cain
7

Harus benar-benar ada cara bawaan untuk melakukan ini. Saya menulis utilitas ini build_pretty_data_viewyang mengambil objek model dan contoh bentuk (formulir berdasarkan model Anda) dan mengembalikan a SortedDict.

Manfaat untuk solusi ini meliputi:

  • Ini menjaga ketertiban menggunakan built-in Django SortedDict.
  • Ketika mencoba untuk mendapatkan label / verbose_name, tetapi kembali ke nama bidang jika tidak ada yang ditentukan.
  • Secara opsional juga akan mengambil exclude()daftar nama bidang untuk mengecualikan bidang tertentu.
  • Jika kelas formulir Anda menyertakan a Meta: exclude(), tetapi Anda masih ingin mengembalikan nilai, lalu tambahkan bidang itu ke append()daftar opsional .

Untuk menggunakan solusi ini, pertama tambahkan file / fungsi ini di suatu tempat, lalu impor ke 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

Jadi sekarang di Anda, views.pyAnda mungkin melakukan sesuatu seperti ini

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

Sekarang di my-template.htmltemplat Anda, Anda dapat mengulangi data seperti ...

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

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

{% endfor %}

Semoga berhasil. Semoga ini bisa membantu seseorang!

Alan Viars
sumber
7

Di bawah ini milik saya, terinspirasi oleh shacker's get_all_fields . Ia mendapatkan dict dari satu contoh model, jika menemukan bidang relasi, lalu menugaskan nilai bidang sebuah dict secara rekursif.

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

Fungsi ini terutama digunakan untuk membuang contoh model ke data json:

def to_json(self):
    tree = to_dict(self, exclude=('id', 'User.password'))
    return json.dumps(tree, ensure_ascii=False)
bertanya-tanya
sumber
Kerja bagus! Sarankan menambahkan pilihan dukungan ... elif hasattr (bidang, 'pilihan'): tree [field.name] = dict (field.choices) .get (nilai, nilai)
oden
5

Alih-alih mengedit setiap model saya akan merekomendasikan untuk menulis satu tag templat yang akan mengembalikan semua bidang model yang diberikan.
Setiap objek memiliki daftar bidang ._meta.fields.
Setiap objek bidang memiliki atribut nameyang akan mengembalikan nama dan metode value_to_string()yang disertakan dengan model Anda objectakan mengembalikan nilainya.
Sisanya sesederhana seperti yang dikatakan dalam dokumentasi Django .

Berikut adalah contoh saya bagaimana templatetag ini terlihat:

    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>] %}")
penjual
sumber
4

Ya itu tidak cantik, Anda harus membuat bungkus sendiri. Lihatlah aplikasi databrowse bawaan , yang memiliki semua fungsi yang Anda butuhkan.

Dmitry Shevchenko
sumber
Saya akan mengatakan ... databrowse melakukan hal itu, meskipun saya menemukan itu menjadi aplikasi yang sama sekali tidak berguna.
buka
4

Ini dapat dianggap sebagai retasan tetapi saya telah melakukan ini sebelum menggunakan modelform_factory untuk mengubah contoh model menjadi formulir.

Kelas Form memiliki lebih banyak informasi di dalamnya yang super mudah untuk diulang dan akan melayani tujuan yang sama dengan mengorbankan overhead yang sedikit lebih banyak. Jika ukuran yang Anda tentukan relatif kecil, saya pikir dampak kinerja akan diabaikan.

Satu keuntungan di samping kenyamanan tentu saja adalah bahwa Anda dapat dengan mudah mengubah tabel menjadi datagrid yang dapat diedit di kemudian hari.

Xealot
sumber
4

Saya telah datang dengan metode berikut, yang bekerja untuk saya karena dalam setiap kasus model akan memiliki ModelForm yang terkait dengannya.

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)

Berikut ini kutipan dari template yang saya gunakan untuk tampilan khusus ini:

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

Yang menyenangkan tentang metode ini adalah bahwa saya dapat memilih berdasarkan template-by-template urutan di mana saya ingin menampilkan label bidang, menggunakan tuple yang diteruskan ke GetModelData dan menentukan nama bidang. Ini juga memungkinkan saya untuk mengecualikan bidang tertentu (misalnya kunci asing Pengguna) karena hanya nama bidang yang dilewatkan melalui tuple yang dimasukkan ke dalam kamus akhir.

Saya tidak akan menerima ini sebagai jawaban karena saya yakin seseorang dapat menemukan sesuatu yang lebih "Djangonic" :-)

Pembaruan: Saya memilih ini sebagai jawaban akhir karena ini adalah yang paling sederhana dari yang diberikan yang melakukan apa yang saya butuhkan. Terima kasih kepada semua orang yang berkontribusi jawaban.

Wayne Koort
sumber
3

Solusi Django 1.7 untuk saya:

Ada variabel yang tepat untuk pertanyaan, tetapi Anda harus dapat membedah contoh ini

Kuncinya di sini adalah cukup banyak menggunakan .__dict__model
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)

templat :

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

di templat saya menggunakan filter untuk mengakses bidang di 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
sumber
2

Saya menggunakan ini, 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
sumber
2

Pendekatan ini menunjukkan bagaimana menggunakan kelas seperti ModelForm django dan tag templat seperti {{form.as_table}}, tetapi memiliki semua tabel yang terlihat seperti output data, bukan formulir.

Langkah pertama adalah untuk subclass widget 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))

Kemudian saya subclassed django's ModelForm untuk menukar keluar widget default untuk versi hanya baca:

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" 

Itulah satu-satunya widget yang saya butuhkan. Tetapi seharusnya tidak sulit untuk memperluas ide ini ke widget lain.

Membuang
sumber
1

Hanya edit dari @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

Biarkan Django menangani semua bidang lain selain bidang terkait. Saya merasa itu lebih stabil

Shivam Goyal
sumber
0

Saya baru saja menguji sesuatu seperti ini di shell dan tampaknya melakukan tugasnya:

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

Perhatikan bahwa jika Anda ingin representasi str () untuk objek asing, Anda harus mendefinisikannya dalam metode str mereka . Dari itu Anda memiliki nilai untuk objek. Kemudian Anda dapat membuat semacam template atau apa pun.

Adam
sumber
0

Django> = 2.0

Tambahkan get_fields()ke Anda 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]

Kemudian panggil seperti object.get_fieldspada Anda template.html:

<table>
    {% for label, value in object.get_fields %}
        <tr>
            <td>{{ label }}</td>
            <td>{{ value }}</td>
        </tr>
    {% endfor %}
</table>
JV conseil
sumber
-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
sumber
1
Halo dan selamat datang di SO. Tolong jangan posting jawaban hanya kode. Juga pertanyaan ini sudah memiliki jawaban yang diterima, pemformatan kode Anda tidak benar, Anda menggunakan atribut html yang sudah tidak berlaku dan yang paling penting: Anda tidak menjelaskan bagaimana kode Anda memberikan solusi yang lebih baik daripada yang diterima.
Frieder