user14358266
user14358266

Reputation:

CircularDependencyError on Foreign Keys Pointing to Each Other Django

I get a circular dependency error that makes me have to comment out a field. Here is how my models are set up:

class Intake(models.Model):
    # This field causes a bug on makemigrations. It must be commented when first
    # running makemigrations and migrate. Then, uncommented and run makemigrations and
    # migrate again.
    start_widget = models.ForeignKey(
        "widgets.Widget",
        on_delete=models.CASCADE,
        related_name="start_widget",
        null=True,
        blank=True,
    )

class Widget(PolymorphicModel):
    intake = models.ForeignKey(Intake, on_delete=models.CASCADE)

By the way, Widget's PolymorphicModel superclass is from here. Why is this happening and how can I solve it without having to comment out over and over again? Thanks!

EDIT: THE FULL ERR:

Traceback (most recent call last):
  File "/Users/nicksmith/Desktop/proj/backend/manage.py", line 22, in <module>
    main()
  File "/Users/nicksmith/Desktop/proj/backend/manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/core/management/base.py", line 371, in execute
    output = self.handle(*args, **options)
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/core/management/base.py", line 85, in wrapped
    res = handle_func(*args, **kwargs)
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 92, in handle
    executor = MigrationExecutor(connection, self.migration_progress_callback)
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/db/migrations/executor.py", line 18, in __init__
    self.loader = MigrationLoader(self.connection)
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/db/migrations/loader.py", line 53, in __init__
    self.build_graph()
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/db/migrations/loader.py", line 282, in build_graph
    self.graph.ensure_not_cyclic()
  File "/Users/nicksmith/Desktop/proj/backend/venv/lib/python3.9/site-packages/django/db/migrations/graph.py", line 274, in ensure_not_cyclic
    raise CircularDependencyError(", ".join("%s.%s" % n for n in cycle))
django.db.migrations.exceptions.CircularDependencyError: intakes.0001_initial, widgets.0001_initial

Upvotes: 2

Views: 1376

Answers (4)

Prajakta Kane
Prajakta Kane

Reputation: 11

This worked for me:

I ran

python manage.py makemigrations customers stores catalogue

and then

python manage.py migrate 

that solved the problem of circular dependency between these three models

Upvotes: 1

V-R
V-R

Reputation: 1401

I'd just had a very similar CircularDependencyError with Django 4.2.6. My goal was to just get all our Django tests working in CI (after upgrading from an older Django) while rebuilding the DB every time like this: rm -f data.sqlite.db && manage.py migrate && manage.py test.

Neither passing a string as first argument to each ForeignKey instead of a class, nor replacing (one, or the other, or both) the ForeignKey's with dummy CharField - none of that was resolving the error and I got pretty frustrated.

What ended up working was running manage.py makemigrations app1 app2 (with both apps simultaneously, instead of one by one). As a result, Django generated not 1 but 2 migrations for one of the apps, and allowed me to run manage.py migrate without CircularDependencyError.

To be precise, I kept passing string as first argument to ForeignKey, which might be necessary, but was definitely not sufficient in my case to resolve the error.

I think makemigrations without parameters is supposed to go through the INSTALLED_APPS and detect all missing migrations automatically, but it kept saying No changes detected so I had to specify the apps myself, and made the mistake of doing it one by one.

Upvotes: 0

Hussain Ebrahim
Hussain Ebrahim

Reputation: 21

     line 274, in ensure_not_cyclic
    raise CircularDependencyError(", ".join("%s.%s" % n for n in cycle))
django.db.migrations.exceptions.CircularDependencyError: POS.0001_initial, CRM.0001_initial, CRM.0002_remove_divisions_cc

This type of errors that i approach for every fresh setup, the solution is quite simple:

  1. Delete all migrations.

  2. find all your "models.ForeignKey" in the project, replace them with:

    models.CharField(('Disabled for Dependency issue'), max_length=20, blank=True) # models.ForeignKey

*Exclude environment directory.

  1. run makemigrations

    python manage.py makemigrations

  2. get back your foreignkeys, replace all the following with blank

    models.CharField(('Disabled for Dependency issue'), max_length=20, blank=True) #

  3. run makemigrations again

    python manage.py makemigrations python manage.py migrate

Upvotes: 2

ha-neul
ha-neul

Reputation: 3248

In Django doc about foreignkey:

If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model object itself:

Here is an example from the doc:

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

So, in your case, this might work:

# put both models in the same models.py

class Intake(models.Model):

    start_widget = models.ForeignKey(
        "Widget",                      # use "Widget" the name of the model
        on_delete=models.CASCADE,
        related_name="start_widget",
        null=True,
        blank=True,
    )

class Widget(PolymorphicModel):
    intake = models.ForeignKey(Intake, on_delete=models.CASCADE)

Or

class Widget(PolymorphicModel):
    intake = models.ForeignKey("Intake", on_delete=models.CASCADE) #use "Intake" rather than Intake.

class Intake(models.Model):

    start_widget = models.ForeignKey(
        Widget,
        on_delete=models.CASCADE,
        related_name="start_widget",
        null=True,
        blank=True,
    )

If you have to have two models in different models.py, then use name of models in both cases.

# they are in separate models.py

##### in this case, do not import models from the other app.

class Widget(PolymorphicModel):
    intake = models.ForeignKey("myapp.Intake", on_delete=models.CASCADE) 
#use "myapp.Intake" rather than Intake.


# in different models.py

# use "widgets.Widget" rather than Widget

class Intake(models.Model):

    start_widget = models.ForeignKey(
        "widgets.Widget",           
        on_delete=models.CASCADE,
        related_name="start_widget",
        null=True,
        blank=True,
    )

Or import model from one app, but use "myapp.MyModel" in the other app.

# in this models.py,  do not import Widget model


class Intake(models.Model):
    start_widget = models.ForeignKey(
        "widgets.Widget",
        on_delete=models.CASCADE,
        related_name="start_widget",
        null=True,
        blank=True,
    )


#### in another models. py

from myapp.models import Intake             # import model

class Widget(PolymorphicModel):
    intake = models.ForeignKey(Intake, on_delete=models.CASCADE)

Upvotes: 1

Related Questions