2008-10-08 12 views
36

¿Hay alguna manera de definir una columna (clave principal) como UUID en SQLAlchemy si usa PostgreSQL (Postgres)?¿Cómo puedo usar los UUID en SQLAlchemy?

+0

Desafortunadamente [Tipo de GUID independiente del backend] (http://docs.sqlalchemy.org/en/rel_0_9/core/custom_types.html?highlight=guid#backend-agnostic-guid-type) de la documentación de SQLAlchemy para tipos de columnas no parece para trabajar con claves primarias en motores de base de datos SQLite. No es tan ecuménico como esperaba. – adamek

Respuesta

-17

Usted podría intentar escribir un custom type, por ejemplo:

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

Esto ni siquiera funciona, es solo un trabajo de cortar y pegar del ejemplo de tipo ficticio de los documentos. La respuesta de Tom Willis a continuación es mucho mejor. –

+0

Además de [la respuesta de Florian] (http://stackoverflow.com/questions/183042/how-can-i-use-uuids-in-sqlalchemy/188427#188427), también hay [esta entrada en el blog] (http: //blog.sadphaeton.com/2009/01/19/sqlalchemy-recipeuuid-column.html). Se ve similar, excepto que subclasifica 'types.TypeDecorator' en lugar de' types.TypeEngine'. ¿Alguno de los enfoques tiene una ventaja o desventaja sobre el otro? –

+0

¿No necesita un 'default =?'? p.ej. 'Column ('id', UUID(), primary_key = True, default = )' – iJames

50

I wrote this y el dominio se ha ido, pero aquí está la tripa ....

Independientemente de cómo mis colegas que realmente se preocupan por la base de datos adecuada sensación de diseño sobre UUID y GUID utilizados para campos clave. A menudo me parece que necesito hacerlo. Creo que tiene algunas ventajas sobre la autoincrementación que hacen que valga la pena.

He estado refinando un tipo de columna UUID durante los últimos meses y creo que finalmente lo tengo sólido.

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

creo almacenar en formato binario (16 bytes) debe llegar a ser más eficiente que la representación de cadena (36 bytes?), Y parece que hay algunos indicios de que la indexación 16 bloques de bytes deben ser más eficientes en MySQL de cuerdas No esperaría que sea peor de todos modos.

Una desventaja que he encontrado es que al menos en phpymyadmin, no puede editar registros porque implícitamente intenta hacer algún tipo de conversión de caracteres para el "select * from table where id = ..." y hay misceláneos problemas de visualización.

Aparte de eso, todo parece funcionar bien, así que lo estoy tirando. Deja un comentario si ves un error evidente con él. Agradezco cualquier sugerencia para mejorarlo.

A menos que me falta algo, la solución anterior funcionará si la base de datos subyacente tiene un tipo de UUID. Si no es así, es probable que obtenga errores cuando se crea la tabla. La solución que se me ocurrió. Mi objetivo era MSSqlServer originalmente y luego fue MySql al final, por lo que creo que mi solución es un poco más flexible, ya que parece funcionar bien en mysql y sqlite. No me he molestado en verificar postgres todavía.

+0

Esto debería haber sido elegido como la respuesta, supongo que lo publicaste mucho más tarde. –

+0

Sí, lo publiqué después de ver las referencias de la respuesta de Jacob. –

+4

Tenga en cuenta que si usa la versión 0.6 o superior, la instrucción de importación de MSBinary en la solución de Tom debe cambiarse a "de sqlalchemy.dialects.mysql.base import MSBinary". Fuente: http://www.mail-archive.com/[email protected]/msg18397.html –

3

En caso de que a alguien le interesa, he estado usando respuesta Tom Willis, pero no encontraron útil añadir una cadena a la conversión uuid.UUID en el método 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

Aquí es un enfoque basado en la Backend agnostic GUID desde los documentos de SQLAlchemy, pero utilizando un campo BINARIO para almacenar los UUID en bases de datos no 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

¿Cuál sería el uso de esto? – codeninja

7

Si está satisfecho con la columna una 'cadena' que tiene un valor UUID, aquí va una solución sencilla:

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

class MyTable(Base): 
    __tablename__ = 'my_table' 

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