36

I would like to get the named values of a choices field for a choice that is not currently selected. Is this possible?

For instance: models.py

FILE_STATUS_CHOICES = (
    ('P', 'Pending'),
    ('A', 'Approved'),
    ('R', 'Rejected'),
)

class File(models.Model):
    status = models.CharField(max_length=1, default='P', choices=FILE_STATUS_CHOICES)

views.py

f = File()
f.status = 'P'
f.save()

old_value = f.status

print f.get_status_display()
> Pending

f.status = 'A'
f.save()

new_value = f.status

print f.get_status_display()
> Approved

How can I get the old display value from the 'P' to 'Pending?' I may be able to do so by creating a form in the view and accessing its dictionary of values/labels. Is this the best/only approach?

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
Furbeenator
  • 8,106
  • 4
  • 46
  • 54
  • once you do `f.save()`, the value is overwritten. Unless you have the object in memory, you cannot retrieve the old value – karthikr Sep 09 '13 at 20:05
  • Thanks, @karthikr, but can I get the display value given the old value? Can I get access to the tuple assigned to choices in the models layer? I suppose if I knew the object, I could get the choices parameter of the field and get the constant tuple from the models.py. Curious if there's any other shortcut to do so. – Furbeenator Sep 09 '13 at 20:07
  • I don't believe there are any helper methods for that. [This question](http://stackoverflow.com/q/1105638) outlines some of the ways you can do it yourself. – dgel Sep 09 '13 at 20:08
  • Thanks, dgel. I am using the method outlined by @alecxe to get the tuple from the instance's ._meta field and its flatchoices attribute. – Furbeenator Sep 09 '13 at 21:48

2 Answers2

59

This is pretty much ok to import your choice mapping FILE_STATUS_CHOICES from models and use it to get Pending by P:

from my_app.models import FILE_STATUS_CHOICES

print dict(FILE_STATUS_CHOICES).get('P')

get_FIELD_display() method on your model is doing essentially the same thing:

def _get_FIELD_display(self, field):
    value = getattr(self, field.attname)
    return force_text(dict(field.flatchoices).get(value, value), strings_only=True) 

And, since there is a flatchoices field on the model field, you can use it with the help of _meta and get_field_by_name() method:

choices = f._meta.get_field_by_name('name')[0].flatchoices
print dict(choices).get('P')

where f is your model instance.

Also see:

Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • The flatchoices is part of a form element? When I do it against the actual file object, status returns the single-character unicode 'P' which doesn't seem to have a flatchoices property. If that's the case, I can set up a form and then get the flatchoices from there, right? – Furbeenator Sep 09 '13 at 20:25
  • @Furbeenator yeah, right, sorry, should get the actual field first: check the updated answer. – alecxe Sep 09 '13 at 20:30
  • Awesome, I figured this out and then saw your update. Thanks a bunch, this gets me out of a jam! – Furbeenator Sep 09 '13 at 20:37
  • How do you access them in a template? – pitchblack408 May 05 '17 at 07:56
0

I recommend using Choice from django-model-utils: https://django-model-utils.readthedocs.io/en/latest/utilities.html#choices.

I use it in every my models if I need choice field. See examples, it has excellent options.

Ibrohim Ermatov
  • 2,169
  • 19
  • 13