如果使用PostgreSQL(Postgres),是否可以在SQLAlchemy中将列(主键)定义为UUID?
如果使用PostgreSQL(Postgres),是否可以在SQLAlchemy中将列(主键)定义为UUID?
Answers:
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字节?)更有效,而且似乎有迹象表明在mysql中索引16个字节的块应该比字符串更有效。我不希望它会变得更糟。
我发现的一个缺点是,至少在phpymyadmin中,您无法编辑记录,因为它隐式地尝试对“ select * from table id = ...的表”进行某种字符转换,并且存在其他显示问题。
除此之外,一切似乎都工作正常,所以我把它扔在那里。如果看到明显的错误,请发表评论。我欢迎任何改进建议。
除非我缺少什么,否则如果基础数据库具有UUID类型,则上述解决方案将起作用。否则,创建表时可能会出错。我想出的解决方案最初是针对MSSqlServer,然后最终成为MySql,所以我认为我的解决方案更灵活一些,因为它似乎可以在mysql和sqlite上正常工作。还没有打扰检查postgres。
sqlalchemy.dialects.postgresql.UUID
即可。参见与后端无关的GUID类型
如果您对具有UUID值的'String'列满意,则可以采用以下简单解决方案:
def generate_uuid():
return str(uuid.uuid4())
class MyTable(Base):
__tablename__ = 'my_table'
uuid = Column(String, name="uuid", primary_key=True, default=generate_uuid)
我使用了UUIDType
以下SQLAlchemy-Utils
软件包中的: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
吗?
SQLAlchemy-Utils
是第三方软件包,您需要首先安装它:pip install sqlalchemy-utils
这是一种基于SQLAlchemy文档的后端不可知GUID的方法,但是使用BINARY字段将UUID存储在非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)
如果有人感兴趣,我一直在使用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),
)
default=?
吗?例如Column('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)