มีวิธีกำหนดคอลัมน์ (คีย์หลัก) เป็นUUIDในSQLAlchemy หรือไม่ถ้าใช้PostgreSQL (Postgres)
มีวิธีกำหนดคอลัมน์ (คีย์หลัก) เป็นUUIDในSQLAlchemy หรือไม่ถ้าใช้PostgreSQL (Postgres)
คำตอบ:
ภาษาของ sqlalchemy postgres รองรับคอลัมน์ UUID นี่เป็นเรื่องง่าย (และคำถามนี้เป็นคำถามเฉพาะ) - ฉันไม่เข้าใจว่าทำไมคำตอบอื่น ๆ ถึงซับซ้อนขนาดนี้
นี่คือตัวอย่าง:
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
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 ไบต์?) และดูเหมือนว่าจะมีข้อบ่งชี้บางอย่างว่าการสร้างดัชนีบล็อก 16 ไบต์ควรมีประสิทธิภาพใน mysql มากกว่าสตริง ฉันจะไม่คาดหวังว่ามันจะแย่ลงอีกต่อไป
ข้อเสียอย่างหนึ่งที่ฉันพบคืออย่างน้อยใน phpymyadmin คุณไม่สามารถแก้ไขระเบียนได้เนื่องจากโดยปริยายพยายามที่จะทำการแปลงอักขระบางประเภทสำหรับ "select * from table where id = ... " และมีปัญหาในการแสดงผลอื่น ๆ
นอกเหนือจากนั้นทุกอย่างดูเหมือนจะทำงานได้ดีดังนั้นฉันจึงโยนมันออกไปที่นั่น แสดงความคิดเห็นหากคุณเห็นข้อผิดพลาดที่ชัดเจน ฉันยินดีรับข้อเสนอแนะสำหรับการปรับปรุง
หากฉันไม่ได้ทำอะไรบางอย่างวิธีแก้ปัญหาข้างต้นจะใช้งานได้หากฐานข้อมูลที่สำคัญมีประเภท UUID หากไม่เป็นเช่นนั้นคุณอาจได้รับข้อผิดพลาดเมื่อสร้างตาราง วิธีแก้ปัญหาที่ฉันคิดขึ้นมาคือกำหนดเป้าหมาย MSSqlServer ในตอนแรกจากนั้นไปที่ MySql ในที่สุดดังนั้นฉันคิดว่าโซลูชันของฉันมีความยืดหยุ่นมากกว่าเล็กน้อยเนื่องจากดูเหมือนว่าจะทำงานได้ดีบน mysql และ sqlite ยังไม่ได้ใส่ใจในการตรวจสอบ postgres เลย
sqlalchemy.dialects.postgresql.UUID
โดยตรง ดูBackend-agnostic GUID Type
หากคุณพอใจกับคอลัมน์ 'String' ที่มีค่า 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)
นี่คือแนวทางตามGUID ที่ไม่เชื่อเรื่องพระเจ้าของแบ็กเอนด์จากเอกสาร SQLAlchemy แต่ใช้ฟิลด์ 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 แต่พบว่ามีประโยชน์ในการเพิ่มสตริงในการแปลง uuid.UUID ในเมธอด process_bind_param
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>)