【问题标题】:Marshmallow validation incorrectly fails when using an SQLAlchemy TypeDecorator使用 SQLAlchemy TypeDecorator 时,棉花糖验证错误地失败
【发布时间】:2020-06-04 18:50:03
【问题描述】:

我创建了一个基于 SQLAlchemy example 的 TypeDecorator,它在 32 个字符的十六进制字符串和存储 UUID 的 BINARY 数据库列之间进行转换:

from __future__ import absolute_import
import uuid
from sqlalchemy import types, func

#https://docs.sqlalchemy.org/en/13/core/custom_types.html#backend-agnostic-guid-type
class HashColumn(types.TypeDecorator):
    impl=types.BINARY

    def process_bind_param(self, value, dialect):
        if value is not None:
            return uuid.UUID(hex=value).bytes

    def process_result_value(self, value, dialect):
        return uuid.UUID(bytes=value).hex


    def copy(self, **kw):
        return HashColumn(self.impl.length)

型号:

def get_uuid():
    return uuid.uuid4().hex

class School(db.Model):
    """
        description: A School
    """
    __tablename__ = "schools"
    id = db.Column('school_id', HashColumn(length=16), primary_key=True, default=get_uuid)
    ...

但是,我遇到的问题是我从 SQLAlchemy 模型生成的棉花糖模式没有将此列视为 32 个字符的字符串:

架构:

from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
...
class SchoolSchema(SQLAlchemyAutoSchema):
    class Meta:
        model = School
        include_relationships = True
        load_instance = True
        include_fk = True
...

在我的代码中:

try:
    new_object = SchoolSchema().load(data, session=db.session)
except ValidationError as err:
    print(err.messages)
    print(err.valid_data)

在使用完全有效的 UUID a5fad20c691546ae8871390d980aae6d 运行此代码时,marshmallow 会引发验证错误并给出以下输出:

{"id": ["Longer than maximum length 16."]}

由于我希望在使用期间将 UUID 格式化为 32 个字符的十六进制字符串(或 Python UUID,如果适用),并在存储到数据库之前转换为 BINARY(16),我需要摆脱这个验证错误,但我不确定如何执行此操作,因为更改 SQLAlchemy 模型上的长度参数将意味着数据库表将创建为 BINARY(32) 而不是 BINARY(16),长度加倍。

是否可以设置一个 SQLAlchemy TypeDecorator 以便它在数据库中存储一个长度 (BINARY(16)) 的类型,但向 Python 和/或 SQLAlchemy 呈现不同的长度 (CHAR(32))以便棉花糖可以正确验证长度为 32 个字符的字符串?

我已经在 StackOverflow 上查看过类似这样的其他问题:

但这似乎是关于转换类型本身,我已经在我的示例代码中完成了。我似乎找不到任何提及如何转换类型的 length 的内容。

【问题讨论】:

    标签: python validation sqlalchemy uuid marshmallow


    【解决方案1】:

    到目前为止,我已经找到了两种方法来解决这个问题:

    1. 调整 TypeDecorator 的类型,使其像 CHAR 列而不是二进制列,并使用 load_dialect_impl 更改呈现给数据库的类型,指定不同的长度作为参数

      class HashColumn(types.TypeDecorator):
          impl=types.CHAR
      
          def load_dialect_impl(self, dialect):
              return dialect.type_descriptor(types.BINARY(16))
      
         ...
      

      (剩下的课基本上和问题里的一样)

      此更改允许我将我的 DB 模型中的 HashColumn(length=16) 定义更改为 HashColumn(length=32),从而允许 marshmallow 正确解释长度。

    2. 或者,我可以更改我的 API PATCH/更新端点的实现以从数据库中获取和更新现有对象,而不是创建一个全新的对象并尝试合并它们的值。这完全消除了 Marshmallow 验证,因为不再使用 ID 来创建新对象,但是,对我来说,这感觉像是一种解决方法太多了,这意味着,因为没有使用 marshmallow 验证,它也不会验证任何其他数据字段。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      • 1970-01-01
      • 1970-01-01
      • 2017-05-12
      • 2017-02-02
      • 1970-01-01
      相关资源
      最近更新 更多