如何在SQLAlchemy中使用UUID?

178 投票
11 回答
184460 浏览
提问于 2025-04-11 09:22

有没有办法在使用SQLAlchemyPostgreSQL(也就是Postgres)时,把某一列(主键)定义为UUID(通用唯一识别码)呢?

11 个回答

43

如果你觉得用一个'字符串'类型的列来存储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)
71

我写了这个,不过那个网站已经不在了,但这里有一些核心内容……

不管我的同事们对在关键字段中使用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,我还没去检查。

334

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()这个函数直接调用,而是要把callableuuid.uuid4传入列的定义中。否则,你会发现这个类的所有实例都有相同的值。更多细节可以在这里找到:

一个标量、Python可调用对象或ColumnElement表达式,表示这个列的默认值,如果在插入时没有在VALUES子句中指定这个列,就会调用它。

撰写回答