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 old_book_object: ‘Book’
.
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. :)
Top comments (0)