Как я могу использовать UUID в SQLAlchemy? - PullRequest
59 голосов
/ 08 октября 2008

Есть ли способ определить столбец (первичный ключ) как UUID в SQLAlchemy при использовании PostgreSQL (Postgres)?

Ответы [ 8 ]

63 голосов
/ 21 марта 2018

Диалект sqlalchemy postgres поддерживает столбцы UUID. Это легко (и вопрос, в частности, postgres) - я не понимаю, почему другие ответы так сложны.

Вот пример:

from sqlalchemy.dialects.postgresql import UUID
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Foo(db.Model):
    id = Column(Integer, primary_key=True)
    uuid = Column(UUID(as_uuid=True), unique=True, nullable=False)
60 голосов
/ 01 мая 2009

Я написал это и домен ушел, но вот мужество ....

Независимо от того, как мои коллеги, которые действительно заботятся о правильном дизайне базы данных, относятся к 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 байт?), И, как представляется, есть некоторые признаки того, что индексирование 16-байтовых блоков должно быть более эффективным в mysql, чем строки. Я бы не ожидал, что все равно будет хуже.

Один недостаток, который я обнаружил, заключается в том, что, по крайней мере, в phpymyadmin вы не можете редактировать записи, потому что он неявно пытается выполнить какое-то преобразование символов для «select * from table, где id = ...», и есть разное проблемы с отображением.

Кроме этого, кажется, все работает нормально, и поэтому я выкидываю его туда. Оставьте комментарий, если вы видите явную ошибку с ним. Я приветствую любые предложения по его улучшению.

Если я что-то не упустил, вышеуказанное решение будет работать, если базовая база данных имеет тип UUID. Если этого не произойдет, вы, скорее всего, получите ошибки при создании таблицы. Решение, которое я придумал, изначально предназначалось для MSSqlServer, а затем, в конце концов, перешло на MySql, поэтому я думаю, что мое решение немного более гибкое, так как кажется, что оно отлично работает на mysql и sqlite. Еще не потрудился проверить postgres.

30 голосов
/ 22 марта 2011

См. Также рецепт для Независимый от сервера GUID Тип в документации по SQLAlchemy для типов столбцов.

13 голосов
/ 03 января 2017

Если вас устраивает столбец 'String' со значением 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)
11 голосов
/ 01 сентября 2015
4 голосов
/ 02 июня 2015

Вот подход, основанный на независимом от идентификатора GUID из документов SQLAlchemy, но с использованием поля 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)
3 голосов
/ 12 ноября 2013

Если кому-то интересно, я использую ответ Тома Уиллиса, но нашел полезным добавить строку в преобразование uuid.UUID в методе 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
0 голосов
/ 09 октября 2008

Вы можете попробовать написать пользовательский тип , например:

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),
)
...