Bir gibi bir kolon (birincil anahtar) tanımlamak için bir yol var UUID'sine içinde SQLAlchemy kullanılıyorsa PostgreSQL (Postgres)?
Bir gibi bir kolon (birincil anahtar) tanımlamak için bir yol var UUID'sine içinde SQLAlchemy kullanılıyorsa PostgreSQL (Postgres)?
Yanıtlar:
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.
uuid.uuid4
).
Column(UUID(as_uuid=True) ...)
Column
ve Integer
kod parçacığı üstündeki ithal edildi veya okumak için değiştirildi db.Column
vedb.Integer
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.
sqlalchemy.dialects.postgresql.UUID
doğrudan kullanın . bkz Arka uç-agnostik GUID Tipi
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)
Kullandığım UUIDType
gelen SQLAlchemy-Utils
paketin: 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
üçüncü taraf bir pakettir, önce yüklemeniz gerekir:pip install sqlalchemy-utils
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)
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)
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
Ö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),
)
types.TypeDecorator
yerine 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ı?
default=?
? örneğinColumn('id', UUID(), primary_key=True, default=<someautouuidgeneratingthing>)