PostgreSQL (Postgres)을 사용하는 경우 SQLAlchemy 에서 열 (기본 키)을 UUID 로 정의하는 방법이 있습니까?
PostgreSQL (Postgres)을 사용하는 경우 SQLAlchemy 에서 열 (기본 키)을 UUID 로 정의하는 방법이 있습니까?
답변:
sqlalchemy postgres 언어는 UUID 열을 지원합니다. 이것은 쉽습니다 (그리고 질문은 특히 postgres입니다)-다른 답변이 왜 그렇게 복잡한 지 이해할 수 없습니다.
다음은 그 예입니다.
from sqlalchemy.dialects.postgresql import UUID
from flask_sqlalchemy import SQLAlchemy
import uuid
db = SQLAlchemy()
class Foo(db.Model):
# id = db.Column(db.Integer, primary_key=True)
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False)
를 사용 callable
uuid.uuid4
하여 함수 자체를 호출하는 것보다 열 정의로 전달하는 것을 놓치지 않도록주의하십시오 uuid.uuid4()
. 그렇지 않으면이 클래스의 모든 인스턴스에 대해 동일한 스칼라 값을 갖게됩니다. 자세한 내용은 여기 :
이 열의 기본값을 나타내는 스칼라, Python 호출 가능 또는 ColumnElement 표현식.이 열이 삽입의 VALUES 절에 지정되지 않은 경우 삽입시 호출됩니다.
uuid.uuid4
).
Column(UUID(as_uuid=True) ...)
Column
및 Integer
코드 조각의 상단에 가져온, 또는 읽기 변경된 db.Column
와db.Integer
나는 이것을 썼고 도메인은 사라졌지 만 여기에 용기가 있습니다 ....
적절한 데이터베이스 디자인에 관심이있는 동료들이 주요 필드에 사용되는 UUID 및 GUID에 대해 어떻게 생각하는지에 관계없이. 나는 종종 그것을해야한다는 것을 알게된다. 나는 그것이 가치가있는 자동 증가보다 몇 가지 장점이 있다고 생각합니다.
지난 몇 달 동안 UUID 컬럼 유형을 수정 해 왔으며 마침내 견고하게 얻은 것 같습니다.
from sqlalchemy import types
from sqlalchemy.dialects.mysql.base import MSBinary
from sqlalchemy.schema import Column
import uuid
class UUID(types.TypeDecorator):
impl = MSBinary
def __init__(self):
self.impl.length = 16
types.TypeDecorator.__init__(self,length=self.impl.length)
def process_bind_param(self,value,dialect=None):
if value and isinstance(value,uuid.UUID):
return value.bytes
elif value and not isinstance(value,uuid.UUID):
raise ValueError,'value %s is not a valid uuid.UUID' % value
else:
return None
def process_result_value(self,value,dialect=None):
if value:
return uuid.UUID(bytes=value)
else:
return None
def is_mutable(self):
return False
id_column_name = "id"
def id_column():
import uuid
return Column(id_column_name,UUID(),primary_key=True,default=uuid.uuid4)
# Usage
my_table = Table('test',
metadata,
id_column(),
Column('parent_id',
UUID(),
ForeignKey(table_parent.c.id)))
바이너리 (16 바이트)로 저장하는 것이 문자열 표현 (36 바이트?)보다 효율적이어야한다고 생각합니다. 그리고 16 바이트 블록을 인덱싱하는 것이 문자열보다 mysql에서 더 효율적이어야한다는 표시가있는 것 같습니다. 어차피 더 나빠질 거라고는 생각하지 않았습니다.
내가 발견 한 한 가지 단점은 적어도 phpymyadmin에서는 "select * from table where id = ..."에 대해 일종의 문자 변환을 암시 적으로 시도하고 기타 표시 문제가 있기 때문에 레코드를 편집 할 수 없다는 것입니다.
그 외에는 모든 것이 잘 작동하는 것 같아서 그것을 밖으로 던지고 있습니다. 눈에 띄는 오류가 보이면 댓글을 남겨주세요. 개선을위한 제안을 환영합니다.
내가 뭔가를 놓치고 있지 않는 한 기본 데이터베이스에 UUID 유형이 있으면 위의 솔루션이 작동합니다. 그렇지 않은 경우 테이블을 만들 때 오류가 발생할 수 있습니다. 내가 생각해 낸 솔루션은 원래 MSSqlServer를 대상으로하고 결국 MySql로 갔기 때문에 mysql 및 sqlite에서 잘 작동하는 것처럼 보이기 때문에 솔루션이 조금 더 유연하다고 생각합니다. 아직 postgres를 확인하는 데 신경 쓰지 않았습니다.
sqlalchemy.dialects.postgresql.UUID
직접 사용하십시오 . 참조 백엔드에 얽매이지 GUID 유형
UUID 값이있는 '문자열'열에 만족하는 경우 다음과 같은 간단한 해결책이 있습니다.
def generate_uuid():
return str(uuid.uuid4())
class MyTable(Base):
__tablename__ = 'my_table'
uuid = Column(String, name="uuid", primary_key=True, default=generate_uuid)
http://sqlalchemy-utils.readthedocs.org/en/latest/data_types.html#module-sqlalchemy_utils.types.uuid 패키지 UUIDType
에서 사용했습니다 .SQLAlchemy-Utils
raise InvalidStatus("notfound: {k}. (cls={cls})".format(k=k, cls=cls))
alchemyjsonschema.InvalidStatus: notfound: BINARY(16). (cls=<class 'sqlalchemy_utils.types.uuid.UUIDType'>)
NameError: name 'sqlalchemy_utils' is not defined
습니까?
SQLAlchemy-Utils
타사 패키지가 먼저 설치해야합니다 :pip install sqlalchemy-utils
Postgres를 사용하고 있으므로 다음과 같이 작동합니다.
from app.main import db
from sqlalchemy.dialects.postgresql import UUID
class Foo(db.Model):
id = db.Column(UUID(as_uuid=True), primary_key=True)
name = db.Column(db.String, nullable=False)
다음은 SQLAlchemy 문서 의 백엔드 불가지론 GUID 를 기반으로 하지만 BINARY 필드를 사용하여 비 postgresql 데이터베이스에 UUID를 저장하는 방법입니다.
import uuid
from sqlalchemy.types import TypeDecorator, BINARY
from sqlalchemy.dialects.postgresql import UUID as psqlUUID
class UUID(TypeDecorator):
"""Platform-independent GUID type.
Uses Postgresql's UUID type, otherwise uses
BINARY(16), to store UUID.
"""
impl = BINARY
def load_dialect_impl(self, dialect):
if dialect.name == 'postgresql':
return dialect.type_descriptor(psqlUUID())
else:
return dialect.type_descriptor(BINARY(16))
def process_bind_param(self, value, dialect):
if value is None:
return value
else:
if not isinstance(value, uuid.UUID):
if isinstance(value, bytes):
value = uuid.UUID(bytes=value)
elif isinstance(value, int):
value = uuid.UUID(int=value)
elif isinstance(value, str):
value = uuid.UUID(value)
if dialect.name == 'postgresql':
return str(value)
else:
return value.bytes
def process_result_value(self, value, dialect):
if value is None:
return value
if dialect.name == 'postgresql':
return uuid.UUID(value)
else:
return uuid.UUID(bytes=value)
누구든지 관심이 있다면 Tom Willis 답변을 사용했지만 process_bind_param 메서드에서 uuid.UUID 변환에 문자열을 추가하는 것이 유용하다는 것을 알았습니다.
class UUID(types.TypeDecorator):
impl = types.LargeBinary
def __init__(self):
self.impl.length = 16
types.TypeDecorator.__init__(self, length=self.impl.length)
def process_bind_param(self, value, dialect=None):
if value and isinstance(value, uuid.UUID):
return value.bytes
elif value and isinstance(value, basestring):
return uuid.UUID(value).bytes
elif value:
raise ValueError('value %s is not a valid uuid.UUId' % value)
else:
return None
def process_result_value(self, value, dialect=None):
if value:
return uuid.UUID(bytes=value)
else:
return None
def is_mutable(self):
return False
예를 들어 사용자 정의 유형 작성을 시도 할 수 있습니다 .
import sqlalchemy.types as types
class UUID(types.TypeEngine):
def get_col_spec(self):
return "uuid"
def bind_processor(self, dialect):
def process(value):
return value
return process
def result_processor(self, dialect):
def process(value):
return value
return process
table = Table('foo', meta,
Column('id', UUID(), primary_key=True),
)
types.TypeDecorator
대신 하위 클래스라는 점을 제외하면 비슷해 보입니다 types.TypeEngine
. 두 접근 방식 중 하나가 다른 접근 방식에 비해 장점이나 단점이 있습니까?
default=?
않습니까? 예Column('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)