UUID'leri SQLAlchemy'de nasıl kullanabilirim?


94

Bir gibi bir kolon (birincil anahtar) tanımlamak için bir yol var UUID'sine içinde SQLAlchemy kullanılıyorsa PostgreSQL (Postgres)?


2
Ne yazık ki , sütun türleri için SQLAlchemy belgelerinden Arka uçtan bağımsız GUID Türü , SQLite veritabanı motorlarındaki birincil anahtarlar için çalışmıyor gibi görünüyor. Umduğum kadar ekümenik değil.
adamek

SQLAlchemy araçları UUIDType dekoratörü sağlar , tekerleği yeniden icat etmeye gerek yoktur
Filipe Bezerra de Sousa

Yanıtlar:


152

Sqlalchemy postgres lehçesi UUID sütunlarını destekler. Bu kolay (ve soru özellikle postgres) - Diğer yanıtların neden bu kadar karmaşık olduğunu anlamıyorum.

İşte bir örnek:

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İşlevi çağırmak yerine sütun tanımına geçmeyi kaçırmamaya dikkat edin uuid.uuid4(). Aksi takdirde, bu sınıfın tüm örnekleri için aynı skaler değere sahip olursunuz. Daha fazla ayrıntı burada :

Bu sütun için varsayılan değeri temsil eden skaler, Python çağrılabilir veya ColumnElement ifadesi; bu sütun, ekin VALUES yan tümcesinde belirtilmemişse, ekleme sırasında çağrılır.


6
Sana tamamiyle katılıyorum. Diğer cevaplardan bazıları diğer veritabanları için harika, ancak son yazılar için bu en temiz çözüm. (Bir varsayılan olarak da ayarlayabilirsiniz uuid.uuid4).
pacha

1
Bir MWE sağlayabilir misiniz? Ya da belki flask_sqlalchemy'deki serileştirici UUID türünü anlıyor? Pastebin'deki kod aşağıdaki hatalar, pastebin.com/hW8KPuYw
Brandon Dube

1
boşver, stdlib'den UUID nesnelerini kullanmak istiyorsan, yapColumn(UUID(as_uuid=True) ...)
Brandon Dube

1
Teşekkür ederim! Eğer güzel olabilir Columnve Integerkod parçacığı üstündeki ithal edildi veya okumak için değiştirildi db.Columnvedb.Integer
Greg Sadetsky

1
Gerek yok @nephanth
Filipe Bezerra de Sousa

64

Bunu yazdım ve alan adı gitti ama işte cesaret ...

Doğru veritabanı tasarımını gerçekten önemseyen meslektaşlarımın UUID'ler ve anahtar alanlar için kullanılan GUID'ler hakkında ne düşündüklerine bakılmaksızın. Sık sık bunu yapmam gerektiğini düşünüyorum. Otomatik artırmaya göre buna değecek bazı avantajları olduğunu düşünüyorum.

Son birkaç aydır bir UUID sütun türünü iyileştiriyorum ve sanırım sonunda sağlam buldum.

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)))

İkili (16 bayt) olarak depolamanın dize gösteriminden (36 bayt?) Daha verimli olması gerektiğine inanıyorum ve 16 baytlık blokları indekslemenin dizelerden daha verimli olması gerektiğine dair bazı göstergeler var gibi görünüyor. Zaten daha kötü olmasını beklemiyorum.

Bulduğum bir dezavantaj, en azından phpymyadmin'de, id = ... olduğu tablodan "seç *" için örtük olarak bir tür karakter dönüşümü yapmaya çalıştığı ve çeşitli görüntüleme sorunları olduğu için kayıtları düzenleyemezsiniz.

Bunun dışında her şey yolunda görünüyor ve bu yüzden onu oraya atıyorum. Bununla ilgili göze çarpan bir hata görürseniz bir yorum bırakın. Bunu geliştirmek için herhangi bir öneriyi memnuniyetle karşılıyorum.

Bir şeyi kaçırmadığım sürece, temeldeki veritabanı bir UUID türüne sahipse yukarıdaki çözüm işe yarayacaktır. Olmazsa, tablo oluşturulduğunda büyük olasılıkla hatalar alırsınız. Bulduğum çözüm, başlangıçta MSSqlServer'ı hedefliyordu ve sonunda MySql'e geçti, bu yüzden çözümümün mysql ve sqlite üzerinde iyi çalıştığı için biraz daha esnek olduğunu düşünüyorum. Henüz postaları kontrol etme zahmetine girmedim.


evet, Jacob'ın cevabından gelen tavsiyeleri gördükten sonra gönderdim.
Tom Willis

4
Sürüm 0.6 veya üzerini kullanıyorsanız, Tom'un çözümündeki MSBinary import deyiminin "from sqlalchemy.dialects.mysql.base import MSBinary" olarak değiştirilmesi gerektiğini unutmayın. Kaynak: mail-archive.com/sqlalchemy@googlegroups.com/msg18397.html
Cal Jacobson

2
"Bunu ben yazdım" ölü bir bağlantıdır.
2013

2

2
@codeninja postgresql zaten yerel bir UUID türüne sahiptir, bu nedenle sqlalchemy.dialects.postgresql.UUIDdoğrudan kullanın . bkz Arka uç-agnostik GUID Tipi
cowbert

24

UUID değerine sahip bir 'String' sütunundan memnunsanız, işte basit bir çözüm:

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
Onları desteklemeyen gerçekten garip bir veritabanı kullanmadığınız sürece UUID'yi dize olarak saklamayın. aksi takdirde, tüm verilerinizi dizeler olarak depolayabilirsiniz ...;)
Nick

@Nick neden? dezavantajı nedir?
rayepps

6
@rayepps - pek çok dezavantaj var - akılda kalan birkaçı: size - string uuid iki kat daha fazla yer kaplıyor - 16 bayt'a karşı 32 karakter - herhangi bir formatlayıcı dahil değil. İşlem süresi - daha fazla bayt = veri kümeniz büyüdükçe CPU tarafından daha fazla işlem süresi. uuid dize biçimleri dile göre farklılık gösterir ve gerekli ek çeviriler eklenir. Birinin sütunu kötüye kullanması daha kolaydır, çünkü içine her şeyi koyabilirsiniz, uuids olmayan şeyler. Bu başlamak için yeterli olmalı.
Nick

Performans sorunları için Dizeleri bir uuid için sütun olarak kullanmamalısınız. Bir İkili (16) daha çok tavsiye edilir.
Cyril N.

19

Şu anda bunu kullanmaya çalışıyorum, sorun şu ki bir hata alıyorum: raise InvalidStatus("notfound: {k}. (cls={cls})".format(k=k, cls=cls)) alchemyjsonschema.InvalidStatus: notfound: BINARY(16). (cls=<class 'sqlalchemy_utils.types.uuid.UUIDType'>)
CodeTrooper

Hatayı aldınız mı NameError: name 'sqlalchemy_utils' is not defined?
Walter

1
SQLAlchemy-Utilsüçüncü taraf bir pakettir, önce yüklemeniz gerekir:pip install sqlalchemy-utils
Berislav Lopac

Geçişlerinizin uuids için UUID ve CHAR / BINARY değerleri olan sistemleri hesaba katması gerekse de, işte bu yoldur.
rjurney

9

Postgres kullandığınız için bu çalışmalıdır:

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 veritabanı kullanan geliştiriciler için kabul edilen tek cevap bu olmalıdır.
José L. Patiño

5

Burada, SQLAlchemy belgelerinden Arka Uç agnostik GUID'sini temel alan ancak postgresql olmayan veritabanlarında UUID'leri depolamak için BINARY alanını kullanan bir yaklaşım .

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
Bunun kullanımı ne olabilir?
CodeTrooper

3

Birinin ilgilenmesi durumunda Tom Willis cevabını kullanıyorum, ancak process_bind_param yönteminde uuid.UUID dönüşümüne bir dize eklemek için yararlı buldum

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

Örneğin, özel bir tür yazmayı deneyebilirsiniz :

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),
)

Florian'ın cevabına ek olarak , bu blog yazısı da var . Bunun types.TypeDecoratoryerine alt sınıflar olması dışında benzer görünüyor types.TypeEngine. Her iki yaklaşımın da diğerine göre avantajı veya dezavantajı var mı?
Jacob Gabrielson

11
Bu bile işe yaramıyor, bu sadece dokümanlardaki sahte tip örneğinden bir kes ve yapıştır işi. Tom Willis'in aşağıdaki cevabı çok daha iyi.
Jesse Dhillon

Bir ihtiyacı yok mu default=?? örneğinColumn('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)
iJames

"Sayfa bulunamadı" bağlantısını işaret ediyor, docs.sqlalchemy.org/en/13/core/… muhtemelen eskisine yakın
barbsan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.