2009-07-13 1 views
79

선택 옵션이있는 모델 필드를 사용하면 사람이 읽을 수있는 이름과 관련된 일부 마법 값을 갖는 경향이 있습니다. 장고에 값 대신 사람이 읽을 수있는 이름으로 필드를 설정하는 편리한 방법이 있습니까? Django IntegerField를 choices = ...로 설정합니다.

이 모델 고려 :

class Thing(models.Model): 
    PRIORITIES = (
    (0, 'Low'), 
    (1, 'Normal'), 
    (2, 'High'), 
) 

    priority = models.IntegerField(default=0, choices=PRIORITIES) 

우리는 것 인스턴스가 어떤 점에서하고 우리는 우선 순위를 설정합니다. 분명히 할 수있다.

thing.priority = 1 

그러나 이것은 당신이 우선 순위의 값 - 이름 매핑을 암기하도록 만든다.

thing.priority = 'Normal' # Throws ValueError on .save() 

은 현재 내가이 바보 해결 방법이 있습니다 :이 작동하지 않습니다

thing.priority = dict((key,value) for (value,key) in Thing.PRIORITIES)['Normal'] 

을하지만 어설픈입니다. 이 시나리오가 얼마나 흔한 일인가를 생각해 보면 나는 누구에게 더 나은 솔루션이 있는지 궁금해하고 있었다. 내가 간과 한 선택 이름으로 필드를 설정하기위한 필드 방법이 있습니까?

답변

130

seen here으로 처리하십시오. 그런 다음 적절한 정수를 나타내는 단어를 사용할 수 있습니다. 그래서 같이

:

LOW = 0 
NORMAL = 1 
HIGH = 2 
STATUS_CHOICES = (
    (LOW, 'Low'), 
    (NORMAL, 'Normal'), 
    (HIGH, 'High'), 
) 

그런 다음 그들은 DB의 정수는 여전히.

사용은

+1

그 주제에 관해 자세히 설명하는 블로그입니다. 너무 Google에 찾기 너무 어렵습니다. –

+1

FWIW, 리터럴 문자열 (양식, 사용자 입력 등)에서 설정해야하는 경우 thing.priority = getattr (thing, strvalue.upper())를 사용하면됩니다. – mrooney

+1

블로그의 캡슐화 섹션을 정말 좋아합니다. –

1

숫자를 원하는 사람이 읽을 수있는 값으로 바꾸기 만하면됩니다. 예 :

PRIORITIES = (
('LOW', 'Low'), 
('NORMAL', 'Normal'), 
('HIGH', 'High'), 
) 

이렇게하면 사람이 읽을 수있게되지만 직접 주문을 정의해야합니다.

아마 한번에 역방향 조회 DICT를 설정 싶지만, 내가하지 않았다면 난 그냥 사용하는 것
7

: 즉석에서 DICT를 구축보다 간단 보인다

thing.priority = next(value for value, name in Thing.PRIORITIES 
         if name=='Normal') 

단지 다시 멀리 던지기 ;-).

+0

네, dict을 던지기가 바보 같아서 이제는 말하겠습니다. :) –

4

thing.priority = Thing.NORMAL가 여기에 내가 몇 분 전에 당신이 원하는 것을 생각 쓴 필드 유형의 것이다. 그것의 생성자는 'choices'를 필요로합니다.이 튜플은 IntegerField에 대한 choices 옵션과 동일한 형식의 2 튜플이거나 간단한 이름 목록 (즉, ChoiceField (('Low', 'Normal' '높음'), 기본값 = '낮음')). 클래스는 문자열을 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] 
+1

그건 나쁘지 않은 앨런입니다. 감사! –

3
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) 
2

나는 상수 정의하는 방법을 주셔서 감사하지만 난 Enum 유형은 지금까지 가장이 작업 믿습니다. 코드를보다 읽기 쉬운 상태로 유지하면서 항목에 정수와 문자열을 동시에 나타낼 수 있습니다.

열거 형은 3.4 버전의 Python에 소개되었습니다. 더 낮은 버전 (예 : v2.x)을 사용하는 경우 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++' 

이것은 거의 모든 것입니다. 문자열을 대표

MyModel.objects.filter(language=int(Language.Ruby)) 
# or if you don't prefer `__int__` method.. 
MyModel.objects.filter(language=Language.Ruby.value) 

는 또한 :

from django.db import models 
from myapp.fields import Language 

class MyModel(models.Model): 
    language = models.IntegerField(choices=Language.choices(), default=int(Language.Python)) 
    # ... 

쿼리 당신이 추측 수 있으므로 케이크에 장식입니다 : 당신은 당신의 자신의 정의를 작성하고 같은 모델 정의에서 사용하는 ChoiceEnum을 상속 할 수 있습니다 쉽게 만들어 :

# 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() 
1

내 대답은 매우 늦게이고 명백한에 요즘-장고 전문가를 보일 수 있지만, 여기에 토지 누구에, 나는 최근에 아주 우아한 해결책을 발견 장고 모델-utils를 제기 : https://django-model-utils.readthedocs.io/en/latest/utilities.html#choices

이 패키지는 세 튜플과 선택을 정의 할 수 있습니다 :

  • 첫 번째 항목은 데이터베이스 값
  • 이다 번째 항목은 코드를 읽을 수있다 값
  • 세 번째 항목은 사람이 읽을 수있는 값 그래서 여기

것입니다 당신은 무엇을 할 수 있는지 :

from model_utils import Choices 

class Thing(models.Model): 
    PRIORITIES = Choices(
     (0, 'low', 'Low'), 
     (1, 'normal', 'Normal'), 
     (2, 'high', 'High'), 
    ) 

    priority = models.IntegerField(default=PRIORITIES.normal, choices=PRIORITIES) 

thing.priority = getattr(Thing.PRIORITIES.Normal) 

이 방법 : 난 야생 콘텐츠를 스크랩 및 표준화에 저장하고 있습니다 때문에

  • 당신은 실제로 내 경우에는 (당신의 필드의 값을 선택하기 위해 사람이 읽을 수있는 값을 사용할 수 있습니다, 그것은 유용 깨끗한 값이
  • 당신이 할 수있는 아무것도 아닌 DRY이 데이터베이스에 저장됩니다
  • 방법))

는 :)

을 즐길 수
+1

django-model-utils를 가져올 때 완전히 유효합니다. 가독성을 높이기위한 하나의 작은 제안. 기본 매개 변수 도트 표기법을 사용하십시오 :'priority = models.IntegerField (default = PRIORITIES.Low, choices = PRIORITIES)'(우선 순위 할당은 그렇게하기 위해 Thing 클래스 안에 들여 쓰기되어야 함). 또한 클래스를 참조하는 것이 아니라 매개 변수 대신 소문자 단어/문자를 파이썬 식별자로 사용하는 것을 고려하십시오 (선택 사항은'(0, '낮음', '낮음') 등이됩니다) . – Kim

+0

감사합니다. @Kim, 대답 수정 됨;) –