Do as seen here. Then you can use a word that represents the proper integer.
Like so:
LOW = 0
NORMAL = 1
HIGH = 2
STATUS_CHOICES = (
(LOW, 'Low'),
(NORMAL, 'Normal'),
(HIGH, 'High'),
)
Then they are still integers in the DB.
Usage would be thing.priority = Thing.NORMAL
As of Django 3.0, you can use:
class ThingPriority(models.IntegerChoices):
LOW = 0, 'Low'
NORMAL = 1, 'Normal'
HIGH = 2, 'High'
class Thing(models.Model):
priority = models.IntegerField(default=ThingPriority.LOW, choices=ThingPriority.choices)
# then in your code
thing = get_my_thing()
thing.priority = ThingPriority.HIGH
I'd probably set up the reverse-lookup dict once and for all, but if I hadn't I'd just use:
thing.priority = next(value for value, name in Thing.PRIORITIES
if name=='Normal')
which seems simpler than building the dict on the fly just to toss it away again;-).
Here's a field type I wrote a few minutes ago that I think does what you want. Its constructor requires an argument 'choices', which may be either a tuple of 2-tuples in the same format as the choices option to IntegerField, or instead a simple list of names (ie ChoiceField(('Low', 'Normal', 'High'), default='Low') ). The class takes care of the mapping from string to int for you, you never see the int.
class ChoiceField(models.IntegerField):
def __init__(self, choices, **kwargs):
if not hasattr(choices[0],'__iter__'):
choices = zip(range(len(choices)), choices)
self.val2choice = dict(choices)
self.choice2val = dict((v,k) for k,v in choices)
kwargs['choices'] = choices
super(models.IntegerField, self).__init__(**kwargs)
def to_python(self, value):
return self.val2choice[value]
def get_db_prep_value(self, choice):
return self.choice2val[choice]
I appreciate the constant defining way but I believe Enum type is far best for this task. They can represent integer and a string for an item in the same time, while keeping your code more readable.
Enums were introduced to Python in version 3.4. If you are using any lower (such as v2.x) you can still have it by installing the backported package: pip install enum34
.
# myapp/fields.py
from enum import Enum
class ChoiceEnum(Enum):
@classmethod
def choices(cls):
choices = list()
# Loop thru defined enums
for item in cls:
choices.append((item.value, item.name))
# return as tuple
return tuple(choices)
def __str__(self):
return self.name
def __int__(self):
return self.value
class Language(ChoiceEnum):
Python = 1
Ruby = 2
Java = 3
PHP = 4
Cpp = 5
# Uh oh
Language.Cpp._name_ = 'C++'
This is pretty much all. You can inherit the ChoiceEnum
to create your own definitions and use them in a model definition like:
from django.db import models
from myapp.fields import Language
class MyModel(models.Model):
language = models.IntegerField(choices=Language.choices(), default=int(Language.Python))
# ...
Querying is icing on the cake as you may guess:
MyModel.objects.filter(language=int(Language.Ruby))
# or if you don't prefer `__int__` method..
MyModel.objects.filter(language=Language.Ruby.value)
Representing them in string is also made easy:
# Get the enum item
lang = Language(some_instance.language)
print(str(lang))
# or if you don't prefer `__str__` method..
print(lang.name)
# Same as get_FOO_display
lang.name == some_instance.get_language_display()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With