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?
Répondre
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),
)
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. –
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? –
N'a-t-il pas besoin d'un 'default =?'? par exemple. 'Column ('id', UUID(), primary_key = Vrai, default =
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.
Cela aurait dû être choisi comme réponse, je suppose que vous l'avez posté beaucoup plus tard. –
ouais je l'ai posté après avoir vu les renvois de la réponse de Jacob. –
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 –
Voir aussi la recette de Backend-agnostic GUID Type dans la documentation de SQLAlchemy pour les types de colonnes.
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
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)
Quel serait l'usage de ce soit? – codeninja
Je l'ai utilisé le UUIDType
du paquet SQLAlchemy-Utils
: http://sqlalchemy-utils.readthedocs.org/en/latest/data_types.html#module-sqlalchemy_utils.types.uuid
J'essaie actuellement d'utiliser ceci, le problème est que j'obtiens une erreur: 'raise InvalidStatus (" notfound: {k}. (Cls = {cls}) ". Format (k = k, cls = cls))' 'alchemyjsonschema.InvalidStatus: notfound: BINAIRE (16). (cls =
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)
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