繁体   English   中英

如何验证SQLAlchemy ORM中的列数据类型?

[英]How can I verify Column data types in the SQLAlchemy ORM?

我想使用SQLAlchemy ORM来确保值是其列的正确类型。

例如,假设我有一个Integer列。 我尝试插入值“ hello”,它不是有效的整数。 SQLAlchemy将允许我执行此操作。 只是稍后,当我执行session.commit() ,它才会引发异常: sqlalchemy.exc.DataError: (DataError) invalid input syntax integer: "hello"…

我要添加成批的记录,出于性能原因,我不想在每个add(…)之后提交。

那么我该如何:

  • 我执行session.add(…)立即引发异常
  • 还是在将我要插入的值添加到批处理之前 ,确保将其转换为目标Column数据类型?
  • 或采取其他任何方法来防止不良记录破坏整个commit()

SQLAlchemy没有内置此功能,因为它符合DBAPI /数据库的要求,是验证和强制值的最佳,最有效的来源。

为了构建自己的验证,通常使用TypeDecorator或ORM级验证。 TypeDecorator的优点是它在核心运行并且可以相当透明,尽管它仅在实际发出SQL时发生。

为了尽早进行验证和强制,这是在ORM级别上。

验证可以通过@validates在ORM层上@validates

http://docs.sqlalchemy.org/en/latest/orm/mapped_attributes.html#simple-validators

@validates使用的事件系统也可以直接使用。 您可以编写一个通用的解决方案,将您选择的验证器链接到要映射的类型:

from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import event
import datetime

Base= declarative_base()

def validate_int(value):
    if isinstance(value, basestring):
        value = int(value)
    else:
        assert isinstance(value, int)
    return value

def validate_string(value):
    assert isinstance(value, basestring)
    return value

def validate_datetime(value):
    assert isinstance(value, datetime.datetime)
    return value

validators = {
    Integer:validate_int,
    String:validate_string,
    DateTime:validate_datetime,
}

# this event is called whenever an attribute
# on a class is instrumented
@event.listens_for(Base, 'attribute_instrument')
def configure_listener(class_, key, inst):
    if not hasattr(inst.property, 'columns'):
        return
    # this event is called whenever a "set" 
    # occurs on that instrumented attribute
    @event.listens_for(inst, "set", retval=True)
    def set_(instance, value, oldvalue, initiator):
        validator = validators.get(inst.property.columns[0].type.__class__)
        if validator:
            return validator(value)
        else:
            return value


class MyObject(Base):
    __tablename__ = 'mytable'

    id = Column(Integer, primary_key=True)
    svalue = Column(String)
    ivalue = Column(Integer)
    dvalue = Column(DateTime)


m = MyObject()
m.svalue = "ASdf"

m.ivalue = "45"

m.dvalue = "not a date"

验证和强制也可以使用TypeDecorator在类型级别上构建,尽管仅当发出SQL时才如此,例如本示例将utf-8字符串强制转换为unicode:

http://docs.sqlalchemy.org/zh_CN/latest/core/custom_types.html#coercing-encoded-strings-to-unicode

在改善@zzzeek的答案上,我建议以下解决方案:

from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.event import listen_for

Base = declarative_base()

@listens_for(Base, 'attribute_instrument')
def configure_listener(table_cls, attr, col_inst):
    if not hasattr(col_inst.property, 'columns'):
        return
    validator = getattr(col_inst.property.columns[0].type, 'validator', None)
    if validator:
        # Only decorate columns, that need to be decorated
        @listens_for(col_inst, "set", retval=True)
        def set_(instance, value, oldvalue, initiator):
            return validator(value)

这使您可以执行以下操作:

class Name(String):
    def validator(self, name):
        if isinstance(name, str):
            return name.upper()
        raise TypeError("name must be a string")

这有两个好处:首先,当实际上有一个验证器附加到数据字段对象时,仅触发一个事件。 它不会在没有定义验证功能的对象的set事件上浪费宝贵的CPU周期。 其次,它允许您定义自己的字段类型,并仅在其中添加一个验证器方法,因此,并非要存储为Integer等的所有内容都经过相同的检查,而只是从新字段类型派生的检查。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM