SQLAlchemy에서 UUID를 어떻게 사용할 수 있습니까?


94

PostgreSQL (Postgres)을 사용하는 경우 SQLAlchemy 에서 열 (기본 키)을 UUID 로 정의하는 방법이 있습니까?


2
불행히도 열 유형에 대한 SQLAlchemy 설명서의 백엔드에 구애받지 않는 GUID 유형 은 SQLite 데이터베이스 엔진의 기본 키에 대해 작동하지 않는 것 같습니다. 내가 기대했던 것만 큼 일치하지 않았습니다.
adamek 2012

SQLAlchemy 유틸리티는 UUIDType 데코레이터를 제공 합니다. 휠을 재발 명 할 필요가 없습니다
Filipe Bezerra de Sousa

답변:


152

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 절에 지정되지 않은 경우 삽입시 호출됩니다.


6
전적으로 동의합니다. 다른 답변 중 일부는 다른 데이터베이스에 적합하지만 postgres의 경우 가장 깨끗한 솔루션입니다. (기본값을로 설정할 수도 있습니다 uuid.uuid4).
pacha

1
MWE를 제공 할 수 있습니까? 아니면 flask_sqlalchemy의 serializer가 UUID 유형을 이해할 수 있습니까? 오류 아래 pastebin의 코드, pastebin.com/hW8KPuYw
Brandon Dube

1
신경 쓰지 마십시오. stdlib에서 UUID 객체를 사용하려면 다음을 수행하십시오Column(UUID(as_uuid=True) ...)
Brandon Dube

1
감사합니다! 있다면 그것은 좋은 일 수 있습니다 ColumnInteger코드 조각의 상단에 가져온, 또는 읽기 변경된 db.Columndb.Integer
그렉 Sadetsky을


64

나는 이것을 썼고 도메인은 사라졌지 만 여기에 용기가 있습니다 ....

적절한 데이터베이스 디자인에 관심이있는 동료들이 주요 필드에 사용되는 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를 확인하는 데 신경 쓰지 않았습니다.


예, Jacob의 답변에서 추천을 본 후에 게시했습니다.
Tom Willis

4
버전 0.6 이상을 사용하는 경우 Tom 솔루션의 MSBinary import 문을 "from sqlalchemy.dialects.mysql.base import MSBinary"로 변경해야합니다. 출처 : mail-archive.com/sqlalchemy@googlegroups.com/msg18397.html
Cal Jacobson

2
"나는 이것을 썼다"는 데드 링크입니다.
julx


2
@codeninja postgresql에는 이미 기본 UUID 유형이 있으므로 sqlalchemy.dialects.postgresql.UUID직접 사용하십시오 . 참조 백엔드에 얽매이지 GUID 유형
cowbert

24

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)

5
UUID를 지원하지 않는 정말 이상한 데이터베이스를 사용하지 않는 한 UUID를 문자열로 저장하지 마십시오. 그렇지 않으면 모든 데이터를 문자열로 저장할 수 있습니다 ...;)
Nick

@ 닉 왜? 단점은 무엇입니까?
rayepps

6
@rayepps-많은 단점이 있습니다. 몇 가지 단점이 있습니다. 크기-문자열 uuid는 공간의 두 배를 차지합니다-16 바이트 대 32 자-포맷터는 포함하지 않습니다. 처리 시간-더 많은 바이트 = 데이터 세트가 커질수록 CPU의 처리 시간이 늘어납니다. uuid 문자열 형식은 언어에 따라 다르며 필요한 번역을 추가합니다. uuid가 아닌 것은 무엇이든 넣을 수 있기 때문에 누군가가 열을 오용하기가 더 쉽습니다. 시작하기에 충분합니다.
Nick

성능 문제를 위해 uuid의 열로 문자열을 사용해서는 안됩니다. Binary (16)가 더 권장됩니다.
Cyril N.

19

나는 현재 이것을 사용하려고하는데, 문제는 다음과 같은 오류가 발생 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습니까?
Walter

1
SQLAlchemy-Utils타사 패키지가 먼저 설치해야합니다 :pip install sqlalchemy-utils
Berislav LoPAC 제품군

마이그레이션은 uuid에 대한 UUID 대 CHAR / BINARY 값이있는 시스템이나 계정을 필요로하지만 이것이 갈 방법입니다.
rjurney

9

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)

1
이것은 PostgreSQL 데이터베이스를 사용하는 개발자에게 유일하게 허용되는 답변이어야합니다.
José L. Patiño

5

다음은 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)

1
이것의 사용법은 무엇입니까?
CodeTrooper 2017

3

누구든지 관심이 있다면 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

-19

예를 들어 사용자 정의 유형 작성을 시도 할 수 있습니다 .

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. 두 접근 방식 중 하나가 다른 접근 방식에 비해 장점이나 단점이 있습니까?
Jacob Gabrielson

11
이것은 작동하지 않으며 문서의 더미 유형 예제에서 잘라내어 붙여 넣기 작업입니다. 아래 Tom Willis의 대답이 훨씬 낫습니다.
Jesse Dhillon

필요하지 default=?않습니까? 예Column('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)
iJames

링크가 "페이지를 찾을 수 없음"을 가리키고 , docs.sqlalchemy.org/en/13/core/… 는 이전
버전과 상당히 유사합니다
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.