Domanda Come posso usare gli UUID in SQLAlchemy?


C'è un modo per definire una colonna (chiave primaria) come a UUID in SQLAlchemy se si utilizza PostgreSQL (Postgres)?


44
2017-10-08 14:26


origine


risposte:


Ho scritto questo e il dominio è sparito ma ecco il coraggio ....

A prescindere da come i miei colleghi che si preoccupano veramente del design del database si sentano a proprio agio su UUID e GUID utilizzati per i campi chiave. Spesso trovo che ho bisogno di farlo. Penso che abbia alcuni vantaggi rispetto all'autoincremento che ne valga la pena.

Ho perfezionato un tipo di colonna UUID negli ultimi mesi e penso di essermi finalmente reso solido.

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

Credo che la memorizzazione come binario (16 byte) dovrebbe risultare più efficiente della rappresentazione della stringa (36 byte?), E sembra esserci qualche indicazione che l'indicizzazione di blocchi da 16 byte dovrebbe essere più efficiente in mysql rispetto alle stringhe. Non mi aspetterei che fosse peggio comunque.

Uno svantaggio che ho trovato è che almeno in phpymyadmin, non puoi modificare i record perché tenta implicitamente di fare una sorta di conversione dei caratteri per "select * from table where id = ..." e ci sono vari problemi di visualizzazione.

Oltre a questo tutto sembra funzionare bene, e quindi lo sto lanciando là fuori. Lascia un commento se vedi un errore evidente con esso. Accolgo con favore qualsiasi suggerimento per migliorarlo.

A meno che non manchi qualcosa la soluzione sopra funzionerà se il database sottostante ha un tipo UUID. In caso contrario, verrebbero probabilmente visualizzati errori quando viene creata la tabella. La soluzione che avevo in mente riguardava inizialmente MSSqlServer e poi MySql è andato alla fine, quindi penso che la mia soluzione sia un po 'più flessibile in quanto sembra funzionare bene su mysql e sqlite. Non ti sei ancora preoccupato di controllare Postgres.


55
2018-05-01 17:29



Vedi anche la ricetta per Tipo GUID indipendente dal backend nella documentazione SQLAlchemy per i tipi di colonna.


28
2018-03-21 21:51



Il dialetto sqlalchemy postgres supporta le colonne UUID. Questo è facile (e la domanda è specificamente postgres) - Non capisco perché le altre risposte siano tutte così complicate.

Ecco un esempio:

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)

12
2018-03-21 04:10



Ho usato il UUIDType dal SQLAlchemy-Utils pacchetto: http://sqlalchemy-utils.readthedocs.org/en/latest/data_types.html#module-sqlalchemy_utils.types.uuid


9
2017-09-01 13:20



Se sei soddisfatto di una colonna 'String' con valore UUID, ecco una soluzione semplice:

def generate_uuid():
   return str(uuid.uuid4())

class MyTable(Base):
   __tablename__ = 'my_table'

   uuid = Column(String, name="uuid", primary_key=True, default=generate_uuid)

9
2018-01-03 00:31



Nel caso in cui qualcuno fosse interessato, ho utilizzato Tom Willis, ma ho trovato utile aggiungere una stringa alla conversione uuid.UUID nel metodo 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

3
2017-11-12 16:57



Ecco un approccio basato sul GUID agnostico di backend dai documenti SQLAlchemy, ma utilizzando un campo BINARY per archiviare gli UUID nei database non 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
2018-06-02 18:39



Potresti provare a scrivere a tipo personalizzato, per esempio:

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

-18
2017-10-09 18:01