简体   繁体   中英

Django template: force choices on field and print display value with get_FOO_display()

I want a model with 5 choices, but I cannot enforce them and display the display value in template. I am using CharField(choice=..) instead of ChoiceField or TypeChoiceField as in the docs . I tried the solutions here but they don't work for me (see below).

model.py:

class Language(models.Model):
    language = models.CharField(max_length=20,blank=False)
    ILR_scale = (
        (5, 'Native'),
        (4, 'Full professional proficiency'),
        (3, 'Professional working proficiency'),
        (2, 'Limited professional proficiency'),
        (1, 'Elementary professional proficiency')
        )
    level = models.CharField(help_text='Choice between 1 and 5', default=5, max_length=25, choices=ILR_scale)
    def level_verbose(self):
        return dict(Language.ILR_scale)[self.level]
    class Meta:
        ordering = ['level','id']
    def __unicode__(self):
        return ''.join([self.language, '-', self.level])

view.py

..
def index(request):
language = Language.objects.all()
..

mytemplate.html

<div class="subheading strong-underlined mb-3 my-3">
      Languages
      </div>
      {% regroup language|dictsortreversed:"level" by level as level_list %}
      <ul>
        {% for lan_list in level_list %}
        <li>
          {% for lan in lan_list.list %}
          <strong>{{ lan.language }}</strong>: {{ lan.level_verbose }}{%if not forloop.last%},{%endif%}
          {% endfor %}
        </li>
        {% endfor %}
      </ul>

From shell:

python3 manage.py shell
from resume.models import Language
l1=Language.objects.create(language='English',level=4)
l1.save()
l1.get_level_display()   #This is good
Out[20]: 'Full professional proficiency'

As soon as I create a Language instance from shell I cannot load the site. It fails at line 0 of the template with Exception Type: KeyError, Exception Value: '4', Exception Location: /models.py in level_verbose, line 175 (which is the return line of the level_verbose method).

Also, I was expecting a validation error here from shell:

l1.level='asdasd'
l1.save()     #Why can I save this instance with this level?

And I can also save a shown above when using ChoiceField, meaning that I do not understand what that field is used for.

How to force instances to take field values within choices, and display the display value in templates?

Well this is the common issue even when I started with django. So first let's look at django's feature that you can do it like below (Note: your choice case's value are going to be store as integer so you should use models.IntegerField instead of models.CharField ):

As you can see in documentation FOO is the field name of your model. in your case it is level so when you want to access corresponding choice value in shell or view you can call method with model instance as below as you have already mentioned:

`l1.get_level_display()`

but when you want to access it in template file you need to write it like below:

{{ l1.get_level_display }}
  • Now let's look at your method level_verbose() if you see quite again your model is a class and level_verbose() is the method you have created you can access self.ILR_scale directly just as you have used self.level

the main catch in you create dictionary of ILR_scale it's keys are Integer values (ie 1, 2, 3, 4, 5) but you have used CharField() to store the level values which returns you string values (ie '1', '2', '3', '4' or '5') and in python dictionary key 1 and '1' are both different one is integer and other is string. So you may change your model field to models.IntegerField() or you can access the keys like

dict(self.ILR_scal)[int(self.level)]

You can also use models.CharField but you have to set field option choices to your tuples.

For exapmle:

FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
LEVELS = (
    (FRESHMAN, 'Freshman'),
    (SOPHOMORE, 'Sophomore'),
    (JUNIOR, 'Junior'),
    (SENIOR, 'Senior'),
)
level = models.CharField(
    max_length=2,
    choices=LEVELS,
    default=FRESHMAN,
)

Then in your template you can use get_FOO_display() for example: {{l1.get_level_display}}

See more in docs

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM