Làm cách nào để sử dụng UUID trong SQLAlchemy?


94

Có cách nào để xác định một cột (khóa chính) làm UUID trong SQLAlchemy nếu sử dụng PostgreSQL (Postgres) không?


2
Thật không may , Loại GUID phụ trợ-bất khả tri từ tài liệu SQLAlchemy cho các loại cột dường như không hoạt động đối với các khóa chính trong công cụ cơ sở dữ liệu SQLite. Không hoàn toàn mang tính đại kết như tôi đã hy vọng.
adamek

SQLAlchemy utils cung cấp trình trang trí UUIDType , không cần phải phát minh lại bánh xe
Filipe Bezerra de Sousa

Câu trả lời:


153

Phương ngữ sqlalchemy postgres hỗ trợ các cột UUID. Điều này thật dễ dàng (và câu hỏi cụ thể là postgres) - Tôi không hiểu tại sao các câu trả lời khác đều phức tạp như vậy.

Đây là một ví dụ:

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)

Hãy cẩn thận để không bỏ lỡ việc chuyển callable uuid.uuid4vào định nghĩa cột, thay vì gọi chính hàm với uuid.uuid4(). Nếu không, bạn sẽ có cùng một giá trị vô hướng cho tất cả các trường hợp của lớp này. Thông tin chi tiết tại đây :

Một biểu thức vô hướng, Python có thể gọi hoặc ColumnElement đại diện cho giá trị mặc định cho cột này, sẽ được gọi khi chèn nếu cột này không được chỉ định trong mệnh đề VALUES của chèn.


6
Tôi hoàn toàn đồng ý với bạn. Một số câu trả lời khác rất tuyệt đối với các cơ sở dữ liệu khác, nhưng đối với postgres thì đây là giải pháp sạch nhất. (Bạn cũng có thể đặt mặc định là uuid.uuid4).
pacha

1
Bạn có thể cung cấp một MWE? Hoặc có thể bộ nối tiếp trong flask_sqlalchemy hiểu loại UUID? Mã trong pastebin dưới đây bị lỗi, pastebin.com/hW8KPuYw
Brandon Dube,

1
nevermind, nếu bạn muốn sử dụng UUID đối tượng từ stdlib, làmColumn(UUID(as_uuid=True) ...)
Brandon Dube

1
Cảm ơn bạn! Nó có thể được tốt đẹp nếu ColumnIntegerđược nhập khẩu ở phía trên cùng của đoạn mã, hoặc đã được thay đổi để đọc db.Columndb.Integer
Greg Sadetsky

1
Không cần đâu @nephanth
Filipe Bezerra de Sousa

64

Tôi đã viết cái này và miền đã biến mất nhưng đây là phần ruột ...

Bất kể các đồng nghiệp của tôi, những người thực sự quan tâm đến thiết kế cơ sở dữ liệu thích hợp cảm thấy thế nào về UUID và các GUID được sử dụng cho các trường chính. Tôi thường thấy mình cần phải làm điều đó. Tôi nghĩ rằng nó có một số lợi thế so với autoincrement khiến nó có giá trị.

Tôi đã tinh chỉnh loại cột UUID trong vài tháng qua và tôi nghĩ rằng cuối cùng tôi đã có được nó.

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)))

Tôi tin rằng lưu trữ dưới dạng nhị phân (16 byte) sẽ hiệu quả hơn so với biểu diễn chuỗi (36 byte?), Và dường như có một số dấu hiệu cho thấy việc lập chỉ mục các khối 16 byte trong mysql sẽ hiệu quả hơn so với chuỗi. Tôi sẽ không mong đợi nó tồi tệ hơn dù sao.

Một nhược điểm mà tôi đã tìm thấy là ít nhất trong phpymyadmin, bạn không thể chỉnh sửa bản ghi vì nó ngầm cố gắng thực hiện một số loại chuyển đổi ký tự cho "select * from table where id = ..." và có các vấn đề hiển thị khác.

Ngoài ra, mọi thứ dường như hoạt động tốt, và vì vậy tôi đang ném nó ra khỏi đó. Để lại nhận xét nếu bạn thấy lỗi rõ ràng với nó. Tôi hoan nghênh bất kỳ đề xuất nào để cải thiện nó.

Trừ khi tôi thiếu thứ gì đó, giải pháp trên sẽ hoạt động nếu cơ sở dữ liệu bên dưới có loại UUID. Nếu không, bạn có thể gặp lỗi khi tạo bảng. Giải pháp mà tôi đưa ra, tôi nhắm mục tiêu ban đầu là MSSqlServer và cuối cùng là MySql, vì vậy tôi nghĩ giải pháp của tôi linh hoạt hơn một chút vì nó có vẻ hoạt động tốt trên mysql và sqlite. Chưa bận tâm kiểm tra bưu điện.


vâng, tôi đã đăng nó sau khi tôi thấy giới thiệu từ câu trả lời của Jacob.
Tom Willis

4
Lưu ý rằng nếu bạn đang sử dụng phiên bản 0.6 trở lên, câu lệnh nhập MSBinary trong giải pháp của Tom sẽ được thay đổi thành "from sqlalchemy.dialects.mysql.base import MSBinary". Nguồn: mail-archive.com/sqlalchemy@googlegroups.com/msg18397.html
Cal Jacobson

2
"Tôi đã viết cái này" là một liên kết chết.
julx


2
@codeninja postgresql đã có loại UUID gốc, vì vậy chỉ cần sử dụng sqlalchemy.dialects.postgresql.UUIDtrực tiếp. thấy Backend-agnostic GUID Loại
cowbert

24

Nếu bạn hài lòng với cột 'Chuỗi' có giá trị UUID, đây là một giải pháp đơn giản:

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
Không lưu trữ UUID dưới dạng chuỗi trừ khi bạn đang sử dụng một cơ sở dữ liệu thực sự kỳ lạ không hỗ trợ chúng. nếu không, có thể lưu trữ tất cả dữ liệu của bạn dưới dạng chuỗi ...;)
Nick

@Nick tại sao? nhược điểm là gì?
rayepps

6
@rayepps - có nhiều nhược điểm - bạn cần lưu ý một số nhược điểm: kích thước - chuỗi uuid chiếm gấp đôi không gian - 16byte so với 32 ký tự - không bao gồm bất kỳ định dạng nào. Thời gian xử lý - nhiều byte hơn = thời gian xử lý của CPU nhiều hơn khi tập dữ liệu của bạn lớn hơn. Các định dạng chuỗi uuid khác nhau theo ngôn ngữ, thêm các bản dịch bắt buộc. Dễ dàng hơn để ai đó sử dụng sai cột, vì bạn có thể đặt bất cứ thứ gì vào đó, những thứ không có lợi. Đó là đủ để bắt đầu.
Nick

Bạn không nên sử dụng Chuỗi làm cột cho một uuid, vì các vấn đề về hiệu suất. Một Binary (16) được khuyến khích hơn.
Cyril N.

19

Tôi hiện đang cố gắng sử dụng này, vấn đề là tôi nhận được một lỗi: raise InvalidStatus("notfound: {k}. (cls={cls})".format(k=k, cls=cls)) alchemyjsonschema.InvalidStatus: notfound: BINARY(16). (cls=<class 'sqlalchemy_utils.types.uuid.UUIDType'>)
CodeTrooper

Có bạn nào nhận lỗi NameError: name 'sqlalchemy_utils' is not definedchưa:?
Walter

1
SQLAlchemy-Utilslà một gói phần mềm của bên thứ ba, bạn cần phải cài đặt nó trước:pip install sqlalchemy-utils
Berislav Lopac

Đây là cách để thực hiện, mặc dù việc di chuyển của bạn cần phải tính đến các hệ thống có giá trị UUID so với CHAR / BINARY cho uuids.
rjurney

9

Vì bạn đang sử dụng Postgres, điều này sẽ hoạt động:

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
Đây phải là câu trả lời được chấp nhận duy nhất cho những nhà phát triển sử dụng cơ sở dữ liệu PostgreSQL.
José L. Patiño

5

Đây là một cách tiếp cận dựa trên GUID bất khả tri phụ trợ từ tài liệu SQLAlchemy, nhưng sử dụng trường BINARY để lưu trữ UUID trong cơ sở dữ liệu không phải postgresql.

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
Cách sử dụng của cái này là gì?
CodeTrooper

3

Trong trường hợp có ai quan tâm, tôi đã sử dụng câu trả lời của Tom Willis, nhưng thấy hữu ích khi thêm một chuỗi vào chuyển đổi uuid.UUID trong phương thức process_bind_param

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

Bạn có thể thử viết một kiểu tùy chỉnh , ví dụ:

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),
)

Ngoài câu trả lời của Florian , còn có mục blog này . Nó trông tương tự ngoại trừ việc nó phân lớp types.TypeDecoratorthay vì types.TypeEngine. Một trong hai cách tiếp cận có lợi thế hay bất lợi so với cách khác?
Jacob Gabrielson

11
Điều này thậm chí không hoạt động, nó chỉ là công việc cắt và dán từ ví dụ loại giả từ tài liệu. Câu trả lời của Tom Willis dưới đây hay hơn nhiều.
Jesse Dhillon

Nó không cần một default=?? ví dụColumn('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)
iJames

Liên kết trỏ đến "Không tìm thấy trang", docs.sqlalchemy.org/en/13/core/… có lẽ gần với liên kết cũ
barbsan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.