-
-
Notifications
You must be signed in to change notification settings - Fork 712
Pydantic Validators does not raise ValueError if conditions are not met #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It seems it related to this part of the code : main.py : line 491 # Only raise errors if not a SQLModel model
if (
not getattr(__pydantic_self__.__config__, "table", False)
and validation_error
): |
I'm facing a similar issue with |
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: from typing import Optional
from pydantic import ValidationError
from sqlmodel import SQLModel, Field, create_engine, Session
class Playlist(SQLModel):
id: Optional[int] = Field(default=None, primary_key=True)
title: str = Field(max_length=255)
description: Optional[str]
class DbPlaylist(Playlist, table=True):
__tablename__ = 'playlists'
# this will now raise a ValidationError:
try:
p = Playlist(title="x" * 300, description="OK")
assert False
except ValidationError:
pass
p = Playlist(title="x" * 255, description="OK")
engine = create_engine(url='sqlite://', echo=True)
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
# You will need to convert the base class to the mapped instance though
mapped_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 instance
assert p.id is None
assert mapped_p.id == 1 |
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 |
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")
if config_validate is True:
# If it was passed by kwargs, ensure it's also set in config
new_cls.__config__.validate = config_validate
for k, v in new_cls.__fields__.items():
col = get_column_from_field(v)
setattr(new_cls, k, col) main.py > SQLModel > init > line 517 if (
(not getattr(__pydantic_self__.__config__, "table", False)
or getattr(__pydantic_self__.__config__, "validate", False)) # Added validate
and validation_error
):
raise validation_error usage class Hero(SQLModel, table=True, validate=True): # Added validate
id: int = Field(primary_key=True, nullable=False)
number: int
@validator('number')
def less_than_100(cls, v):
if v > 100:
raise ValueError('must be less than 100')
return v When |
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.
|
Duplicate of #52 |
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 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 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: class MySQLModel(SQLModel, table=False):
@classmethod
def create(cls, **kwargs):
class Validator(cls, table=False):
class Config:
table = False
# Check if all fields are set
Validator(**kwargs)
return cls(**kwargs) |
I ran into a similar issue recently when trying to display models as various FastUI forms. Here's my workaround: from typing import Optional
from sqlmodel import SQLModel, Field
from datetime import datetime as dt
from pydantic import BaseModel, model_validator
# Form Definition
class DbInfoForm(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 Validation
class DbInfo(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")
@classmethod
def remove_ms_from_dt(cls, data: Any) -> Any:
if isinstance(data, dict):
for field_name, field_value in data.items():
if isinstance(field_value, dt):
data.update({field_name: field_value.replace(microsecond=0)})
else:
assert isinstance(data, dict)
return data
# Model with Table Configuration
class DbInfoModel(DbInfo, table=True):
... Usage ExampleThe 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()) |
First Check
Commit to Help
Example Code
Description
When the condition is not met, the code just returns None for the field instead of raising an error.
The other fields are well populated.
Operating System
Windows
Operating System Details
No response
SQLModel Version
0.0.4
Python Version
3.9.2
Additional Context
No response
The text was updated successfully, but these errors were encountered: