sqlmodel
sqlmodel copied to clipboard
__init_subclass__ not working as expected with SQLModel
First Check
- [X] I added a very descriptive title to this issue.
- [x] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the SQLModel documentation, with the integrated search.
- [X] I already searched in Google "How to X in SQLModel" and didn't find any information.
- [X] I already read and followed all the tutorial in the docs and didn't find an answer.
- [X] I already checked if it is not related to SQLModel but to Pydantic.
- [X] I already checked if it is not related to SQLModel but to SQLAlchemy.
Commit to Help
- [X] I commit to help with one of those options 👆
Example Code
#WORKING EXAMPLE
from pydantic import BaseModel, root_validator, ValidationError
class Philosopher(BaseModel):
def __init_subclass__(cls, default_name=None, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_name = default_name
@root_validator(pre=True)
def default_name_validator(cls, values):
print(cls.default_name)
if values["name"] != cls.default_name:
raise ValidationError("name not correct")
return values
class AustralianPhilosopher(Philosopher, default_name="Bruce"):
name: str
class GermanPhilosopher(Philosopher, default_name="Nietzsche"):
name: str
Bruce = AustralianPhilosopher(name="Bruce")
Nietzsche = GermanPhilosopher(name="Nietzsche")
Error = GermanPhilosopher(name="test")
# DESIRED:
class Philosopher(SQLModel):
def __init_subclass__(cls, default_name=None, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_name = default_name
@root_validator(pre=True)
def default_name_validator(cls, values):
print(cls.default_name) # -> Now this is None
if values["name"] != cls.default_name:
raise ValidationError("name not correct")
return values
class AustralianPhilosopher(Philosopher, default_name="Bruce"):
name: str
class GermanPhilosopher(Philosopher, default_name="Nietzsche"):
name: str
Bruce = AustralianPhilosopher(name="Bruce")
Nietzsche = GermanPhilosopher(name="Nietzsche")
Error = GermanPhilosopher(name="test")
Description
Building forward from this Stackoverflow issue, I create a base class that has a __init_subclass__ method. In this baseclass, I want to have a root_validator for all classes that subclass this baseclass. This seems to work when I have pydantic's BaseModel as the Base for my Philosopher class, but my actual goal is to do this with SQLModel's Base. If I do so, the default_name attribute of cls is None.
.
Operating System
macOS
Operating System Details
No response
SQLModel Version
sqlmodel = "0.0.6"
Python Version
3.10.3
Additional Context
Workaround I could do a dirty fix by setting title = "Bruce" and title ="Nietzsche" instead of default_name. Because then I would be able to call it using cls.config.title but that does not seem like a responsible fix
class Philosopher(SQLModel):
def __init_subclass__(cls, default_name=None, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_name = default_name
@root_validator(pre=True)
def default_name_validator(cls, values):
print(cls.__config__.title)
if values["name"] != cls.__config__.title:
raise ValidationError("name not correct")
return values
class AustralianPhilosopher(Philosopher, title="Bruce"):
name: str
class GermanPhilosopher(Philosopher, title="Nietzsche"):
name: str
Bruce = AustralianPhilosopher(name="Bruce")
Nietzsche = GermanPhilosopher(name="Nietzsche")
Error = GermanPhilosopher(name="test")
Is there a good reason why not to do it this way?
from pydantic import root_validator, ValidationError
from sqlmodel import SQLModel
class Philosopher(SQLModel):
__default_name__ = None
@root_validator(pre=True)
def default_name_validator(cls, values):
print(cls.__default_name__)
if values["name"] != cls.__default_name__:
raise ValidationError("name not correct")
return values
class AustralianPhilosopher(Philosopher):
__default_name__ = "Bruce"
name: str
class GermanPhilosopher(Philosopher):
__default_name__ = "Nietzsche"
name: str
Bruce = AustralianPhilosopher(name="Bruce")
Nietzsche = GermanPhilosopher(name="Nietzsche")
Error = GermanPhilosopher(name="test")
Is there a good reason why not to do it this way?
from pydantic import root_validator, ValidationError from sqlmodel import SQLModel class Philosopher(SQLModel): __default_name__ = None @root_validator(pre=True) def default_name_validator(cls, values): print(cls.__default_name__) if values["name"] != cls.__default_name__: raise ValidationError("name not correct") return values class AustralianPhilosopher(Philosopher): __default_name__ = "Bruce" name: str class GermanPhilosopher(Philosopher): __default_name__ = "Nietzsche" name: str Bruce = AustralianPhilosopher(name="Bruce") Nietzsche = GermanPhilosopher(name="Nietzsche") Error = GermanPhilosopher(name="test")
Thank you, this is actually a better solution to the problem than my hack. Though, I still hope (for sake of clean code) it would be possible through __init_subclass_ method.
Currently, only Pydantic config options are allowed to pass by SQLModel's metaclass, e.g. extra=Extra.allow, so your default_name will never reach the __init_subclass__ method and its default value (None) will apply.