Relationship type annotations disappear after class definition is evaluated
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
from sqlmodel import Relationship, SQLModel, Field
from pydantic.main import BaseModel
class SpecialFieldValidator(BaseModel):
id: int = Field(primary_key=True, index=True)
def __init_subclass__(cls) -> None:
# Try to figure out what type the special_field relation has
print("special_field" in cls.__annotations__) # False
print("special_field" in cls.__fields__) # False
print("special_field" in cls.__sqlmodel_relationships__) # True, but no type info via this dict, just a plain RelationshipInfo() instance:
print(dict(cls.__sqlmodel_relationships__["special_field"].__dict__)) # {'back_populates': None, 'link_model': None, 'sa_relationship': None, 'sa_relationship_args': None, 'sa_relationship_kwargs': None}
return super().__init_subclass__()
class MyModelA(SQLModel, SpecialFieldValidator, table=True):
id: int = Field(primary_key=True)
special_field_id: int = Field(foreign_key="mymodelb.id")
special_field: "MyModelB" = Relationship()
class MyModelB(SQLModel, SpecialFieldValidator, table=True):
id: int = Field(primary_key=True)
special_field_id: int = Field(foreign_key="mymodela.id")
special_field: MyModelA = Relationship()
Description
I'm trying to write a class (SpecialFieldValidator) that is supposed to check whether a specific field, which is always a Relationship, was annotated with a particular type.
However, the actual type annotation seems to be getting erased - looking in fields, annotations yields nothing, and the data in sqlmodel_relationships does not feature a type.
Operating System
Linux
Operating System Details
No response
SQLModel Version
0.0.8
Python Version
3.11.0
Additional Context
SQLAlchemy version is 1.4.41 I found this issue: https://github.com/tiangolo/sqlmodel/issues/255 that seems related, saying SQLAlchemy 1.4.36 breaks relationship, but SQLModels dependencies were officially updated to >=1.4.41 more recently than that so I figured this is a new issue.
I'm also facing this issue. From what I could see, one potential solution could be the following (if support for relationship information as part of the pydantic model is not included as a feature in SQLModel fure releases, so consider this a workaround):
- Override the
__new__dunder method ( as__init__is not used by SQLModel). - Populate the pydantic _model_fields property with missing info from relationships.
- Rebuild pydantic model schema to reflect such changes.
In your particular use-case it would be something like the following (behaviour could potentially be abstracted at SQLModel level, or at least at a base class level, so it is not performed in a per-class/per-attribute level each time):
from sqlmodel import Relationship, SQLModel, Field
from pydantic.main import BaseModel
class SpecialFieldValidator(BaseModel):
id: int = Field(primary_key=True, index=True)
def __init_subclass__(cls) -> None:
# Try to figure out what type the special_field relation has
print("special_field" in cls.__annotations__) # False
print("special_field" in cls.__fields__) # False
print("special_field" in cls.__sqlmodel_relationships__) # True, but no type info via this dict, just a plain RelationshipInfo() instance:
print(dict(cls.__sqlmodel_relationships__["special_field"].__dict__)) # {'back_populates': None, 'link_model': None, 'sa_relationship': None, 'sa_relationship_args': None, 'sa_relationship_kwargs': None}
return super().__init_subclass__()
class MyModelA(SQLModel, SpecialFieldValidator, table=True):
id: int = Field(primary_key=True)
special_field_id: int = Field(foreign_key="mymodelb.id")
special_field: "MyModelB" = Relationship()
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
new_object = super().__new__(cls, *args, **kwargs)
new_object.model_fields.update({"special_field": Field(..., description="description of your model property coming from the relationship")})
new_object.model_rebuild(force=True)
return new_object
class MyModelB(SQLModel, SpecialFieldValidator, table=True):
id: int = Field(primary_key=True)
special_field_id: int = Field(foreign_key="mymodela.id")
special_field: MyModelA = Relationship()
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
new_object = super().__new__(cls, *args, **kwargs)
new_object.model_fields.update({"special_field": Field(..., description="description of your model property coming from the relationship")})
new_object.model_rebuild(force=True)
return new_object
Kind regards.