У меня есть следующий базовый класс sqlmodel и модели таблиц, но мне не удается сделать поле Decimal
необязательным, сохранив при этом ограничения.
PS: Я уже знаю, что это уже обсуждалось для pydantic, но решение, похоже, не работает с sqlmodel.
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"
# ...
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
# ...
max_digits
и decimal_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
)
После этой последней попытки ограничения le
и ge
работают в Python правильно:
ItemTable(..., price=Decimal(123.45)) # => throws ValidationError
ItemTable(..., price=Decimal(34.12)) # => works fine
Но теперь моя проблема в том, что сгенерированная с ее помощью схема json снимает все мои ограничения и становится только:
anyOf: [{type: number}, {type: string}, {type: null}]
Проблематично, потому что тогда я использую схему json для создания поддельных объектов (с библиотекой jsf) для своих тестов, и без ограничений все мои тесты терпят неудачу...
Изменить с ответом:
Благодаря @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'}
"""
Но таким образом я не могу указать шаблон непосредственно в поле, поэтому все десятичные дроби будут иметь один и тот же шаблон. Если у кого-то есть идея, как передать регулярное выражение из определения поля в этот decimal_schema
метод, я бы с радостью им воспользовался!
Версии следующие: sqlmodel==0.0.16 и pydantic==2.6.4.
Попробуй это
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
.
Спасибо, это помогло мне понять немного больше. Кажется, моя проблема issubclass() arg 1 must be a class
связана с sql alchemy, когда я устанавливаю table=True
. typing-extensions
последняя версия кажется 4.11.0
однако. И проблема для меня действительно заключалась в том, что ограничения, какими бы они ни были, были просто удалены. Если вы можете дополнительно рассказать мне, как добавить регулярное выражение в виде строкового шаблона? (:
Я понял, как это сделать, переопределение метода GenerateJsonSchema.decimal_schema
помогло. Я дам полный ответ на свой вопрос. Спасибо за вашу помощь в указании мне в правильном направлении.
Какую версию sqlmodel вы используете?