Есть ли способ определить столбец (первичный ключ) как UUID в SQLAlchemy, если вы используете PostgreSQL (Postgres)?
Как я могу использовать UUID в SQLAlchemy?
Ответ 1
Диалект sqlalchemy postgres поддерживает столбцы UUID. Это легко (а вопрос специально задан) - я не понимаю, почему все остальные ответы такие сложные.
Вот пример:
from sqlalchemy.dialects.postgresql import UUID
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Foo(db.Model):
id = db.Column(db.Integer, primary_key=True)
uuid = db.Column(UUID(as_uuid=True), unique=True, nullable=False)
Ответ 2
Я написал это, и домен ушел, но вот кишки....
Независимо от того, как мои коллеги, которые действительно заботятся о правильном дизайне базы данных, считают, что 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.
Ответ 3
См. Также рецепт для независимого от Backend GUID Type в документации по SQLAlchemy для типов столбцов.
Ответ 4
Если вас устраивает столбец "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)
Ответ 5
Я использовал UUIDType
из пакета SQLAlchemy-Utils
: http://sqlalchemy-utils.readthedocs.org/en/latest/data_types.html#module-sqlalchemy_utils.types.uuid
Ответ 6
Ниже приведен подход, основанный на Бэкэнда агностический 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 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)
Ответ 7
В случае, если кто-то заинтересован, я использовал ответ Тома Уиллиса, но нашел полезным добавить строку к преобразованию 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
Ответ 8
Поскольку вы используете 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)
Ответ 9
Вы можете попробовать написать настраиваемый тип, например:
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),
)