You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Pydantic Validators does not raise ValueError if conditions are not met #134
For your specific example, refactoring as the following would provide validation:
fromtypingimportOptionalfrompydanticimportValidationErrorfromsqlmodelimportSQLModel, Field, create_engine, SessionclassPlaylist(SQLModel):
id: Optional[int] =Field(default=None, primary_key=True)
title: str=Field(max_length=255)
description: Optional[str]
classDbPlaylist(Playlist, table=True):
__tablename__='playlists'# this will now raise a ValidationError:try:
p=Playlist(title="x"*300, description="OK")
assertFalseexceptValidationError:
passp=Playlist(title="x"*255, description="OK")
engine=create_engine(url='sqlite://', echo=True)
SQLModel.metadata.create_all(engine)
withSession(engine) assession:
# You will need to convert the base class to the mapped instance thoughmapped_p=DbPlaylist.from_orm(p)
session.add(mapped_p)
session.commit()
# and remember that the mapped playlist will have the auto-incremented ID set, not the original# playlist instanceassertp.idisNoneassertmapped_p.id==1
Reacted by Charles Stern, Toni Alatalo and Rerouter
I had had a similar issue where I was wanting to use regular pydantic validation on initialisation instead of only when being commited into the database. While I'm not sure of the other impacts it may cause, my solution is below.
main.py > SQLModelMetaclass > new > line 307
config_validate=get_config("validate")
ifconfig_validateisTrue:
# If it was passed by kwargs, ensure it's also set in confignew_cls.__config__.validate=config_validatefork, vinnew_cls.__fields__.items():
col=get_column_from_field(v)
setattr(new_cls, k, col)
classHero(SQLModel, table=True, validate=True): # Added validateid: int=Field(primary_key=True, nullable=False)
number: int@validator('number')defless_than_100(cls, v):
ifv>100:
raiseValueError('must be less than 100')
returnv
When validate is disabled, validation runs on commit as usual, with it enabled, validation runs on object initialisation. Works for pydanic @validate functions as well as others such as max_length
Reacted by lovetoburnswhen, Nikita Shamaev, Christian Garcia, eugene and Michael
Mine situation is more strange, the validators are not runing at all! Maybe it's related to this, but after I tried @AndrewRiggs-Atkins's patch they still not working.
I was testing some other types out when I got really confused because you can initialize an a model with required fields without providing any of them. So I dug in and I realized the validation wasn't happening at all. Definitely an unexpected behavior from my perspective. I am sure there was a reason behind the decision, but I don't know what that reason was. I want everything to validate all the time, and if I didn't want to I would use the construct method to avoid it.
Could work around it with an additional class, but a big part of why I was looking at SQLModel was the ability to reduce the number of classes. Its still better than the alternative probably, but the unexpected behavior may cause some issues in the future.
The maintainer proposed a good approach here #52 (comment)
The reason for not running validators is SQLAlchemy assigning stuff into relationships/etc after instantiating the class. If it's possible I'd find doing the initialization using Model.construct() first and then passing the final object to the validators after SQLAlchemy magic better.
Now the act of creating another model for data validation and then passing the values to the actual table-model can be done something like this:
classMySQLModel(SQLModel, table=False):
@classmethoddefcreate(cls, **kwargs):
classValidator(cls, table=False):
classConfig:
table=False# Check if all fields are setValidator(**kwargs)
returncls(**kwargs)
I ran into a similar issue recently when trying to display models as various FastUI forms. Here's my workaround:
fromtypingimportOptionalfromsqlmodelimportSQLModel, FieldfromdatetimeimportdatetimeasdtfrompydanticimportBaseModel, model_validator# Form DefinitionclassDbInfoForm(SQLModel):
name: str=Field(
title="Database Name",
description="The common name for your database",
)
short_name: str=Field(
title="Database Short Name",
description="The short name for the database.",
)
display_name: str=Field(
title="Database Display Name",
description="The display name of the database.",
)
category: Optional[str] =Field(
title="Database Category",
description="Category of Database. For example, your department or the type of records.",
)
alias: Optional[str] =Field(
title="Database Alias",
description="What other people or systems might refer to your database or category as.",
)
description: Optional[str] =Field(
title="Detailed Description",
description="Write a detailed description of this database and its purpose.",
)
# Model Definition That performs ValidationclassDbInfo(DbInfoForm):
id: Optional[int] =Field(default=None, primary_key=True)
created_at: dt=Field(default=dt.now())
updated_at: dt=Field(default_factory=dt.now)
status: Optional[str] =Field(default="building", max_length=100)
@model_validator(mode="before")@classmethoddefremove_ms_from_dt(cls, data: Any) ->Any:
ifisinstance(data, dict):
forfield_name, field_valueindata.items():
ifisinstance(field_value, dt):
data.update({field_name: field_value.replace(microsecond=0)})
else:
assertisinstance(data, dict)
returndata# Model with Table ConfigurationclassDbInfoModel(DbInfo, table=True):
...
Usage Example
The DbInfo class should only be required before DbInfoModel when creating or updating the database.
Creating Database:
db_info_form=DbInfoForm(
name="PyTestDB",
short_name="PTDB",
display_name="PyTest Database",
category="Testing",
alias="Some Alias for PytestDB",
description="This is a pytest database",
)
validated_form=DbInfo(**db_info_form.model_dump())
form_to_add=DbInfoModel(**validated_form.model_dump())
Activity
sorasful commentedon Oct 16, 2021
It seems it related to this part of the code :
main.py : line 491
mudassirkhan19 commentedon Oct 28, 2021
I'm facing a similar issue with
max_lengthparam inField. I filed an issue here: #148, then realised this is a similar issue so closed it.chriswhite199 commentedon Nov 24, 2021
I've seen this in another ticket, and while not explicitly documented, https://sqlmodel.tiangolo.com/tutorial/fastapi/multiple-models/ notes to use multiple models and inheritance.
For your specific example, refactoring as the following would provide validation:
rickerp commentedon Dec 10, 2021
Any news on this? For me this is kind of a security issue, as I am expecting to not create a new entry and it tries to create
AndrewRiggs-Atkins commentedon Jan 12, 2022
I had had a similar issue where I was wanting to use regular pydantic validation on initialisation instead of only when being commited into the database. While I'm not sure of the other impacts it may cause, my solution is below.
main.py > SQLModelMetaclass > new > line 307
main.py > SQLModel > init > line 517
usage
When
validateis disabled, validation runs on commit as usual, with it enabled, validation runs on object initialisation. Works for pydanic@validatefunctions as well as others such asmax_lengthsynodriver commentedon Jun 15, 2022
Mine situation is more strange, the validators are not runing at all! Maybe it's related to this, but after I tried @AndrewRiggs-Atkins's patch they still not working.
Here is my code and I'm using async mode.
eudu commentedon Jun 30, 2022
Duplicate of #52
eseglem commentedon Aug 17, 2022
I was testing some other types out when I got really confused because you can initialize an a model with required fields without providing any of them. So I dug in and I realized the validation wasn't happening at all. Definitely an unexpected behavior from my perspective. I am sure there was a reason behind the decision, but I don't know what that reason was. I want everything to validate all the time, and if I didn't want to I would use the
constructmethod to avoid it.Could work around it with an additional class, but a big part of why I was looking at SQLModel was the ability to reduce the number of classes. Its still better than the alternative probably, but the unexpected behavior may cause some issues in the future.
Jaakkonen commentedon Jun 12, 2023
The maintainer proposed a good approach here #52 (comment)
The reason for not running validators is SQLAlchemy assigning stuff into relationships/etc after instantiating the class. If it's possible I'd find doing the initialization using
Model.construct()first and then passing the final object to the validators after SQLAlchemy magic better.Now the act of creating another model for data validation and then passing the values to the actual table-model can be done something like this:
Tanner-Ray-Martin commentedon Aug 14, 2024
I ran into a similar issue recently when trying to display models as various FastUI forms. Here's my workaround:
Usage Example
The
DbInfoclass should only be required beforeDbInfoModelwhen creating or updating the database.Creating Database:
YuriiMotov commentedon Aug 27, 2025
This is a duplicate of #52
And there is more recent #453 on the same topic
Let's close this issue in favor of #453 as has higher support (more reactions)
table=Truenot validate data ? #453