2008-10-08 9 views
36

Existe-t-il un moyen de définir une colonne (clé primaire) comme UUID dans SQLAlchemy si vous utilisez PostgreSQL (Postgres)?Comment utiliser les UUID dans SQLAlchemy?

+0

Malheureusement [Type GUID backend-agnostic] (http://docs.sqlalchemy.org/en/rel_0_9/core/custom_types.html?highlight=guid#backend-agnostic-guid-type) de la documentation SQLAlchemy pour les types de colonnes ne semble pas travailler pour les clés primaires dans les moteurs de base de données SQLite. Pas aussi œcuménique que j'espérais. – adamek

Répondre

-17

Vous pouvez essayer d'écrire un custom type, par exemple:

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

Cela ne fonctionne même pas, c'est juste un travail de couper-coller à partir de l'exemple de type factice de la docs. La réponse de Tom Willis ci-dessous est bien meilleure. –

+0

En plus de [la réponse de Florian] (http://stackoverflow.com/questions/183042/how-can-i-use-uuids-in-sqlalchemy/188427#188427), il y a aussi [cette entrée de blog] (http: //blog.sadphaeton.com/2009/01/19/sqlalchemy-recipeuuid-column.html). Il semble similaire sauf qu'il sous-classe 'types.TypeDecorator' au lieu de' types.TypeEngine'. Est-ce que l'une ou l'autre approche a un avantage ou un désavantage par rapport à l'autre? –

+0

N'a-t-il pas besoin d'un 'default =?'? par exemple. 'Column ('id', UUID(), primary_key = Vrai, default = )' – iJames

50

I wrote this et le domaine est disparu, mais voici le courage ....

Peu importe la façon dont mes collègues qui se soucient vraiment de la base de données appropriée la conception se sent à propos des UUID et des GUID utilisés pour les champs clés. Je trouve souvent que j'ai besoin de le faire. Je pense qu'il a quelques avantages par rapport à l'auto-incrémentation qui en font la peine.

J'ai raffiné un type de colonne UUID pour les derniers mois et je pense que je l'ai finalement obtenu solide.

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

je crois stocker sous forme binaire (16 octets) devrait finir par être plus efficace que la représentation de chaîne (36 octets?), Et il semble y avoir une certaine indication que l'indexation 16 blocs d'octets doivent être plus efficaces dans mysql que les chaînes. Je ne m'attendrais pas à ce que ce soit pire de toute façon. Un inconvénient que j'ai trouvé est qu'au moins dans phpymyadmin, vous ne pouvez pas modifier les enregistrements car il essaye implicitement de faire une sorte de conversion de caractères pour le "select * from table where id = ..." et il y a problèmes d'affichage divers. À part ça, tout semble bien fonctionner, alors je le lance là-bas. Laissez un commentaire si vous voyez une erreur flagrante avec elle. J'accueille toutes les suggestions pour l'améliorer.

À moins de manquer quelque chose, la solution ci-dessus fonctionnera si la base de données sous-jacente a un type UUID. Si ce n'est pas le cas, vous obtiendrez probablement des erreurs lors de la création de la table. La solution que j'ai trouvée est que je visais MSSqlServer à l'origine, puis MySQL à la fin, donc je pense que ma solution est un peu plus flexible car elle semble fonctionner correctement sur mysql et sqlite. Je n'ai pas encore pris la peine de vérifier postgres.

+0

Cela aurait dû être choisi comme réponse, je suppose que vous l'avez posté beaucoup plus tard. –

+0

ouais je l'ai posté après avoir vu les renvois de la réponse de Jacob. –

+4

Notez que si vous utilisez la version 0.6 ou supérieure, l'instruction d'importation MSBinary dans la solution de Tom doit être remplacée par "from sqlalchemy.dialects.mysql.base import MSBinary". Source: http://www.mail-archive.com/[email protected]/msg18397.html –

3

Dans le cas où quelqu'un est intéressé, je me sers de réponse Tom Willis, mais a trouvé utile d'ajouter une chaîne à la conversion uuid.UUID dans la méthode 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

Voici une approche basée sur la Backend agnostic GUID à partir des documents SQLAlchemy, mais en utilisant un champ BINARY pour stocker les UUID dans les bases de données 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

Quel serait l'usage de ce soit? – codeninja

7

Si vous êtes satisfait avec une colonne 'String' ayant une valeur UUID, va ici une solution simple:

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

class MyTable(Base): 
    __tablename__ = 'my_table' 

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