SQLAlchemyでUUIDを使用するにはどうすればよいですか?


95

PostgreSQL(Postgres)を使用している場合、SQLAlchemyで列(主キー)をUUIDとして定義する方法はありますか?


2
残念ながら、列タイプのSQLAlchemyドキュメントにあるバックエンドに依存しないGUIDタイプは、SQLiteデータベースエンジンの主キーでは機能しないようです。私が望んでいたほどエキュメニカルではありません。
adamek 2012

SQLAlchemy utilsはUUIDTypeデコレータを提供し、車輪の再発明を行う必要はありません
Filipe Bezerra

回答:


158

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句で指定されていない場合、挿入時に呼び出されます。


6
完全にあなたと同意します。他の回答のいくつかは他のデータベースにとってはかっこいいですが、postgresにとってはこれが最もクリーンなソリューションです。(デフォルトをとして設定することもできますuuid.uuid4)。
pacha 2018

1
MWEを提供できますか?または、flask_sqlalchemyのシリアライザーがUUIDタイプを理解している可能性がありますか?エラーの下のペーストビンのコード、pastebin.com / hW8KPuYw
Brandon Dube

1
stdlibのUUIDオブジェクトを使用する場合は、気にしないでくださいColumn(UUID(as_uuid=True) ...)
Brandon Dube 2018年

1
ありがとうございました!場合、それはいいかもしれないColumnとは、Integerコードスニペットの上部に輸入された、または読み取りに変更されたdb.Columndb.Integer
グレッグSadetsky

2
いいえ、@ nephanthは必要ありません
Filipe Bezerra

65

私はこれを書き、ドメインはなくなりましたが、ここに根性があります。

適切なデータベース設計を本当に気にかけている私の同僚が、キーフィールドに使用される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をチェックすることを気にしませんでした。


ええ、ジェイコブの答えからの紹介を見た後に投稿しました。
トムウィリス

4
バージョン0.6以降を使用している場合は、TomのソリューションのMSBinaryimportステートメントを「fromsqlalchemy.dialects.mysql.baseimportMSBinary」に変更する必要があることに注意してください。出典:mail-archive.com/sqlalchemy@googlegroups.com/msg18397.html
Cal Jacobson

2
「私はこれを書いた」はデッドリンクです。
julx 2013年


2
@codeninja postgresqlにはすでにネイティブUUIDタイプがあるため、sqlalchemy.dialects.postgresql.UUID直接使用してください。バックエンドに依存しないGUIDタイプ
cowbert 2017年

24

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
UUIDをサポートしていない本当に奇妙なデータベースを使用している場合を除いて、UUIDを文字列として保存しないでください。それ以外の場合は、すべてのデータを文字列として保存する可能性があります...;)
Nick

@ニックなぜ?欠点は何ですか?
rayepps

6
@ rayepps-多くの欠点があります-頭から離れたところがいくつかあります:サイズ-文字列uuidはスペースの2倍を占めます-16バイト対32文字-フォーマッタは含まれていません。処理時間-バイト数が増える=データセットが大きくなるにつれてCPUによる処理時間が長くなります。uuid文字列形式は言語によって異なり、追加の必要な翻訳が追加されます。誰かが列を誤用しやすいのは、そこに何でも入れることができるので、uuidではないものです。開始するにはそれで十分です。
ニック

パフォーマンスの問題のために、uuidの列として文字列を使用しないでください。Binary(16)をお勧めします。
CyrilN.20年

19

私はパッケージUUIDTypeからを使用しましたSQLAlchemy-Utilshttp//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'>)
CodeTrooper 2017年

あなたたちはエラーを受け取りましたNameError: name 'sqlalchemy_utils' is not definedか?
ウォルター

1
SQLAlchemy-Utilsサードパーティ製のパッケージである、あなたが最初にそれをインストールする必要があります:pip install sqlalchemy-utils
Berislav LOPAC

これが進むべき道ですが、移行では、uuidに対してUUIDとCHAR / BINARYの値を持つアカウントまたはシステムが必要です。
rjurney

9

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)

1
これは、PostgreSQLデータベースを使用している開発者にとって唯一受け入れられている答えです。
ホセ・L.パティーニョ

6

これは、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)

1
これの使用法は何でしょうか?
codeTrooper 2017年

3

誰かが興味を持っている場合に備えて、私は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

-19

たとえば、カスタムタイプを書いてみることができます。

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ます。どちらのアプローチにも、他のアプローチよりも長所と短所がありますか?
Jacob Gabrielson

11
これは機能しません。ドキュメントのダミータイプの例からのカットアンドペーストジョブです。以下のトムウィリスの答えははるかに優れています。
Jesse Dhillon 2010

必要ありませんdefault=?か?例Column('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)
iJames 2016年

「ページが見つかりません」、へのリンクポイントdocs.sqlalchemy.org/en/13/core/...が近い古いものにpropablyある
barbsan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.