sqlmodel icon indicating copy to clipboard operation
sqlmodel copied to clipboard

Unable to use original type hint by defining relationship

Open Whitroom opened this issue 4 years ago • 5 comments

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 typing import Optional

from sqlmodel import Field, SQLModel, Relationship, create_engine, Session

class Team(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(index=True)
    headquarters: str

    # key sentence
    heroes: list["Hero"] = Relationship(back_populates='team')

class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(index=True)
    secret_name: str
    age: Optional[int] = Field(default=None, index=True)

    team_id: Optional[int] = Field(default=None, foreign_key=Team.id)

database_url = f'mysql+mysqlconnector://{user}:{password}@{host}/{database_name}'

engine = create_engine(database_url, encoding='utf8', echo=True)

if __name__ == '__main__':
    SQLModel.metadata.create_all(engine)

Description

I create Hero and Team models and try to use python original type hint, but while running it, it occurs exceptions...

Traceback (most recent call last): File "pydantic\validators.py", line 709, in pydantic.validators.find_validators TypeError: issubclass() arg 1 must be a class

Traceback (most recent call last): File "C:\Users\10620\Desktop\Code\Python\sqlmodel\table_relationship.py", line 5, in
class Team(SQLModel, table=True): File "C:\Users\10620\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlmodel\main.py", line 342, in init temp_field = ModelField.infer( File "pydantic\fields.py", line 488, in pydantic.fields.ModelField.infer File "pydantic\fields.py", line 419, in pydantic.fields.ModelField.init File "pydantic\fields.py", line 534, in pydantic.fields.ModelField.prepare File "pydantic\fields.py", line 728, in pydantic.fields.ModelField._type_analysis File "pydantic\fields.py", line 778, in pydantic.fields.ModelField._create_sub_type File "pydantic\fields.py", line 419, in pydantic.fields.ModelField.init File "pydantic\fields.py", line 539, in pydantic.fields.ModelField.prepare File "pydantic\fields.py", line 801, in pydantic.fields.ModelField.populate_validators File "pydantic\validators.py", line 718, in find_validators RuntimeError: error checking inheritance of 'Hero' (type: str)

The solution is adding List from typing and replacing it, but I don't know how to solve this bug...

link: https://sqlmodel.tiangolo.com/tutorial/relationship-attributes/define-relationships-attributes/

Operating System

Windows

Operating System Details

No response

SQLModel Version

0.0.6

Python Version

3.10.1

Additional Context

No response

Whitroom avatar Jan 19 '22 02:01 Whitroom

I am not sure I understand your question correctly, but here list["Hero"] should indeed be List["Hero"]. From Python 3.9 onwards, generic list[Hero] should work as well, I think, but that requires your code to be rearranged, so that Hero is defined before Team.

byrman avatar Jan 31 '22 14:01 byrman

@Whitroom pls update to pydantic v1.9.1 - it should be fixed

aleksul avatar May 19 '22 12:05 aleksul

I meet a similar problem,

class Base(SQLModel):
    pass
class Test:
    basemodel = Base # type alias at here, classVar[type[Base]] also didn't work
    __slots__ = 'filename', 'datamodel'
    def __init__(self, filename: str, datamodel: Optional[Base] = None, **kwargs):
        self.filename = filename
        if datamodel is not None:
            assert isinstance(datamodel, self.basemodel) # self.basemodel is Any at here, should be type[Base]
            self.datamodel = datamodel
        else:
            self.datamodel = self.basemodel(**kwargs) # self.basemodel is Any at here, should be type[Base]

If you remove inheritance of Base, typehint work perfectly. My stupid solution is adding a classmethod to get instance of Base,

class Base(SQLModel, table=True):
    @classmethod
    def get_instance(cls, data: dict):
        return Base(**data)


class Test:
    basemodel = Base.get_instance
    __slots__ = 'filename', 'datamodel'
    def __init__(self, filename: str, datamodel: Optional[Base] = None, **kwargs):
        self.filename = filename
        if datamodel is not None:
            assert isinstance(datamodel, self.basemodel)
            self.datamodel = datamodel
        else:
            self.datamodel = self.basemodel(**kwargs)

Btw, filename is private attribute of Base at first, but idk how to init it.

Shuntw6096 avatar Mar 07 '24 08:03 Shuntw6096