如何在SQLAlchemy中使用UUID?
有没有办法在使用SQLAlchemy和PostgreSQL(也就是Postgres)时,把某一列(主键)定义为UUID(通用唯一识别码)呢?
11 个回答
如果你觉得用一个'字符串'类型的列来存储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)
我写了这个,不过那个网站已经不在了,但这里有一些核心内容……
不管我的同事们对在关键字段中使用UUID和GUID有多么看重数据库设计,我发现我经常需要用到它们。我觉得它们相比自动递增的ID有一些优势,值得使用。
过去几个月我一直在改进一个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)))
我认为把UUID存储为二进制格式(16字节)应该比字符串格式(36字节)更高效。而且有迹象表明,在MySQL中,索引16字节的块比字符串更有效率。反正我不指望它会更差。
我发现的一个缺点是,至少在phpMyAdmin中,你无法编辑记录,因为它会隐式尝试进行某种字符转换,比如“select * from table where id =...”时,会出现各种显示问题。
除此之外,其他一切似乎都正常,所以我把这个分享出来。如果你发现有什么明显的错误,请留言告诉我。我欢迎任何改进的建议。
除非我漏掉了什么,否则上述解决方案在底层数据库支持UUID类型时会有效。如果不支持,你在创建表时可能会遇到错误。我最初是针对MSSqlServer设计的,最后转向了MySQL,所以我觉得我的解决方案更灵活,因为它在MySQL和SQLite上都能很好地工作。至于Postgres,我还没去检查。
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(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
要小心,不要把uuid.uuid4()
这个函数直接调用,而是要把callable
的uuid.uuid4
传入列的定义中。否则,你会发现这个类的所有实例都有相同的值。更多细节可以在这里找到:
一个标量、Python可调用对象或ColumnElement表达式,表示这个列的默认值,如果在插入时没有在VALUES子句中指定这个列,就会调用它。