선택 옵션이있는 모델 필드가 있으면 사람이 읽을 수있는 이름과 관련된 마법 값이있는 경향이 있습니다. Django에 값 대신 사람이 읽을 수있는 이름으로 이러한 필드를 설정하는 편리한 방법이 있습니까?
이 모델을 고려하십시오.
class Thing(models.Model):
PRIORITIES = (
(0, 'Low'),
(1, 'Normal'),
(2, 'High'),
)
priority = models.IntegerField(default=0, choices=PRIORITIES)
어느 시점에서 Thing 인스턴스가 있고 우선 순위를 설정하려고합니다. 분명히 할 수 있습니다.
thing.priority = 1
그러나 그것은 당신이 PRIORITIES의 Value-Name 매핑을 기억하도록 강요합니다. 작동하지 않습니다.
thing.priority = 'Normal' # Throws ValueError on .save()
현재 다음과 같은 어리석은 해결 방법이 있습니다.
thing.priority = dict((key,value) for (value,key) in Thing.PRIORITIES)['Normal']
그러나 그것은 투박합니다. 이 시나리오가 얼마나 흔한지를 감안할 때 누구에게 더 나은 솔루션이 있는지 궁금합니다. 내가 완전히 간과 한 선택 이름으로 필드를 설정하는 필드 방법이 있습니까?
답변
여기 에서 본대로하십시오 . 그런 다음 적절한 정수를 나타내는 단어를 사용할 수 있습니다.
이렇게 :
LOW = 0
NORMAL = 1
HIGH = 2
STATUS_CHOICES = (
(LOW, 'Low'),
(NORMAL, 'Normal'),
(HIGH, 'High'),
)
그런 다음 그들은 여전히 DB의 정수입니다.
사용법은 thing.priority = Thing.NORMAL
답변
나는 아마도 역방향 조회 딕셔너리를 한 번에 설정했을 것입니다.하지만 그렇지 않았다면 그냥 사용했을 것입니다.
thing.priority = next(value for value, name in Thing.PRIORITIES
if name=='Normal')
즉석에서 딕셔너리를 구축하는 것보다 더 간단 해 보입니다.
답변
여기에 내가 몇 분 전에 작성한 필드 유형이 있으며 원하는 것을 수행한다고 생각합니다. 생성자는 IntegerField에 대한 선택 옵션과 동일한 형식의 2- 튜플 튜플이거나 대신 간단한 이름 목록 (예 : ChoiceField (( ‘Low’, ‘Normal’, ‘High’), 기본값 = ‘Low’)). 클래스는 문자열에서 int 로의 매핑을 처리하므로 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]
답변
지속적인 정의 방법에 감사하지만 Enum 유형 이이 작업에 가장 적합 하다고 생각 합니다. 코드를 더 읽기 쉽게 유지하면서 동시에 항목의 정수와 문자열을 나타낼 수 있습니다.
열거 형은 버전 3.4에서 Python에 도입되었습니다. 당신이 어떤 낮은 (버전 2.x 등)를 사용하는 경우 여전히 설치하여 수 백 포트 패키지 : 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++'
이것은 거의 모든 것입니다. 를 상속 ChoiceEnum
하여 고유 한 정의를 만들고 다음과 같은 모델 정의에서 사용할 수 있습니다.
from django.db import models
from myapp.fields import Language
class MyModel(models.Model):
language = models.IntegerField(choices=Language.choices(), default=int(Language.Python))
# ...
추측 할 수 있듯이 쿼리는 케이크를 장식합니다.
MyModel.objects.filter(language=int(Language.Ruby))
# or if you don't prefer `__int__` method..
MyModel.objects.filter(language=Language.Ruby.value)
문자열로 표현하는 것도 쉽습니다.
# 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()
답변
class Sequence(object):
def __init__(self, func, *opts):
keys = func(len(opts))
self.attrs = dict(zip([t[0] for t in opts], keys))
self.choices = zip(keys, [t[1] for t in opts])
self.labels = dict(self.choices)
def __getattr__(self, a):
return self.attrs[a]
def __getitem__(self, k):
return self.labels[k]
def __len__(self):
return len(self.choices)
def __iter__(self):
return iter(self.choices)
def __deepcopy__(self, memo):
return self
class Enum(Sequence):
def __init__(self, *opts):
return super(Enum, self).__init__(range, *opts)
class Flags(Sequence):
def __init__(self, *opts):
return super(Flags, self).__init__(lambda l: [1<<i for i in xrange(l)], *opts)
다음과 같이 사용하십시오.
Priorities = Enum(
('LOW', 'Low'),
('NORMAL', 'Normal'),
('HIGH', 'High')
)
priority = models.IntegerField(default=Priorities.LOW, choices=Priorities)
답변
Django 3.0부터 다음을 사용할 수 있습니다.
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
답변
원하는 사람이 읽을 수있는 값으로 숫자를 바꾸면됩니다. 이와 같이 :
PRIORITIES = (
('LOW', 'Low'),
('NORMAL', 'Normal'),
('HIGH', 'High'),
)
이렇게하면 사람이 읽을 수 있지만 자신 만의 순서를 정의해야합니다.