42

I have a field in my module that is used to hold the status of the object. So far I have used:

ORDER_STATUS = ((0, 'Started'), (1, 'Done'), (2, 'Error'))
status = models.SmallIntegerField(choices=ORDER_STATUS)

Its easy to convert one way:

def status_str(self): return ORDER_STATUS[self.status][1]

The problem is when updating. I find myself having code like this:

order.status = 2 # Error Status

Which is quite awful and gets really hard to synchronize. I guess a solution would be something similar to C's enum{}. Or perhaps there is a whole different way to tackle this problem ?

Thanks

Boris
  • 3,163
  • 5
  • 37
  • 46
  • possible duplicate of [Set Django IntegerField by choices=... name](http://stackoverflow.com/questions/1117564/set-django-integerfield-by-choices-name) – Felix Kling Mar 29 '12 at 16:59

7 Answers7

49

Maybe this question helps you: Set Django IntegerField by choices=… name.
I quote from the accepted answer (with adjustments ;)):
Put this into your class (STATUS_CHOICES will be the list that is handed to the choices option of the field):

PENDING = 0
DONE = 1
STATUS_CHOICES = (
    (PENDING, 'Pending'),
    (DONE, 'Done'),
)

Then you can do order.status = Order.DONE.


Note that you don't have to implement an own method to retrieve the (readable) value, Django provides the method get_status_display itself.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 1
    Updated link: https://docs.djangoproject.com/en/1.6/ref/models/instances/#django.db.models.Model.get_FOO_display – steps Mar 13 '14 at 11:39
  • 1
    Link that works in 2016: https://docs.djangoproject.com/en/1.9/topics/db/models/#field-options – Mr. Napik Mar 24 '16 at 16:56
  • Link that works in 2020: https://docs.djangoproject.com/en/3.1/topics/db/models/#field-options – David Dahan Sep 04 '20 at 10:54
11

what I usually do for this situation is:

models.py

from static import ORDER_STATUS    
status = models.PositiveSmallIntegerField(choices=ORDER_STATUS)

static.py

ORDER_STATUS = ((0, 'Started'), (1, 'Done'), (2, 'Error'))
ORDER_STATUS_DICT = dict((v, k) for k, v in ORDER_STATUS)

Now you can do:

from static import ORDER_STATUS_DICT
order.status = ORDER_STATUS_DICT['Error']
Dan Dyer
  • 53,737
  • 19
  • 129
  • 165
fijter
  • 17,607
  • 2
  • 25
  • 28
  • What I love about this one is that it's super clean and without dependancies. Most libs or utils give you the same functional end result as this solution but with the risk of breakage. – GerardJP Dec 06 '20 at 14:23
8

This is a very late answer, however for completeness I should mention that django-model-utils already contains a StatusField and even better a StatusModel. I am using it everywhere I need to have a status.

Serafeim
  • 14,962
  • 14
  • 91
  • 133
2

you can try enum package: http://pypi.python.org/pypi/enum/

zaca
  • 629
  • 7
  • 9
  • I don't see how persisting of these things into the Django database would work. Can this answer be extended on that topic? – jonalv Mar 22 '16 at 10:30
2

You don't need your status_str method - Django automatically provides a get_status_display() which does exactly the same thing.

To reverse, you could use this:

def set_order_status(self, val):
    status_dict = dict(ORDER_STATUS)
    self.status = status_dict[val][0]

Now you can do:

order.set_order_status('Done')
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Nice idea, but _**wrong**_. Using `ORDER_STATUS` as defined by the asker would set `status_dict` to `{0: 'Started', 1: 'Done', 2: 'Error'}`. Note that the dictionary key are numbers, not strings. Calling the method as you suggests results in the error, `KeyError: 'Done'`. Change `status_dict` to `status_dict = {x[1]:x[0] for x in ORDER_STATUS}` and `self.status` to `self.status = status_dict[val]`. – Mr. Lance E Sloan Feb 24 '16 at 13:18
0

Maybe stick a method on the model, like:

def status_code(self, text):
    return [n for (n, t) in self.ORDER_STATUS if t == text][0]

Then you’d do:

order.status = order.status_code('Error')
Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270
0

don't do all those things.just make change in views.py as follows

  context['value'] = Model_name.objects.order_by('-choice')

where

   choice = ('pending','solved','closed')
raghu
  • 384
  • 7
  • 10