我有下面的mlModel Base Class和表模型,但我无法在保持约束的同时使我的Decimal字段成为可选的. PS:我已经知道这个问题已经针对pydantic进行了讨论,但该解决方案似乎不适用于SQlModel.

  1. 首次try (错误:ValueError: Unknown constraint max_digits)
class ItemBase(SQLModel):
  # ...
  price: Optional[Decimal] = Field(
    default=None, max_digits=7, decimal_places=4, ge=0, le=100
  )
  # ...

class ItemTable(ItemBase, table=True):
  __tablename__ = "item"
  # ...
  1. Pydantic解决方案要求仅对December(https://github.com/pydantic/pydantic/discussions/7962#discussioncomment-7939114)应用约束在SQlModel中会产生错误:TypeError: issubclass() arg 1 must be a class
class ItemBase(SQLModel):
  # ...
  price: Optional[Annotated[Decimal, Field(
    default=None, max_digits=7, decimal_places=4, ge=0, le=100
  )]] = None
  # ...
  1. 我让代码发挥作用的解决方案是使用sa_entry来实现max_digitsdecimal_places约束,这没关系,因为这在数据库端确实很重要.
class ItemBase(SQLModel):
  price: Optional[Decimal] = Field(
    default=None, ge=0, le=100, sa_column=Column(DECIMAL(7,4)),
    regex=r"^\d+(\.\d+)?$"  # added for the string part to only accept numbers
  )

通过最后一次try ,lege约束在Python中正常工作:

ItemTable(..., price=Decimal(123.45))  # => throws ValidationError
ItemTable(..., price=Decimal(34.12))  # => works fine

但现在我的问题是用它生成的json模式放弃了我的所有约束,变成了only: anyOf: [{type: number}, {type: string}, {type: null}]

有问题,因为我然后使用json模式为我的测试创建假对象(使用jsf库),并且在没有约束的情况下,我的测试都会失败.

Edit with an answer:

感谢@Jeremy,我认为第2点中所述的错误仅发生在table=True个模型上.所以我最终做了以下操作:

class ItemBase(SQLModel):
  # ...
  price: Optional[Annotated[Decimal, Field(
    multiple_of=0.0001, ge=0, le=100
  )]] = None
  # ...

class ItemTable(ItemBase, table=True):
  price: Optional[Decimal] = Field(
    default=None, ge=0, le=100, sa_column=Column(DECIMAL(7,4)
  )

为了将模式约束添加到小数json模型的字符串类型中,我必须将pydantic的GenerateJsonSchema子类化:

class MySchemaGenerator(GenerateJsonSchema):
  def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue:
    json_schema = self.str_schema(core_schema.str_schema(pattern=r"^[-+]?\d+((?:\.\d+)|(?:[eE][-+]?\d+))?$"))
    ... # the rest of the method is unchanged

ItemBase.model_json_schema(schema_generator=MySchemaGenerator)
"""returns for 'price' property:
{'price': {'anyOf': [{'maximum': 100.0,
     'minimum': 0.0,
     'multipleOf': 0.0001,
     'type': 'number'},
    {'pattern': '^[-+]?\\d+((?:\\.\\d+)|(?:[eE][-+]?\\d+))?$',
     'type': 'string'},
    {'type': 'null'}],
   'default': None,
   'title': 'Price'}
"""

但这样我就不能直接在字段上指定模式,因此所有小数都将具有相同的模式.如果有人知道如何将regex从字段定义传递到decimal_schema方法,我很乐意接受它!

推荐答案

试试这个

  • 0相当于default = 0
  • multiple_of为您提供了作为整除的精度要求
  • le为您提供最大的整值
class ItemBase(SQLModel)
  price: Optional[Annotated[Decimal, Field(0, multiple_of=0.0001, le=1000000)]] | None

它应该生成类似这样的模式

{
    "type": [
        "object",
        "null"
    ],
    "properties": {
        "price": {
            "type": "number",
            "maximum": 1000000,
            "multipleOf": 0.0001
        }
    }
}

这将验证最多7位数的数字,如果提供了小数值,则应验证最多四(4)个精度点. 456123.1235

如果仍然失败,则有相当多的SO答案表明typing-extensions包可能需要升级.

pip install --force-reinstall typing-extensions==4.5.0

我还没有深入研究,但我想知道pydantic是否在生成JSON模式时遇到了一些困难,因为定义的类型和约束之间的关键字冲突,因为max_digits相当于maxLength,这是type: string个值的约束.

Python相关问答推荐

如何计算列表列行之间的公共元素

将HLS纳入媒体包

即使在可见的情况下也不相互作用

可变参数数量的重载类型(args或kwargs)

通过Selenium从页面获取所有H2元素

Pandas - groupby字符串字段并按时间范围 Select

如何将Docker内部运行的mariadb与主机上Docker外部运行的Python脚本连接起来

Polars:用氨纶的其他部分替换氨纶的部分

使用Python查找、替换和调整PDF中的图像'

在单次扫描中创建列表

如何防止Pandas将索引标为周期?

ConversationalRetrivalChain引发键错误

在Docker容器(Alpine)上运行的Python应用程序中读取. accdb数据库

如何删除重复的文字翻拍?

将链中的矩阵乘法应用于多组值

如何求相邻对序列中元素 Select 的最小代价

Tensorflow tokenizer问题.num_words到底做了什么?

如何从比较函数生成ngroup?

按条件计算将记录拆分成两条记录

按最大属性值Django对对象进行排序