Home > Mobile >  Circular import error while trying to implement a new ForeignKey() in Django
Circular import error while trying to implement a new ForeignKey() in Django

Time:12-01

I am working on an API project that provides endpoints to front-end. There is a new field must added to related model that is "organization".

Firstly, this field must added to "File" model as a foreign key and other related files like populator, renderer and validator.

Secondly, 'organizationId" filter must added to /files/ endpoint.

Here are my codes:

files/models.py

# Django
from django.db.models import(
        CharField,
        ForeignKey,
        PROTECT,
    ) 

# Authic - Common
from authic.common.models import Model

# Authic - Organization
from authic.organizations.models import Organization


ALLOWED_EXTENSIONS = [
    'avif', 'gif', 'jpeg', 'jpg', 'mp4', 'png', 'svg', 'webm', 'webp'
]


class File(Model):

    cloudinary_public_id = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=64
    )

    extension = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=4
    )
    
    organization = ForeignKey(
        Organization,
        on_delete=PROTECT,
        related_name=' ',
        blank=True,
        null=True,
        default=None,
    )

    class Meta:
        db_table = 'files'

organizations/models.py

# Django
from django.db.models import (
    BooleanField,
    CharField,
    ForeignKey,
    PROTECT,
    TextField
)

# Authic - Common
from authic.common.models import Model

# Authic - Files
from authic.files.models import File


class Organization(Model):

    name = CharField(
        blank=False,
        null=False,
        default=None,
        max_length=256
    )
    url = CharField(
        blank=False,
        null=False,
        default=None,
        max_length=2048
    )
    theme_color = CharField(
        max_length=7,
        blank=False,
        null=False,
        default=None
    )
    logo = ForeignKey(
        File,
        on_delete=PROTECT,
        related_name=' ',
        blank=True,
        null=True,
        default=None
    )
    html_title = CharField(
        blank=False,
        null=False,
        default=None,
        max_length=256
    )
    meta_image = ForeignKey(
        File,
        on_delete=PROTECT,
        related_name=' ',
        blank=True,
        null=True,
        default=None
    )
    meta_description = TextField(
        blank=False,
        null=False,
        default=None
    )

    has_header_links = BooleanField(
        blank=False,
        null=False,
        default=None
    )
    has_footer_links = BooleanField(
        blank=False,
        null=False,
        default=None
    )
    has_footer_logo = BooleanField(
        blank=False,
        null=False,
        default=None
    )
    has_filters = BooleanField(
        blank=False,
        null=False,
        default=None
    )
    has_sorting = BooleanField(
        blank=False,
        null=False,
        default=None
    )
    has_search = BooleanField(
        blank=False,
        null=False,
        default=None
    )
    has_random_minting = BooleanField(
        blank=False,
        null=False,
        default=None
    )
    random_minting_preview_name = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=64
    )
    random_minting_preview_image_url = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=2048
    )
    token_id_strategy = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=64
    )
    twitter_handle = CharField(
        blank=False,
        null=False,
        default=None,
        max_length=64
    )
    custom_css = TextField(
        blank=True,
        null=True,
        default=None
    )
    has_custom_marketplace_header = BooleanField(
        blank=False,
        null=False,
        default=None
    )

    class Meta:
        db_table = 'organizations'

collections/models.py

# Django
from django.db.models import (
    BooleanField,
    CharField,
    ForeignKey,
    PROTECT,
    TextField
)

# Authic - Common
from authic.common.models import Model, QuerySet

# Authic - Files
from authic.files.models import File

# Authic - Users
from authic.users.models import User

# Authic - Organization
from authic.organizations.models import Organization


COLLECTION_CATEGORIES = ['art', 'photography', 'video']


class Collection(Model):

    address = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=42
    )

    hash = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=66
    )

    name = CharField(
        blank=False,
        null=False,
        default=None,
        max_length=64
    )

    symbol = CharField(
        blank=False,
        null=False,
        default=None,
        max_length=6
    )

    slug = CharField(
        blank=False,
        null=False,
        default=None,
        max_length=16
    )

    slug_lower = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=16
    )

    description = TextField(
        blank=True,
        null=True,
        default=None
    )

    url = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=2048
    )

    discord_username = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=64
    )

    instagram_username = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=64
    )

    telegram_username = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=64
    )
    # from authic.organizations.models import Organization
    # from B.models import X 
    # fk = models.ForeignKey(X)
    # fk = models.ForeignKey("B.X") 
    organization = ForeignKey(
        to='organizations.Organization',
        on_delete=PROTECT,
        related_name=' ',
        blank=True,
        null=True,
        default=None,
    )

    logo = ForeignKey(
        File,
        on_delete=PROTECT,
        related_name=' ',
        blank=True,
        null=True,
        default=None
    )

    banner = ForeignKey(
        File,
        on_delete=PROTECT,
        related_name=' ',
        blank=True,
        null=True,
        default=None
    )

    category = CharField(
        blank=False,
        null=False,
        default=None,
        max_length=64
    )

    is_public = BooleanField(
        blank=False,
        null=False,
        default=False
    )


    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.tags = QuerySet()

    @property
    def is_deletable(self):
        if self.address is not None:
            return False
        if self.nfts.count() > 0:
            return False
        return True

    class Meta:
        db_table = 'collections'
        unique_together = [
            ['creator', 'slug_lower']
        ]

Circular import error:

Traceback (most recent call last):
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\manage.py", line 12, in <module>
    main()
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\manage.py", line 8, in main
    execute_from_command_line(sys.argv)
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\core\management\__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\core\management\__init__.py", line 420, in execute
    django.setup()
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\apps\registry.py", line 116, in populate
    app_config.import_models()
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\apps\config.py", line 269, in import_models
    self.models_module = import_module(models_module_name)
  File "C:\Python39\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 855, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
    from authic.files.models import File
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\authic\files\models.py", line 12, in <module>
    from authic.organizations.models import Organization
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\authic\organizations\models.py", line 14, in <module>
    from authic.files.models import File
ImportError: cannot import name 'File' from partially initialized module 'authic.files.models' (most likely due to a circular import) (C:\Users\eba\Desktop\AUTHIC\projects\authic-api\authic\files\models.py)
(env) PS C:\Users\eba\Desktop\AUTHIC\projects\authic-api> py manage.py makemigrations
Traceback (most recent call last):
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\manage.py", line 12, in <module>
    main()
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\manage.py", line 8, in main
    execute_from_command_line(sys.argv)
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\core\management\__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\core\management\__init__.py", line 420, in execute
    django.setup()
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\apps\registry.py", line 116, in populate
    app_config.import_models()
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\env\lib\site-packages\django\apps\config.py", line 269, in import_models
    self.models_module = import_module(models_module_name)
  File "C:\Python39\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 855, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\authic\collections\models.py", line 14, in <module>
    from authic.files.models import File
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\authic\files\models.py", line 12, in <module>
    from authic.organizations.models import Organization
  File "C:\Users\eba\Desktop\AUTHIC\projects\authic-api\authic\organizations\models.py", line 14, in <module>
    from authic.files.models import File
ImportError: cannot import name 'File' from partially initialized module 'authic.files.models' (most likely due to a circular import) (C:\Users\eba\Desktop\AUTHIC\projects\authic-api\authic\files\models.py)

Please considering me as a junior software developer :D Thank you from now!

I have implemented organization field to much more files except "files". I get circular import error even I tried work around methods like to="organizations.Organization" etc.

CodePudding user response:

In cases like this one you can use a string in dot notation to reference the Foreign Key's model:

class MyModel(models.Model):
    foreign_key = models.ForeignKey(
        '<app name>.<model name>',
        on_delete=models.CASCADE,
    )

By instance, the logo field of your Organization model could be written this way:

    logo = ForeignKey(
        "files.File",
        on_delete=PROTECT,
        related_name=' ',
        blank=True,
        null=True,
        default=None
    )

Ie, the app name is files and the model name is File, so you have files.File.

Django docs include some nice examples here: https://docs.djangoproject.com/en/4.1/ref/models/fields/#foreignkey

CodePudding user response:

I realized using the work around method to='.' works but I forgot removing the import statement. After removing the unnecessary import statement it works! Here are the solution(files/models.py):

# Django
from django.db.models import(
        CharField,
        ForeignKey,
        PROTECT,
    ) 

# Authic - Common
from authic.common.models import Model

# Authic - Organization
#from authic.organizations.models import Organization --> unnecessary import


ALLOWED_EXTENSIONS = [
    'avif', 'gif', 'jpeg', 'jpg', 'mp4', 'png', 'svg', 'webm', 'webp'
]


class File(Model):

    cloudinary_public_id = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=64
    )

    extension = CharField(
        blank=True,
        null=True,
        default=None,
        max_length=4
    )
    
    organization = ForeignKey(
        to="organizations.Organization", # work around method(dot notation)
        on_delete=PROTECT,
        related_name=' ',
        blank=True,
        null=True,
        default=None,
    )

    class Meta:
        db_table = 'files'
  • Related