web-dev-qa-db-ja.com

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

PostgreSQL を使用している場合、 [〜#〜] uuid [〜#〜] in SQLAlchemy として列(主キー)を定義する方法はありますか=(Postgres)?

60
Vasil

Sqlalchemy postgresダイアレクトはUUID列をサポートします。これは簡単です(そして質問は具体的にはpostgresです)-​​他の答えがすべてそんなに複雑な理由がわかりません。

次に例を示します。

from sqlalchemy.dialects.postgresql import UUID
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Foo(db.Model):
    id = Column(Integer, primary_key=True)
    uuid = Column(UUID(as_uuid=True), unique=True, nullable=False)
69
JDiMatteo

これを書いた そしてドメインはなくなったが、ここに勇気がある...

適切なデータベース設計を真剣に考えている同僚が、キーフィールドに使用される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でより効率的であるという兆候があるようです。とにかく悪化するとは思わないでしょう。

私が見つけた1つの欠点は、少なくともphpymyadminでは、「select * from table where id = ...」に対して何らかの種類の文字変換を暗黙的に試行し、その他の表示の問題があるため、レコードを編集できないことです。

それ以外はすべてうまくいくように見えるので、私はそれをそこに捨てています。明らかなエラーが表示された場合は、コメントを残してください。それを改善するための提案を歓迎します。

基本的なデータベースにUUIDタイプがある場合、何かが足りない限り、上記のソリューションは機能します。そうでない場合、テーブルの作成時にエラーが発生する可能性があります。私が思いついた解決策は、もともとMSSqlServerを対象としていたのに、最終的にMySqlに移行したため、mysqlとsqliteでうまく機能するように見えるため、私の解決策はもう少し柔軟だと思います。まだpostgresをチェックする必要はありません。

61
Tom Willis

列タイプについては、SQLAlchemyドキュメントの Backend-agnostic GUID Type のレシピも参照してください。

32
Kamil Kisiel

UUID値を持つ「文字列」列に満足している場合、簡単な解決策を示します。

def generate_uuid():
    return str(uuid.uuid4())

class MyTable(Base):
    __table= 'my_table'

    uuid = Column(String, name="uuid", primary_key=True, default=generate_uuid)
17
Kushal Ahmed
11
Berislav Lopac

SQLAlchemyドキュメントの Backend agnostic 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)
4
zwirbeltier

誰かが興味を持っている場合、私はトム・ウィリスの答えを使用していますが、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
3
Nemeth