123

Is there a way to define a column (primary key) as a UUID in SQLAlchemy if using PostgreSQL (Postgres)?

4

9 回答 9

224

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)

注意不要错过将 传递callable uuid.uuid4到列定义中,而不是使用 . 调用函数本身uuid.uuid4()。否则,对于此类的所有实例,您将拥有相同的标量值。更多细节在这里

表示此列的默认值的标量、Python 可调用或 ColumnElement 表达式,如果在插入的 VALUES 子句中未指定此列,则将在插入时调用该表达式。

于 2018-03-21T04:10:23.180 回答
67

我写了这个,域名消失了,但这是胆量....

不管我真正关心正确数据库设计的同事如何看待用于关键字段的 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 中,您不能编辑记录,因为它隐式地尝试为“从 id = ... 的表中选择 *”进行某种字符转换,并且存在各种显示问题。

除此之外,一切似乎都很好,所以我把它扔在那里。如果您看到明显的错误,请发表评论。我欢迎任何改进它的建议。

除非我遗漏了什么,否则如果底层数据库具有 UUID 类型,上述解决方案将起作用。如果没有,您可能会在创建表时遇到错误。我想出的解决方案最初是针对 MSSqlServer,然后最终使用了 MySql,所以我认为我的解决方案更灵活一些,因为它似乎在 mysql 和 sqlite 上运行良好。还没有打扰检查postgres。

于 2009-05-01T17:29:48.340 回答
33

如果您对具有 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)
于 2017-01-03T00:31:07.150 回答
21

我已经使用UUIDTypeSQLAlchemy-Utils包装中的。

于 2015-09-01T13:20:16.400 回答
10

由于您使用的是 Postgres,这应该可以工作:

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)
于 2019-09-13T07:23:14.807 回答
7

这是一种基于 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)
于 2015-06-02T18:39:59.610 回答
3

如果有人感兴趣,我一直在使用 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
于 2013-11-12T16:57:36.457 回答
0

我们可以使用UUIDType

from sqlalchemy_utils import UUIDType
from sqlalchemy import VARCHAR,
class User(Base):
    __tablename__ = "user"
    id = Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4())
    name = Column(VARCHAR(length=30))
    first_name = Column(VARCHAR(length=30))
    last_name = Column(VARCHAR(length=30))

更多细节我们可以参考官方文档

于 2021-08-31T14:06:31.510 回答
-21

您可以尝试编写自定义类型,例如:

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),
)
于 2008-10-09T18:01:58.063 回答