2008-10-08 12 views
36

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

+0

Purtroppo [Tipo back-end-agnostico] (http://docs.sqlalchemy.org/en/rel_0_9/core/custom_types.html?highlight=guid#backend-agnostic-guid-type) dalla documentazione SQLAlchemy per i tipi di colonna non sembra lavorare per chiavi primarie nei motori di database SQLite. Non proprio ecumenico come speravo. – adamek

risposta

-17

Si potrebbe provare a scrivere un custom type, 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), 
) 
+9

Questo non ha nemmeno lavoro, è solo un lavoro di taglia e incolla dal tipo di esempio fittizio dalla documentazione. La risposta di Tom Willis è molto meglio. –

+0

Oltre a [risposta di Florian] (http://stackoverflow.com/questions/183042/how-can-i-use-uuids-in-sqlalchemy/188427#188427), c'è anche [questo blog] (http: //blog.sadphaeton.com/2009/01/19/sqlalchemy-recipeuuid-column.html). Sembra simile con la differenza che le sottoclassi 'types.TypeDecorator' invece di' types.TypeEngine'. L'uno o l'altro approccio ha un vantaggio o uno svantaggio rispetto all'altro? –

+0

Non ha bisogno di un 'default =?'? per esempio. 'Colonna ('id', UUID(), primary_key = True, default = )' – iJames

50

I wrote this e il dominio è andato, ma qui è il coraggio ....

Indipendentemente da come i miei colleghi che realmente si preoccupano e propria banca dati aspetto del design di 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.

Sono stato Perfezionamento di una tipo di colonna UUID per gli ultimi mesi e penso di aver finalmente capito 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))) 

ritengo memorizzazione come binario (16 byte) dovrebbe finire per essere più efficiente rispetto alla rappresentazione di stringa (36 byte?) E sembra esserci qualche indicazione che l'indicizzazione 16 blocchi byte dovrebbe essere più efficiente in mysql di stringhe. Non mi aspetterei che fosse peggio comunque.

Uno svantaggio che ho trovato è che almeno in phpymyadmin, non è possibile modificare i record perché tenta implicitamente di fare una sorta di conversione dei caratteri per "select * dalla tabella dove id = ..." e non c'è vari problemi di visualizzazione.

Oltre a ciò 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 mi manca qualcosa la soluzione di cui sopra funziona se il database sottostante ha un tipo di 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.

+0

Questo avrebbe dovuto essere scelta come la risposta, credo che hai postato molto più tardi. –

+0

sì, l'ho postato dopo aver visto i referral dalla risposta di Jacob. –

+4

Si noti che se si sta utilizzando la versione 0.6 o superiore, l'istruzione import MSBinary in soluzione di Tom dovrebbe essere cambiato in "da sqlalchemy.dialects.mysql.base MSBinary importazione". Fonte: http://www.mail-archive.com/[email protected]/msg18397.html –

3

Nel caso in cui qualcuno è interessato, ho usato Tom Willis risposta, ma hanno 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

Ecco un approccio basato sul Backend agnostic GUID 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) 
+0

Quale sarebbe l'utilizzo di questo? – codeninja

7

Se si è soddisfatti con la colonna di un 'String' avere valore UUID, ecco qui 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) 
Problemi correlati