programing

장고 모델에서 열거형을 선택 필드로 사용하려면 어떻게 해야 합니까?

muds 2023. 5. 6. 16:48
반응형

장고 모델에서 열거형을 선택 필드로 사용하려면 어떻게 해야 합니까?

두 개의 필드를 선택 필드로 하는 모델 클래스가 있으므로 이러한 선택 항목을 채우기 위해 아래 나열된 열거형을 사용합니다.

#models.py
class Transaction(models.Model):
    transaction_status = models.CharField(max_length=255, choices=TransactionStatus.choices())
    transaction_type = models.CharField(max_length=255, choices=TransactionType.choices())

#enums.py
class TransactionType(Enum):

    IN = "IN",
    OUT = "OUT"

    @classmethod
    def choices(cls):
        print(tuple((i.name, i.value) for i in cls))
        return tuple((i.name, i.value) for i in cls)

class TransactionStatus(Enum):

    INITIATED = "INITIATED",
    PENDING = "PENDING",
    COMPLETED = "COMPLETED",
    FAILED = "FAILED"
    ERROR = "ERROR"

    @classmethod
    def choices(cls):
        print(tuple((i.name, i.value) for i in cls))
        return tuple((i.name, i.value) for i in cls)

그러나 Django Admin을 통해 이 모델에 액세스하려고 하면 다음 오류가 발생합니다.

Django Version: 1.11
Exception Type: ValueError
Exception Value:    
too many values to unpack (expected 2)

열거형 사용 방법을 설명하는 두 개의 기사를 따라갔습니다.

Django 3.0에는 Enums에 대한 내장 지원 기능이 있습니다.

예:

from django.utils.translation import gettext_lazy as _

class Student(models.Model):

    class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', _('Freshman')
        SOPHOMORE = 'SO', _('Sophomore')
        JUNIOR = 'JR', _('Junior')
        SENIOR = 'SR', _('Senior')
        GRADUATE = 'GR', _('Graduate')

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

이것들은 Python의 표준 라이브러리의 enum과 유사하게 작동하지만 일부 수정 사항이 있습니다.

  • 열거 멤버 값은 구체적인 데이터 유형을 구성할 때 사용할 인수의 튜플입니다.는 이 수 있는하거나 Django 이값문자추을가사열여람다하것니는지을합원사용하로름을으이는이는읽있플수에튜의끝▁adding▁value▁d▁an▁to▁d,다▁extra▁string▁orle▁tup-▁the니합지▁to▁be▁supports▁the원▁as▁usedjang▁of것o는▁name▁end을▁this는하용readablejang▁human사로는름o으이를 지원합니다.label.label게으른 변환 가능 문자열일 수 있습니다.은 라서대의경멤우값버은같다습다니음과따부분▁a▁be가 됩니다.(value, label)이음매의튜플이 제공되지 않거나 마지막 항목이 (지연) 문자열이 아닌 경우 레이블은 구성원 이름에서 자동으로 생성됩니다.
  • A .label속성이 값에 추가되어 사람이 읽을 수 있는 이름을 반환합니다. 속성이 됩니다. - " " " " " " " " " " ..choices,.labels,.values,그리고..names목록의 개별 부분 목록에 쉽게 액세스할 수 있습니다.사용하다.choices필드 정의의 선택사항에 전달하기에 적합한 값입니다.
  • 사용은 값을 여러 번 정의할 수 없도록 하기 위해 시행됩니다.이는 필드 선택에서 예상할 수 없는 결과입니다.

자세한 내용은 설명서를 참조하십시오.

참고:

@Danielle Madeley가 지적했듯이, 만약 당신이 접속하려고 한다면,year_in_school직접 특성 Django는 여전히 Enum 개체 대신 원시 문자열을 반환합니다.

>>> student.year_in_school
'FR'

내가 보통 하는 일은 Enum 객체를 반환하는 도우미 메소드를 만드는 것입니다.

class Student(models.Model):
    ...

    def get_year_in_school(self) -> YearInSchool:
        # Get value from choices enum
        return self.YearInSchool[self.year_in_school]

Django 2.x 이하의 경우:

사용자가 정의합니다.Enum여기에 설명된 대로 다양한 옵션을 설정합니다.

class TransactionStatus(Enum):

    INITIATED = "INITIATED"
    PENDING = "PENDING"
    COMPLETED = "COMPLETED"
    FAILED = "FAILED"
    ERROR = "ERROR"

쉼표가 없습니다!에서 이를통코서참수있다습니조할다음을나중에드해에▁to다▁this▁refer를 참조할 수 .TransactionStatus.ERROR또는TransactionStatus.PENDING.

나머지 코드는 정확합니다.당신은 이해합니다.choices .option.name,option.value.

업데이트: Django 3.x 이상의 경우 기본 제공 유형을 사용합니다.TextChoices,IntegerChoices그리고.Choices여기에 기술된 바와 같이그렇게 하면 당신은 그것을 구성할 필요가 없습니다.choices스스로를 태플링합니다.

django > 3.0 - 에넘에 대한 기본 지원 포함

from django.utils.translation import gettext_lazy as _


class Student(models.Model):
    class YearInSchool(models.TextChoices):
        FRESHMAN = "FR", _("Freshman")
        SOPHOMORE = "SO", _("Sophomore")
        JUNIOR = "JR", _("Junior")
        SENIOR = "SR", _("Senior")
        GRADUATE = "GR", _("Graduate")

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

    def is_upperclass(self):
        return self.year_in_school in {
            self.YearInSchool.JUNIOR,
            self.YearInSchool.SENIOR,
        }

당신 코드의 문제는INITIATED = "INITIATED",뒤의 쉼표.INITIATED옵션 및 기타 옵션.어떤 문자열 뒤에 쉼표를 추가하면 튜플이 됩니다.아래 예제 참조

s = 'my str'
print(type(s))
# output: str

s = 'my str',
print(type(s))
# output: tuple

#1987년파이의

class Transaction(models.Model):
    trasaction_status = models.CharField(max_length=255, choices=TransactionStatus.choices())
    transaction_type = models.CharField(max_length=255, choices=TransactionType.choices())

#enums.py

class TransactionType(Enum):

    IN = "IN"
    OUT = "OUT"

    @classmethod
    def choices(cls):
        print(tuple((i.name, i.value) for i in cls))
        return tuple((i.name, i.value) for i in cls)

class TransactionStatus(Enum):

    INITIATED = "INITIATED"
    PENDING = "PENDING"
    COMPLETED = "COMPLETED"
    FAILED = "FAILED"
    ERROR = "ERROR"

    @classmethod
    def choices(cls):
        print(tuple((i.name, i.value) for i in cls))
        return tuple((i.name, i.value) for i in cls)

django > 3.0 https://docs.djangoproject.com/en/4.0/ref/models/fields/ #field-messages-messages-types

내 프로젝트의 예:

import enum

from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils.translation import gettext_lazy as _


class NotificationTemplate(models.Model):
class Meta:
    verbose_name = _('notification template')
    verbose_name_plural = _('notification templates')

@enum.unique
class Name(str, enum.Enum):
    ONBOARDING = 'onboarding'
    TG_ERROR = 'tg_error'
    FB_ERROR = 'fb_error'

    @classmethod
    def choices(cls):
        return [(item.value, item.name) for item in cls]

@enum.unique
class Type(int, enum.Enum):
    PUSH = 1
    EMAIL = 2
    TELEGRAM = 3
    VK = 4
    OTHER = 5

    @classmethod
    def choices(cls):
        return [(item.value, item.name) for item in cls]

name = models.CharField(_('notification name'), max_length=64, unique=True, choices=Name.choices(), default=Name.ONBOARDING)
template_type = ArrayField(models.PositiveSmallIntegerField(_('type'), choices=Type.choices()))
max_count = models.PositiveSmallIntegerField(default=1)

def __str__(self):
    return self.Name(self.name).name

이 오류가 표시되는 경우:

'tuples'는 튜플(tuples 값, 사람이 읽을 수 있는 이름)을 포함하는 반복 가능한 것이어야 합니다.

그리고 Django3를 사용하고 있다면 아마 제가 했던 것과 같은 문제에 직면하게 될 것입니다."Enums"는 사용하려는 모델에 포함되어야 하며 모델 외부에 선언할 수 없습니다.예를 들어, 이것은 작동하지 않습니다.

class YearInSchool(models.TextChoices):
    FRESHMAN = 'FR', _('Freshman')
    SOPHOMORE = 'SO', _('Sophomore')
    JUNIOR = 'JR', _('Junior')
    SENIOR = 'SR', _('Senior')
    GRADUATE = 'GR', _('Graduate')

class Student(models.Model):
   year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

문서의 이 예는 다음과 같습니다.

class Student(models.Model):

    class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', _('Freshman')
        SOPHOMORE = 'SO', _('Sophomore')
        JUNIOR = 'JR', _('Junior')
        SENIOR = 'SR', _('Senior')
        GRADUATE = 'GR', _('Graduate')

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )
class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', _('Freshman')
        SOPHOMORE = 'SO', _('Sophomore')
        JUNIOR = 'JR', _('Junior')
        SENIOR = 'SR', _('Senior')
        GRADUATE = 'GR', _('Graduate')

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

위의 Django 3.0의 경우 위의 예를 사용할 수 있습니다.

정수 선택의 경우 아래 코드를 사용할 수 있습니다.

class Suit(models.IntegerChoices):
        DIAMOND = 1
        SPADE = 2
        HEART = 3
        CLUB = 4

    suit = models.IntegerField(choices=Suit.choices)

그런데 Djanog는 Python 3의 auto()도 Enum 값으로 지원합니다.다음 도우미 클래스를 사용하여 보다 쉽게 생활할 수 있습니다.

from django.db.models.enums import TextChoices

class AutoEnumChoices(TextChoices):
    def _generate_next_value_(name, start, count, last_values):  # @NoSelf
        return name.lower()
    
    @property
    def choices(cls):  # @NoSelf
        empty = [(None, cls.__empty__)] if hasattr(cls, '__empty__') else []
        return empty + [(member.value, member.label) for member in cls]

그런 다음 선택 정의에 사용합니다.

class TransferBasicStatus(AutoEnumChoices):
    NONE = auto()
    WAITING = auto()
    PENDING = auto()
    PROGRESS = auto()
    SUCCESS = auto()
    DECLINED = auto()
    ENDED =  'ended', _('Ended - The transfer has ended with mixed states')

당신이 https://hackernoon.com/using-enum-as-model-field-choice-in-django-92d8b97aaa63 에서 언급한 바에 따르면.선택 항목은 튜플 목록이어야 하며, 튜플 목록은 튜플을 반환합니다.게다가 i는 i.name 와 다릅니다.시도:

#enums.py
class TransactionType(Enum):
    IN = "IN"
    OUT = "OUT"

    @classmethod
    def choices(cls):
        return [(i, i.value) for i in cls]

django-enum 패키지는 이를 매우 쉽게 만듭니다.

from django.db import models
from django_enum import EnumField

class MyModel(models.Model):

    class TextEnum(models.TextChoices):

        VALUE0 = 'V0', 'Value 0'
        VALUE1 = 'V1', 'Value 1'
        VALUE2 = 'V2', 'Value 2'

    class IntEnum(models.IntegerChoices):

        ONE   = 1, 'One'
        TWO   = 2, 'Two',
        THREE = 3, 'Three'

    # this is equivalent to:
    #  CharField(max_length=2, choices=TextEnum.choices, null=True, blank=True)
    txt_enum = EnumField(TextEnum, null=True, blank=True)

    # this is equivalent to
    #  PositiveSmallIntegerField(choices=IntEnum.choices)
    int_enum = EnumField(IntEnum)

EnumField 단순한 가명이 아닙니다이제 필드를 by-value가 아닌 열거형 유형으로 할당하고 액세스할 수 있습니다.

instance = MyModel.objects.create(
    txt_enum=MyModel.TextEnum.VALUE1,
    int_enum=3  # by-value assignment also works
)

assert instance.txt_enum == MyModel.TextEnum('V1')
assert instance.txt_enum.label == 'Value 1'

assert instance.int_enum == MyModel.IntEnum['THREE']
assert instance.int_enum.value == 3

장고는 또한 제공합니다.IntegerChoices그리고.TextChoices열거형 필드를 매우 풍부하게 만드는 열거형 속성에서 확장된 형식입니다.

from enum_properties import s
from django_enum import TextChoices  # use instead of Django's TextChoices
from django.db import models

class TextChoicesExample(models.Model):

    class Color(TextChoices, s('rgb'), s('hex', case_fold=True)):

        # name   value   label       rgb       hex
        RED     = 'R',   'Red',   (1, 0, 0), 'ff0000'
        GREEN   = 'G',   'Green', (0, 1, 0), '00ff00'
        BLUE    = 'B',   'Blue',  (0, 0, 1), '0000ff'

        # any named s() values in the Enum's inheritance become properties on
        # each value, and the enumeration value may be instantiated from the
        # property's value

    color = EnumField(Color)

instance = TextChoicesExample.objects.create(
    color=TextChoicesExample.Color('FF0000')
)
assert instance.color == TextChoicesExample.Color('Red')
assert instance.color == TextChoicesExample.Color('R')
assert instance.color == TextChoicesExample.Color((1, 0, 0))

# direct comparison to any symmetric value also works
assert instance.color == 'Red'
assert instance.color == 'R'
assert instance.color == (1, 0, 0)

# save by any symmetric value
instance.color = 'FF0000'

# access any enum property right from the model field
assert instance.color.hex == 'ff0000'

# this also works!
assert instance.color == 'ff0000'

# and so does this!
assert instance.color == 'FF0000'

instance.save()

# filtering works by any symmetric value or enum type instance
assert TextChoicesExample.objects.filter(
    color=TextChoicesExample.Color.RED
).first() == instance

assert TextChoicesExample.objects.filter(color=(1, 0, 0)).first() == instance

assert TextChoicesExample.objects.filter(color='FF0000').first() == instance

문서의 예제를 기반으로 다음과 같은 작업을 수행할 수 있습니다.:

from enum import Enum

class BaseEnum(Enum):
    def __new__(cls, *args):
        obj = object.__new__(cls)
        obj._value_ = args[0]
        obj.display_name = args[1]
        return obj

    @classmethod
    def model_choices(cls):
        return [(cls.__members__[member].value, cls.__members__[member].display_name)
            for member in cls.__members__.keys()]

다음과 같은 결과를 초래할 수 있습니다.

>>> class TransactionType(BaseEnum):
...     IN = ('in', 'In')
...     OUT = ('out', 'Out')
...
>>> TransactionType.IN.value
'in'
>>> TransactionType.IN.display_name
'In'
>>> TransactionType.model_choices()
[('in', 'In'), ('out', 'Out')]

필드의 선택에 대한 주장으로 사용될 수 있습니다.

다음과 같이 쓸 수도 있습니다.

class Transaction(models.Model):
    class TransactionStatus(Enum):
        initiated = ('in', 'Initiated')
        pending = ('pe', 'Pending')
        completed = ('co', 'Completed')
        failed = ('fa', 'Failed')
        error = ('er', 'Error')
        
        @classmethod
        def get_value(cls, member):
            return cls[member].value[0]
    
    class TransactionType(Enum):
        _in = ('in', 'In')
        out = ('ou', 'Out')
   
        @classmethod
        def get_value(cls, member):
            return cls[member].value[0]

    trasaction_status = models.CharField(max_length=2, choices=[x.value for x in TransactionStatus])
    transaction_type = models.CharField(max_length=2, choices=[x.value for x in TransactionType])

와 함께get_value예를 들어 다음과 같이 쓸 수 있습니다.

Transaction.objects.filter(status=Transaction.TransactionStatus.get_value('initialited'))

@모델을 변경해야 하는 경우 @class method defections(cls): print(i.value(i.value, i.name ) 반환 튜플(i.value(i.value, i.name ))

그것은 나에게 효과가 있었다.

언급URL : https://stackoverflow.com/questions/54802616/how-can-one-use-enums-as-a-choice-field-in-a-django-model

반응형