【发布时间】:2019-09-05 16:50:43
【问题描述】:
SQLAlchemy 允许在声明 String 列时指定长度:
foo = Column(String(10))
在 SQL 中:
foo VARCHAR(10)
我知道某些 DBMS 使用此长度值在表中创建行时分配内存。但是一些 DBMS(如 SQLite)并不关心它,并且只为了与 SQL 标准兼容而接受这种语法。但有些 DBMS(如 MySQL)需要指定它。
就个人而言,我喜欢为某些文本数据指定最大长度,因为它有助于设计 UI,因为您知道显示它所需的区域。
此外,我认为这将使我的应用程序行为在不同的 DBMS 中更加一致。
因此,我想通过对照声明的长度检查字符串/Unicode 列的长度(当声明长度时)来验证插入时字符串/Unicode 列的值。
检查约束
第一个解决方案是使用check constraint:
from sqlalchemy import CheckConstraint, Column, Integer, String, create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine("sqlite:///:memory:", echo=True)
Base = declarative_base(bind=engine)
Session = sessionmaker(bind=engine)
class Foo(Base):
__tablename__ = "Foo"
id = Column(Integer, primary_key=True)
bar = Column(String(10), CheckConstraint("LENGTH(bar) < 10"))
Base.metadata.create_all()
if __name__ == "__main__":
session = Session()
session.add(Foo(bar="a" * 20))
try:
session.commit()
except IntegrityError as e:
print(f"Failed with: {e.orig}")
它可以工作,但 SQLAlchemy 不会生成 SQL 约束表达式。因此,如果 DBMS 需要不同的语法,它可能需要一些自定义生成。
验证器
我也尝试使用 SQLAlchemy validator:
class Foo(Base):
__tablename__ = "Foo"
id = Column(Integer, primary_key=True)
bar = Column(String(10))
@validates("bar")
def check_bar_length(self, key, value):
column_type = getattr(type(self), key).expression.type
max_length = column_type.length
if len(value) > max_length:
raise ValueError(
f"Value '{value}' for column '{key}' "
f"exceed maximum length of '{max_length}'"
)
return value
try:
Foo(bar="a" * 20)
except ValueError as e:
print(f"Failed with: {e}")
现在,最大长度是从声明的长度推断出来的。
检查是在实体创建时完成的,而不是在提交时完成的。不知道会不会有问题。
自定义类型
上面显示的两种解决方案都需要对每一列应用验证。我正在寻找一种解决方案来自动检查具有声明长度的字符串/Unicode 列。
使用custom type 可能是解决方案。但它看起来像一个丑陋的 hack,因为自定义类型不是用于数据验证而是用于数据转换。
那么,您是否考虑过另一种解决方案,也许是我不知道的 SQLAlchemy 功能,这将帮助我将检查自动添加到指定了 length 的所有 String 列?
【问题讨论】:
-
我不知道你说的是什么意思,但是SQLAlchemy不会生成SQL约束表达式。似乎检查约束是在
Base.metadata.create_all()期间生成的,并且在我运行您的代码时通过抛出异常来按预期工作。 -
@IanWilson 我的示例工作正常。关键是必须手动将约束添加到每一列。
-
@IanWilson 当我写到 SQL 约束表达式不是由 SQLAlchemy 生成时,我的意思是我需要将它写成一个字符串。 SA 仅生成
CHECK关键字作为CREATE TABLE语句的一部分。 -
好吧,我明白你的意思了,我添加了一个可能是妥协的答案
标签: python validation sqlalchemy maxlength