We all face sometimes
ImportError due to having a circular import that is occurred only for type hinting. There is a simple way to handle this kind of problem.
Let’s take we have two files like following:
#book_manager.py from django.db import models class BookManager(models.Manager): def create_new_version_of_book(self, old_book_object, version): return self.create(name=old_book_object.name, version=version)
The another file:
from django.db import models from library.models import BookManager class Book(models.Model): name = models.CharField(max_length=200) version = models.CharField(max_length=50) objects = BookManager()
These two files will work fine as we did not added any type hinting. Only the
BookManager is imported in
book_model.py file. But if we add type hint to the
create_new_version_of_book method from
BookManager, then it will be as follows:
from django.db import models from library.models import Book class BookManager(models.Manager): def create_new_version_of_book(self, old_book_object: Book, version: str) -> Book: return self.create(name=old_book_object.name, version=version)
Here now we will get an
ImportError due to circular import and we will get something like the below message when we want to run the project/files:
ImportError: cannot import name 'BookManager' from partially initialized module 'library.models' (most likely due to a circular import)
The solution is using
typing.TYPE_CHECKING constant as below:
from __future__ import annotations from typing import TYPE_CHECKING from django.db import models if TYPE_CHECKING: from library.models import Book class BookManager(models.Manager): def create_new_version_of_book(self, old_book_object: Book, version: str) -> Book: return self.create(name=old_book_object.name, version=version)
Line 1: We have imported annotations.
__future__.annotations is not default in Python now; but it will become the default in Python 3.11. For details, you have a look into PEP 563. If you don’t import it, you can use type hints as string. In our case, it will be
Line 3: We have imported
typing.TYPE_CHECKING special constant. The value of the constant is always False, but set to True by any type checkers, such as Mypy. We have used the this constant to make our import conditional.
Line 7–8: We have imported the model with a condition, so it will only be imported when the TYPE_CHECKING variable is True.
Hope, this will help. :)