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
감사합니다! 있다면 그것은 좋은 일 수 있습니다 Column및 Integer코드 조각의 상단에 가져온, 또는 읽기 변경된 db.Column와db.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.