Template Django: versi verbose pilihan

127

Saya punya model:

from django.db import models

CHOICES = (
    ('s', 'Glorious spam'),
    ('e', 'Fabulous eggs'),
)

class MealOrder(models.Model):
    meal = models.CharField(max_length=8, choices=CHOICES)

Saya punya formulir:

from django.forms import ModelForm

class MealOrderForm(ModelForm):
    class Meta:
        model = MealOrder

Dan saya ingin menggunakan formtools.preview. Template default mencetak versi pendek dari pilihan ('e' bukannya 'Fabulous eggs'), karena itu digunakan

{% for field in form %}
<tr>
<th>{{ field.label }}:</th>
<td>{{ field.data }}</td>
</tr>
{% endfor %}.

Saya ingin template yang umum seperti yang disebutkan, tetapi mencetak 'Telur yang luar biasa' sebagai gantinya.

[Karena saya ragu di mana pertanyaan sebenarnya, saya berani untuk kita semua :)]

Saya tahu cara mendapatkan versi verbose dari suatu pilihan dengan cara yang jelek:

{{ form.meal.field.choices.1.1 }}

Rasa sakit yang sebenarnya adalah saya harus mendapatkan pilihan yang dipilih, dan satu-satunya cara datang ke pikiran saya adalah beralih melalui pilihan dan memeriksa {% ifequals currentChoice.0 choiceField.data %}, yang bahkan lebih buruk.

Bisakah itu dilakukan dengan mudah? Atau perlu beberapa pemrograman template-tag? Bukankah seharusnya itu sudah tersedia di Django?

Artur Gajowy
sumber

Jawaban:

258

Di templat Django Anda dapat menggunakan metode " get_FOO_display()", yang akan mengembalikan alias yang bisa dibaca untuk bidang, di mana 'FOO' adalah nama bidang.

Catatan: jika FormPreviewtemplat standar tidak menggunakannya, maka Anda selalu dapat menyediakan templat sendiri untuk formulir itu, yang akan berisi sesuatu seperti {{ form.get_meal_display }}.

rampok
sumber
1
ya saya tahu. Ini tidak umum (universal), kecuali - kecuali jika Anda tahu cara untuk mengulangi dalam templat atas semua metode get_FOO_display objek model :) Saya agak terlalu malas untuk menulis templat non-generik;) Selain itu, dokumen mengatakan itu metode contoh model. Oleh karena itu harus menjadi bentuk model yang terikat pada objek yang ada yang tidak terjadi dan juga tidak umum.
Artur Gajowy
2
Perhatikan bahwa penggunaan ini tidak terbatas pada tampilan, get_FOO_display () adalah metode pada objek model itu sendiri sehingga Anda dapat menggunakannya dalam kode model juga! Misalnya, dalam __unicode __ () itu sangat berguna
Bogatyr
51

Solusi terbaik untuk masalah Anda adalah menggunakan fungsi pembantu. Jika pilihan disimpan dalam PILIHAN variabel dan bidang model menyimpan pilihan yang dipilih adalah ' pilihan ' maka Anda dapat langsung menggunakan

 {{ x.get_choices_display }}

di templat Anda. Di sini, x adalah contoh model. Semoga ini bisa membantu.

Reema
sumber
3
Mengapa Anda menjawab seperti ini 2 tahun setelah jawaban yang bermanfaat sudah ada? Dan siapa yang akan memilihnya? Jawabannya sama dengan @roberto hanya 2 tahun kemudian ....
boatcoder
15
@ Mark0978 alasan untuk meningkatkan jawaban ini adalah karena (bagi saya) lebih jelas untuk mengikuti maka jawaban "terpilih sebagai". YMMV.
Nir Levy
49

Saya minta maaf jika jawaban ini berlebihan dengan yang tercantum di atas, tetapi tampaknya jawaban ini belum ditawarkan, dan tampaknya cukup bersih. Inilah cara saya memecahkan ini:

from django.db import models

class Scoop(models.Model):
    FLAVOR_CHOICES = [
        ('c', 'Chocolate'),
        ('v', 'Vanilla'),
    ]

    flavor = models.CharField(choices=FLAVOR_CHOICES)

    def flavor_verbose(self):
        return dict(Scoop.FLAVOR_CHOCIES)[self.flavor]

Tampilan saya meneruskan Scoop ke templat (note: not Scoop.values ​​()), dan templat tersebut berisi:

{{ scoop.flavor_verbose }}
Dan Kerchner
sumber
10

Mendasarkan pada jawaban Nuh, inilah versi yang kebal terhadap ladang tanpa pilihan:

#annoyances/templatetags/data_verbose.py
from django import template

register = template.Library()

@register.filter
def data_verbose(boundField):
    """
    Returns field's data or it's verbose version 
    for a field with choices defined.

    Usage::

        {% load data_verbose %}
        {{form.some_field|data_verbose}}
    """
    data = boundField.data
    field = boundField.field
    return hasattr(field, 'choices') and dict(field.choices).get(data,'') or data

Saya tidak yakin apakah boleh menggunakan filter untuk tujuan seperti itu. Jika ada yang punya solusi yang lebih baik, saya akan senang melihatnya :) Terima kasih Nuh!

Artur Gajowy
sumber
+1 untuk menyebutkan path Anda # gangguan / templatetags / ... LOL ... Saya menggunakan get_FOO_display (), yang disebutkan di bagian bawah form docs.
fmalina
ide bagus dengan penggunaan hasattr pada pilihan!
oden
7

Kami dapat memperluas solusi filter oleh Nuh agar lebih universal dalam menangani data dan tipe bidang:

<table>
{% for item in query %}
    <tr>
        {% for field in fields %}
            <td>{{item|human_readable:field}}</td>
        {% endfor %}
    </tr>
{% endfor %}
</table>

Berikut kodenya:

#app_name/templatetags/custom_tags.py
def human_readable(value, arg):
    if hasattr(value, 'get_' + str(arg) + '_display'):
        return getattr(value, 'get_%s_display' % arg)()
    elif hasattr(value, str(arg)):
        if callable(getattr(value, str(arg))):
            return getattr(value, arg)()
        else:
            return getattr(value, arg)
   else:
       try:
           return value[arg]
       except KeyError:
           return settings.TEMPLATE_STRING_IF_INVALID
register.filter('human_readable', human_readable)
Ivan Kharlamov
sumber
Kelihatannya sangat universal :) Tidak bisa memastikan, karena saya tidak melakukan terlalu banyak Python atau Django sejak saat itu. Ini cukup menyedihkan, meskipun, masih membutuhkan filter pihak ke-3 (tidak termasuk dalam Django) (jika tidak, Anda akan memberi tahu kami, Ivan, bukan
?;
@ ArturGajowy Ya, sampai hari ini tidak ada fitur default di Django. Saya sudah mengusulkannya, siapa tahu, mungkin itu akan disetujui .
Ivan Kharlamov
SEMPURNA! BEKERJA SEPERTI PEKERJAAN! ROX TEMPLATE FILTER KUSTOM! TERIMA KASIH! :-)
CeDeROM
5

Saya tidak berpikir ada cara bawaan untuk melakukan itu. Namun, filter dapat melakukan trik:

@register.filter(name='display')
def display_value(bf):
    """Returns the display value of a BoundField"""
    return dict(bf.field.choices).get(bf.data, '')

Maka Anda dapat melakukan:

{% for field in form %}
    <tr>
        <th>{{ field.label }}:</th>
        <td>{{ field.data|display }}</td>
    </tr>
{% endfor %}
Noah Medling
sumber
3

Tambahkan ke models.py Anda satu fungsi sederhana:

def get_display(key, list):
    d = dict(list)
    if key in d:
        return d[key]
    return None

Sekarang, Anda bisa mendapatkan nilai verbose dari bidang pilihan seperti itu:

class MealOrder(models.Model):
    meal = models.CharField(max_length=8, choices=CHOICES)

    def meal_verbose(self):
        return get_display(self.meal, CHOICES)    

Upd .: Saya tidak yakin, apakah solusi itu "pythonic" dan "django-way" cukup atau tidak, tetapi berhasil. :)

Igor Pomaranskiy
sumber
0

Anda memiliki Model.get_FOO_display () di mana FOO adalah nama bidang yang memiliki pilihan.

Dalam templat Anda, lakukan ini:

{{ scoop.get_flavor_display }}
Mohamed OULD EL KORY
sumber