Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python, model <> model, how to avoid circular import

Let's say that I have an pydantic (it could be any kind of) model that shold know how to convert itself to orm (it could be any kind of) model. To do so I have constructed mod_a.py:

from pydantic import BaseModel

from mod2mod.mod_b import DBUser

class User(BaseModel):

    name: str
    surname: str

    def to_orm(self) -> DBUser:
        return DBUser(
            full_name = f"{self.surname},{self.name}"
        )

I would also like my orm model to be able to convert itself to pydantic model, so my mod_b.py is:

from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String
from mod_a import User

Base = declarative_base()

class DBUser(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    full_name = Column(String)

    def to_pydantic(self) -> User:
        surname, name = self.full_name.split(',')
        User(
            name=name,
            surname=surname
        )

Idea is to be able to go from model to model at will like in mod2mod.py:

import pydantic
from mod_a import User
from mod_b import DBUser


user = User(name='John', surname='Doe')
orm_user = user.to_orm()

pydantic_user = orm_user.to_pydantic()

print(user)
print(pydantic_user)

Obviously this will not work because:

ImportError: cannot import name 'User' from partially initialized module 'mod_a' (most likely due to a circular import) (/mod2mod/mod_a.py)

How to solve this type of problem? Do I need another entity that will now how to do conversions between models and put current model methods there or there is some other design pattern?

like image 901
user3225309 Avatar asked Feb 23 '26 05:02

user3225309


2 Answers

You can use import ... style instead of from ... import .... And wrap return type hints in quotes (I did such below) or use postponed annotation evaluation PEP-563 (by the way, it is default for Python 3.10)

# mod_a.py
from pydantic import BaseModel
import mod_b


class User(BaseModel):
    name: str
    surname: str

    def to_orm(self) -> 'mod_b.DBUser':
        return mod_b.DBUser()
# mod_b.py
import mod_a

class DBUser:
    def to_pydantic(self) -> 'mod_a.User':
        return mod_a.User(
            name="",
            surname=""
        )
like image 111
alex_noname Avatar answered Feb 24 '26 19:02

alex_noname


You have to to do a local import and us TYPE_CHECKING to be able to do a type hint.

from typing import TYPE_CHECKING
from pydantic import BaseModel

if TYPE_CHECKING:
    from mod2mod.mod_b import DBUser
    

class User(BaseModel):

    name: str
    surname: str

    def to_orm(self) -> "DBUser":
        from mod2mod.mod_b import DBUser

        return DBUser(
            full_name = f"{self.surname},{self.name}"
        )

Do this for DBUser as well.

like image 38
finswimmer Avatar answered Feb 24 '26 17:02

finswimmer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!