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?
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?
Câu trả lời:
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.uuid4
và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.
uuid.uuid4
).
Column(UUID(as_uuid=True) ...)
Column
và Integer
được nhập khẩu ở phía trên cùng của đoạn mã, hoặc đã được thay đổi để đọc db.Column
vàdb.Integer
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.
sqlalchemy.dialects.postgresql.UUID
trực tiếp. thấy Backend-agnostic GUID Loại
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)
Tôi đã sử dụng UUIDType
từ SQLAlchemy-Utils
gói: http://sqlalchemy-utils.readthedocs.org/en/latest/data_types.html#module-sqlalchemy_utils.types.uuid
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
chưa:?
SQLAlchemy-Utils
là 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
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)
Đâ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)
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
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),
)
types.TypeDecorator
thay 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?
default=?
? ví dụColumn('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)