Sqalchemy에서 열거를 하는 가장 좋은 방법은?
sqalchemy에 대해 읽고 있는데 다음 코드를 봤습니다.
employees_table = Table('employees', metadata,
Column('employee_id', Integer, primary_key=True),
Column('name', String(50)),
Column('manager_data', String(50)),
Column('engineer_info', String(50)),
Column('type', String(20), nullable=False)
)
employee_mapper = mapper(Employee, employees_table, \
polymorphic_on=employees_table.c.type, polymorphic_identity='employee')
manager_mapper = mapper(Manager, inherits=employee_mapper, polymorphic_identity='manager')
engineer_mapper = mapper(Engineer, inherits=employee_mapper, polymorphic_identity='engineer')
라이브러리에 상수가 있는 'type'을 int로 만들어야 합니까?아니면 그냥 열거형으로 해야 하나요?
Python의 열거된 유형은 SQLLchemy 1.1의 SQLLchemy Enum 유형에서 직접 허용됩니다.
import enum
from sqlalchemy import Integer, Enum
class MyEnum(enum.Enum):
one = 1
two = 2
three = 3
class MyClass(Base):
__tablename__ = 'some_table'
id = Column(Integer, primary_key=True)
value = Column(Enum(MyEnum))
위에서 문자열 값 "1", "2", "3"은 정수 값이 아닌 지속됩니다.
이전 버전의 SQLLchemy의 경우 자체 Enumerated type(http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/) 을 만드는 게시물을 작성했습니다.
from sqlalchemy.types import SchemaType, TypeDecorator, Enum
from sqlalchemy import __version__
import re
if __version__ < '0.6.5':
raise NotImplementedError("Version 0.6.5 or higher of SQLAlchemy is required.")
class EnumSymbol(object):
"""Define a fixed symbol tied to a parent class."""
def __init__(self, cls_, name, value, description):
self.cls_ = cls_
self.name = name
self.value = value
self.description = description
def __reduce__(self):
"""Allow unpickling to return the symbol
linked to the DeclEnum class."""
return getattr, (self.cls_, self.name)
def __iter__(self):
return iter([self.value, self.description])
def __repr__(self):
return "<%s>" % self.name
class EnumMeta(type):
"""Generate new DeclEnum classes."""
def __init__(cls, classname, bases, dict_):
cls._reg = reg = cls._reg.copy()
for k, v in dict_.items():
if isinstance(v, tuple):
sym = reg[v[0]] = EnumSymbol(cls, k, *v)
setattr(cls, k, sym)
return type.__init__(cls, classname, bases, dict_)
def __iter__(cls):
return iter(cls._reg.values())
class DeclEnum(object):
"""Declarative enumeration."""
__metaclass__ = EnumMeta
_reg = {}
@classmethod
def from_string(cls, value):
try:
return cls._reg[value]
except KeyError:
raise ValueError(
"Invalid value for %r: %r" %
(cls.__name__, value)
)
@classmethod
def values(cls):
return cls._reg.keys()
@classmethod
def db_type(cls):
return DeclEnumType(cls)
class DeclEnumType(SchemaType, TypeDecorator):
def __init__(self, enum):
self.enum = enum
self.impl = Enum(
*enum.values(),
name="ck%s" % re.sub(
'([A-Z])',
lambda m:"_" + m.group(1).lower(),
enum.__name__)
)
def _set_table(self, table, column):
self.impl._set_table(table, column)
def copy(self):
return DeclEnumType(self.enum)
def process_bind_param(self, value, dialect):
if value is None:
return None
return value.value
def process_result_value(self, value, dialect):
if value is None:
return None
return self.enum.from_string(value.strip())
if __name__ == '__main__':
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import Session
Base = declarative_base()
class EmployeeType(DeclEnum):
part_time = "P", "Part Time"
full_time = "F", "Full Time"
contractor = "C", "Contractor"
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(60), nullable=False)
type = Column(EmployeeType.db_type())
def __repr__(self):
return "Employee(%r, %r)" % (self.name, self.type)
e = create_engine('sqlite://', echo=True)
Base.metadata.create_all(e)
sess = Session(e)
sess.add_all([
Employee(name='e1', type=EmployeeType.full_time),
Employee(name='e2', type=EmployeeType.full_time),
Employee(name='e3', type=EmployeeType.part_time),
Employee(name='e4', type=EmployeeType.contractor),
Employee(name='e5', type=EmployeeType.contractor),
])
sess.commit()
print sess.query(Employee).filter_by(type=EmployeeType.contractor).all()
SQLLchemy의 Enum 유형이 0.6 이후입니다. http://docs.sqlalchemy.org/en/latest/core/type_basics.html?highlight=enum#sqlalchemy.types.Enum
데이터베이스에 네이티브 열거형이 있는 경우에만 사용을 권장합니다.그렇지 않으면 개인적으로 그냥 int를 사용할 것입니다.
저는 SQLAlchemy에 대해 잘 알지 못하지만 Paulo의 이러한 접근법은 제게 훨씬 간단해 보였습니다.
사용자 친화적인 설명이 필요 없어 그대로 진행했습니다.
파울로의 말을 인용합니다. (여기에 다시 올리는 것을 그가 개의치 않기를 바랍니다.)
파이썬의
namedtuple
구조를 위한 수집품이름에서 알 수 있듯이, a.namedtuple
는 각 항목에 이름이 있는 튜플입니다.일반적인 튜플처럼 품목은 불변입니다.일반적인 튜플과 달리, 점 표기법을 사용하여 아이템의 가치에 접근할 수 있습니다.여기에 다음과 같은 유틸리티 기능이 있습니다.
namedtuple
:from collections import namedtuple def create_named_tuple(*values): return namedtuple('NamedTuple', values)(*values)
*
값 변수가 목록의 항목을 unpack링하기 위한 것이기 때문에 각 항목이 함수에 대한 개별 인수로 전달됩니다.
namedtuple
, 필요한 값을 사용하여 위 함수를 호출합니다.>>> project_version = create_named_tuple('alpha', 'beta', 'prod') NamedTuple(alpha='alpha', beta='beta', prod='prod')
이제 우리는 사용할 수 있습니다.
project_version
namedtuple 버전 필드의 값을 지정합니다.class Project(Base): ... version = Column(Enum(*project_version._asdict().values(), name='projects_version')) ...
이것은 제게 아주 효과적이고 이전에 발견했던 다른 솔루션들보다 훨씬 간단합니다.
참고: 다음은 구식입니다.sqalchemy.type을 사용해야 합니다.Wolph가 추천한 대로 지금 열거하세요.특히 SQLA lchemy 1.1 이후 PEP-435를 준수하기 때문에 좋습니다.
저는 http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/, 에 있는 zzzeek의 레시피를 좋아하지만 두 가지를 바꿨습니다.
- EnumSymbol의 Python 이름을 데이터베이스의 이름으로 사용하는 대신 사용하고 있습니다.그게 덜 헷갈리는 것 같아요.UI에서 팝업 메뉴를 만들 때와 같이 별도의 값을 사용해도 여전히 유용합니다.설명은 툴팁 등에 사용할 수 있는 값의 긴 버전으로 간주할 수 있습니다.
- 원래 레시피에서 Enum 기호의 순서는 Python에서 반복할 때와 데이터베이스에서 "주문 기준"을 수행할 때 모두 임의적입니다.하지만 종종 단호한 명령을 받고 싶습니다.그래서 속성을 문자열이나 튜플로 설정하면 알파벳 순서로 변경했고, 속성을 EnumESymbols로 명시적으로 설정하면 값이 선언되는 순서로 변경했습니다. 이는 SQLLchemy가 DeclarativeBase 클래스의 Columns를 정렬할 때 사용하는 것과 동일한 트릭을 사용합니다.
예:
class EmployeeType(DeclEnum):
# order will be alphabetic: contractor, part_time, full_time
full_time = "Full Time"
part_time = "Part Time"
contractor = "Contractor"
class EmployeeType(DeclEnum):
# order will be as stated: full_time, part_time, contractor
full_time = EnumSymbol("Full Time")
part_time = EnumSymbol("Part Time")
contractor = EnumSymbol("Contractor")
Python 2.7에서 사용할 수 있는 OrderedDict 클래스를 사용하는 수정된 레시피는 다음과 같습니다.
import re
from sqlalchemy.types import SchemaType, TypeDecorator, Enum
from sqlalchemy.util import set_creation_order, OrderedDict
class EnumSymbol(object):
"""Define a fixed symbol tied to a parent class."""
def __init__(self, value, description=None):
self.value = value
self.description = description
set_creation_order(self)
def bind(self, cls, name):
"""Bind symbol to a parent class."""
self.cls = cls
self.name = name
setattr(cls, name, self)
def __reduce__(self):
"""Allow unpickling to return the symbol linked to the DeclEnum class."""
return getattr, (self.cls, self.name)
def __iter__(self):
return iter([self.value, self.description])
def __repr__(self):
return "<%s>" % self.name
class DeclEnumMeta(type):
"""Generate new DeclEnum classes."""
def __init__(cls, classname, bases, dict_):
reg = cls._reg = cls._reg.copy()
for k in sorted(dict_):
if k.startswith('__'):
continue
v = dict_[k]
if isinstance(v, basestring):
v = EnumSymbol(v)
elif isinstance(v, tuple) and len(v) == 2:
v = EnumSymbol(*v)
if isinstance(v, EnumSymbol):
v.bind(cls, k)
reg[k] = v
reg.sort(key=lambda k: reg[k]._creation_order)
return type.__init__(cls, classname, bases, dict_)
def __iter__(cls):
return iter(cls._reg.values())
class DeclEnum(object):
"""Declarative enumeration.
Attributes can be strings (used as values),
or tuples (used as value, description) or EnumSymbols.
If strings or tuples are used, order will be alphabetic,
otherwise order will be as in the declaration.
"""
__metaclass__ = DeclEnumMeta
_reg = OrderedDict()
@classmethod
def names(cls):
return cls._reg.keys()
@classmethod
def db_type(cls):
return DeclEnumType(cls)
class DeclEnumType(SchemaType, TypeDecorator):
"""DeclEnum augmented so that it can persist to the database."""
def __init__(self, enum):
self.enum = enum
self.impl = Enum(*enum.names(), name="ck%s" % re.sub(
'([A-Z])', lambda m: '_' + m.group(1).lower(), enum.__name__))
def _set_table(self, table, column):
self.impl._set_table(table, column)
def copy(self):
return DeclEnumType(self.enum)
def process_bind_param(self, value, dialect):
if isinstance(value, EnumSymbol):
value = value.name
return value
def process_result_value(self, value, dialect):
if value is not None:
return getattr(self.enum, value.strip())
mysql은 사투리를 씁니다.
from sqlalchemy.dialects.mysql import ENUM
...
class Videos(Base):
...
video_type = Column(ENUM('youtube', 'vimeo'))
...
이와 관련된 StackOverflow 스레드 응답은 Postgre에 의존합니다.SQL 또는 기타 방언별 타이핑.그러나 일반적인 지원은 Alembic 마이그레이션과 호환되는 SQLLechemy에서 쉽게 이루어질 수 있습니다.
백엔드가 Enum을 지원하지 않는 경우 SQLLchemy 및 alembic을 통해 varchar 및 유사한 유형에 대한 제약 조건을 적용하여 Enumerated 열 유형을 모방할 수 있습니다.
먼저 Python Enum, SQLchemy Enum 및 SQLchemy 선언 기반을 사용자 지정 SQ를 선언할 곳으로 가져옵니다.Llchemy Enum 열 유형입니다.
import enum
from sqlalchemy import Enum
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
OP의 원래 파이썬 열거 클래스를 들어보겠습니다.
class PostStatus(enum.Enum):
DRAFT='draft'
APPROVE='approve'
PUBLISHED='published'
이제 SQLLchemyEnum 인스턴스화를 만듭니다.
PostStatusType: Enum = Enum(
PostStatus,
name="post_status_type",
create_constraint=True,
metadata=Base.metadata,
validate_strings=True,
)
Alembic 실행 시alembic revision --autogenerate -m "Revision Notes"
그리고 수정을 적용하려고 노력합니다.alembic upgrade head
, 존재하지 않는 유형에 대한 오류가 발생할 가능성이 높습니다.예를 들어,
...
sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedObject) type "post_status_type" does not exist
LINE 10: post_status post_status_type NOT NULL,
...
이 문제를 해결하려면 SQLChemyEum 클래스를 가져와 다음을 추가합니다.upgrade()
그리고.downgrade()
Alembic 자동 생성 리비전 스크립트의 함수입니다.
from myproject.database import PostStatusType
...
def upgrade() -> None:
PostStatusType.create(op.get_bind(), checkfirst=True)
... the remainder of the autogen code...
def downgrade() -> None:
...the autogen code...
PostStatusType.drop(op.get_bind(), checkfirst=True)
마지막으로, 자동 생성된 것을 업데이트해야 합니다.sa.Column()
열거된 형식을 사용하여 테이블에서 선언하는 대신 SQLAlchemyEum 형식을 단순히 참조하는 대신 Alembic의 재선언 시도를 사용합니다.예를 들어 에def upgrade() -> None:
op.create_table(
"my_table",
sa.Column(
"post_status",
PostStatusType,
nullable=False,
),
)
이것은 제가 사용하는 방법입니다 - IntEnum을 사용합니다.
from enum import IntEnum
class GenderType(IntEnum):
FEMALE: int = 1
MALE: int = 2
TRANSGENDER: int = 3
class Citizen(Base):
__tablename__ = 'citizen'
user_uuid: int = Column(UUID(as_uuid=True), primary_key=True)
gender_type: int = Column(Integer, nullable=False, default=GenderType.MALE)
full_name: str = Column(String(64))
address: str = Column(String(128))
언급URL : https://stackoverflow.com/questions/2676133/best-way-to-do-enum-in-sqlalchemy
'source' 카테고리의 다른 글
MySQL DB에서 고객 정보를 암호화하는 가장 좋은 방법은? (0) | 2023.10.18 |
---|---|
늘 레코드를 포함하는 왼쪽 조인 (0) | 2023.10.18 |
각 줄의 텍스트 파일을 파워셸을 통해 배열로 저장하는 방법 (0) | 2023.10.18 |
jQuery를 사용하여 텍스트 정렬 스타일을 동적으로 추가하는 방법 (0) | 2023.10.18 |
스택 포인터 값 출력 (0) | 2023.10.18 |