Skip to content

Conversation

@monchin
Copy link

@monchin monchin commented Aug 1, 2024

I found that when table=True is set, pydantic becomes invalid for usage like hero = Hero(name="Deadpond", secret_name="Dive Wilson", age="test"). As the document says you get all of Pydantic's features, including automatic data validation, serialization, and documentation. You can use SQLModel in the same way you can use Pydantic., I think table=True makes pydantic invalid is a bug, so I tried to fix it.

I delete a test case because this case is incompatible with pydantic. I also add a test case to ensure my code works. For current code, my new test case would fail, but it would pass for my new code.

raw_self = self.model_copy()
pydantic_validated_model = self.__pydantic_validator__.validate_python(
data,
self_instance=raw_self,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was raw_self necessary here?

Copy link
Author

@monchin monchin Nov 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was raw_self necessary here?

self.__pydantic_validator__.validate_python(
                data,
                self_instance=raw_self,
            )

would update self, and something would go wrong on sqlmodel_table_construct for the changed self. Maybe that's the reason that the original code dosen't validate when table=True.

Actually, we need to get the pydantic-validated dict, and use this dict to update the original dict, and then construct tables by the updated-original dict.

Anyway, you can try self_instance=self, and some unittests would fail.

Copy link

@spandan-sharma spandan-sharma Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should also do:

data = raw_self.model_dump()
self.__dict__ |= raw_self.__dict__

after validate_python is called on raw_self.

Otherwise you'll likely have broken model-validator behaviour, double default-factory calls etc. I explain all this here including why you need model_copy (or maybe even __dict__.copy() would do) - see Full Solution section at the bottom to skip the problem description etc.

@iloveitaly
Copy link

Some related issues for this one:

However, I think this comment indicates that this is actually the desired behavior.

@monchin
Copy link
Author

monchin commented Nov 27, 2024

this is actually the desired behavior

I regard this action as a bug rather than a feature because sqlmodel announce that you get all of Pydantic's features, including automatic data validation, serialization, and documentation. You can use SQLModel in the same way you can use Pydantic. Models with no validation when table=True is definitely not the way we are using pydantic.

@muhammadyaseen
Copy link

Hi all, is there a plan to merge this? Currently facing the same issues discussed in #11 and #453 (and several other related issues) and none of the proposed workaround seem clean enough for my use case.

Thanks.

@monchin
Copy link
Author

monchin commented Dec 17, 2024

@tiangolo Would you please look at this PR snd see whether this solution is OK? It passes all tests.

@ohadc-orchid
Copy link

Hey, bumping this because we urgently need it as well :)

Thanks!

@aedan-chiari
Copy link

aedan-chiari commented Jan 30, 2025

@ohadc-orchid

Fortunately, there seems to be an easy solution.

class Config:
        validate_assignment = True
from datetime import datetime

from pydantic import BaseModel
from sqlmodel import SQLModel, Field


class DatetimeModel(BaseModel):
    timestamp: datetime

class DatetimeSQLModel(SQLModel):
    id: str = Field("dummy", primary_key=True)
    timestamp: datetime

class DatetimeSQLTableModel(SQLModel, table=True):
    id: str = Field("dummy", primary_key=True)
    timestamp: datetime
    class Config:
        validate_assignment = True

t = '2024-11-13T12:30:12.584245Z'
m1 = DatetimeModel(timestamp=t)
m2 = DatetimeSQLModel(timestamp=t)
m3 = DatetimeSQLTableModel(timestamp=t)

print(type(m1.timestamp))
print(type(m2.timestamp))
print(type(m3.timestamp))

if __name__ == '__main__':
    pass

@svlandeg svlandeg added the bug Something isn't working label Feb 24, 2025
@svlandeg svlandeg changed the title ✏️ Fix pydantic invalid when table=True(#1036) 🐛 Ensure that type checks are executed when setting table=True Feb 24, 2025
@paulomtts
Copy link

Bumping this. I get @tiangolo 's intention, but it seems that things are naturally leaning in another direction. If nothing is wrong with this implementation, then I think it's a huge step forward.

@furkansenol
Copy link

@ohadc-orchid

Fortunately, there seems to be an easy solution.

class Config:
        validate_assignment = True
from datetime import datetime

from pydantic import BaseModel
from sqlmodel import SQLModel, Field


class DatetimeModel(BaseModel):
    timestamp: datetime

class DatetimeSQLModel(SQLModel):
    id: str = Field("dummy", primary_key=True)
    timestamp: datetime

class DatetimeSQLTableModel(SQLModel, table=True):
    id: str = Field("dummy", primary_key=True)
    timestamp: datetime
    class Config:
        validate_assignment = True

t = '2024-11-13T12:30:12.584245Z'
m1 = DatetimeModel(timestamp=t)
m2 = DatetimeSQLModel(timestamp=t)
m3 = DatetimeSQLTableModel(timestamp=t)

print(type(m1.timestamp))
print(type(m2.timestamp))
print(type(m3.timestamp))

if __name__ == '__main__':
    pass

This works for type validations(as PR aims to fix) but it is not very helpful if you want to use field validators, please see the comment: #52 (comment)

Just wanted to point it out if someone ends up here looking for how to work with field or model validators like me.

@vimota
Copy link

vimota commented Jun 1, 2025

this is actually the desired behavior

I regard this action as a bug rather than a feature because sqlmodel announce that you get all of Pydantic's features, including automatic data validation, serialization, and documentation. You can use SQLModel in the same way you can use Pydantic. Models with no validation when table=True is definitely not the way we are using pydantic.

Agree with this interpretation, but at the same time the code seems to explicitly try to avoid that behaviour (https://github.com/fastapi/sqlmodel/blob/main/sqlmodel/_compat.py#L564). Would love some clarity from the FastAPI folks on the intention here. This was definitely the most surprising behaviour I noticed using SQLModel.

@svlandeg svlandeg self-assigned this Sep 17, 2025
@svlandeg svlandeg changed the title 🐛 Ensure that type checks are executed when setting table=True ✨ Ensure that type checks are executed when setting table=True Oct 7, 2025
@svlandeg svlandeg added feature New feature or request and removed bug Something isn't working labels Oct 7, 2025
@svlandeg
Copy link
Member

svlandeg commented Oct 7, 2025

Hi all!

The current behaviour is clearly intentional, as stated in the docs and the code, and as further explained by Tiangolo here.

As such, I think it's unlikely that Tiangolo will want to change this behaviour. Nevertheless, I'll request his feedback internally.

Personally I think that if we choose to keep the current behaviour, we should consider adding more detailed documentation (and examples) about this functionality, as it's clearly unintuitive for many users.

@svlandeg svlandeg removed their assignment Oct 7, 2025
@svlandeg svlandeg linked an issue Oct 7, 2025 that may be closed by this pull request
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Why does a SQLModel class with table=True not validate data ?