PostgreSQL(Postgres)を使用している場合、SQLAlchemyで列(主キー)をUUIDとして定義する方法はありますか?
PostgreSQL(Postgres)を使用している場合、SQLAlchemyで列(主キー)をUUIDとして定義する方法はありますか?
回答:
sqlalchemypostgresダイアレクトはUUID列をサポートします。これは簡単です(そして質問は特にpostgresです)-他の答えがすべてそれほど複雑な理由がわかりません。
次に例を示します。
from sqlalchemy.dialects.postgresql import UUID
from flask_sqlalchemy import SQLAlchemy
import uuid
db = SQLAlchemy()
class Foo(db.Model):
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True)
callable
uuid.uuid4
関数自体をuuid.uuid4()
。で呼び出すのではなく、を列定義に渡すことを見逃さないように注意してください。それ以外の場合は、このクラスのすべてのインスタンスで同じスカラー値になります。詳細はこちら:
この列のデフォルト値を表すスカラー、Python呼び出し可能、またはColumnElement式。この列が挿入のVALUES句で指定されていない場合、挿入時に呼び出されます。
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バイト?)よりも効率的になるはずです。また、mysqlでは文字列よりも16バイトブロックのインデックス付けの方が効率的であることが示されているようです。とにかく悪化するとは思わない。
私が見つけた1つの欠点は、少なくともphpymyadminでは、「select * from table where id = ...」に対して何らかの文字変換を暗黙的に実行しようとし、その他の表示の問題があるため、レコードを編集できないことです。
それ以外はすべてうまくいくようですので、私はそれを捨てています。明白なエラーが表示された場合は、コメントを残してください。それを改善するための提案を歓迎します。
何かが足りない場合を除いて、基になるデータベースのUUIDタイプがあれば、上記のソリューションは機能します。そうでない場合は、テーブルの作成時にエラーが発生する可能性があります。私が思いついたソリューションは、もともとMSSqlServerをターゲットにしていて、最終的にMySqlを使用したため、mysqlとsqliteで正常に機能するように見えるため、ソリューションはもう少し柔軟だと思います。まだpostgresをチェックすることを気にしませんでした。
sqlalchemy.dialects.postgresql.UUID
直接使用してください。バックエンドに依存しないGUIDタイプ
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)
これは、SQLAlchemyドキュメントのバックエンドに依存しないGUIDに基づくアプローチですが、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)
誰かが興味を持っている場合に備えて、私はTom Willisの回答を使用していますが、process_bind_paramメソッドでuuid.UUID変換に文字列を追加すると便利です。
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>)