Есть ли способ определить столбец (первичный ключ) как UUID в SQLAlchemy при использовании PostgreSQL (Postgres)?
python
postgresql
orm
sqlalchemy
uuid
Василь
источник
источник
Ответы:
Диалект postgres sqlalchemy поддерживает столбцы UUID. Это легко (и вопрос конкретно о postgres) - я не понимаю, почему другие ответы все такие сложные.
Вот пример:
from sqlalchemy.dialects.postgresql import UUID from flask_sqlalchemy import SQLAlchemy import uuid db = SQLAlchemy() class Foo(db.Model): # id = db.Column(db.Integer, primary_key=True) id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False)
Будьте осторожны, чтобы не пропустить передачу
callable
uuid.uuid4
в определение столбца вместо вызова самой функции с помощьюuuid.uuid4()
. В противном случае у вас будет одно и то же скалярное значение для всех экземпляров этого класса. Подробнее здесь :источник
uuid.uuid4
).Column(UUID(as_uuid=True) ...)
Column
иInteger
были импортированы в верхней части фрагмента кода, или были изменены на чтениеdb.Column
иdb.Integer
Я написал это, и домен исчез, но вот мужество ....
Независимо от того, как мои коллеги, которые действительно заботятся о правильном проектировании базы данных, относятся к UUID и GUID, используемым для ключевых полей. Я часто нахожу, что мне это нужно. Я думаю, что у него есть некоторые преимущества перед автоинкрементом, и оно того стоит.
Я уточнял тип столбца UUID в течение последних нескольких месяцев, и я думаю, что наконец-то понял его.
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)))
Я считаю, что хранение в двоичном формате (16 байтов) должно быть более эффективным, чем строковое представление (36 байтов?). И, похоже, есть некоторые признаки того, что индексирование 16-байтовых блоков должно быть более эффективным в mysql, чем строки. В любом случае я бы не ожидал, что будет хуже.
Один недостаток, который я обнаружил, заключается в том, что, по крайней мере, в phpymyadmin вы не можете редактировать записи, потому что он неявно пытается выполнить какое-то преобразование символов для «select * from table where id = ...» и есть разные проблемы с отображением.
В остальном вроде все работает нормально, так что бросаю. Оставьте комментарий, если увидите в нем явную ошибку. Я приветствую любые предложения по его улучшению.
Если мне что-то не хватает, вышеуказанное решение будет работать, если базовая база данных имеет тип UUID. В противном случае вы, вероятно, получите ошибки при создании таблицы. Решение, которое я придумал, я изначально нацеливался на MSSqlServer, а затем в конце перешел на MySql, поэтому я думаю, что мое решение немного более гибкое, поскольку, похоже, оно отлично работает с mysql и sqlite. Еще не удосужился проверить postgres.
источник
sqlalchemy.dialects.postgresql.UUID
напрямую. см. Backend-Если вас устраивает столбец String со значением UUID, вот простое решение:
def generate_uuid(): return str(uuid.uuid4()) class MyTable(Base): __tablename__ = 'my_table' uuid = Column(String, name="uuid", primary_key=True, default=generate_uuid)
источник
Я использовал
UUIDType
изSQLAlchemy-Utils
пакета: http://sqlalchemy-utils.readthedocs.org/en/latest/data_types.html#module-sqlalchemy_utils.types.uuidисточник
raise InvalidStatus("notfound: {k}. (cls={cls})".format(k=k, cls=cls))
alchemyjsonschema.InvalidStatus: notfound: BINARY(16). (cls=<class 'sqlalchemy_utils.types.uuid.UUIDType'>)
NameError: name 'sqlalchemy_utils' is not defined
:?SQLAlchemy-Utils
это сторонний пакет, вам нужно сначала установить его:pip install sqlalchemy-utils
Поскольку вы используете Postgres, это должно работать:
from app.main import db from sqlalchemy.dialects.postgresql import UUID class Foo(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True) name = db.Column(db.String, nullable=False)
источник
Вот подход, основанный на независимом от Backend GUID из документации SQLAlchemy, но с использованием поля BINARY для хранения UUID в базах данных, отличных от 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)
источник
В случае, если кому-то интересно, я использовал ответ Тома Уиллиса, но нашел полезным добавить строку в преобразование uuid.UUID в методе 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
источник
Вы можете попробовать написать собственный тип , например:
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), )
источник
types.TypeDecorator
вместоtypes.TypeEngine
. Есть ли у одного из подходов преимущество или недостаток перед другим?default=?
? напримерColumn('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)