Opened 10 years ago

Closed 10 years ago

Last modified 9 years ago

#22932 closed Bug (fixed)

"makemigrations" generates circular dependencies

Reported by: Manuel Kaufmann Owned by: nobody
Component: Migrations Version: dev
Severity: Normal Keywords: circular dependency, migrations, makemigrations
Cc: Manuel Kaufmann, berto Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I'm having an issue with the new migrations system included in Django. The steps that I'm following to reproduce is, once the example project is downloaded just run:

  1. manage.py makemigrations
  2. manage.py migrate
Traceback (most recent call last):
  File "/home/humitos/Source/migration_test/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 330, in execute_from_command_line
    utility.execute()
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 322, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/core/management/base.py", line 363, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/core/management/base.py", line 412, in execute
    output = self.handle(*args, **options)
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/core/management/commands/makemigrations.py", line 78, in handle
    loader.project_state(),
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/db/migrations/loader.py", line 268, in project_state
    return self.graph.make_state(nodes=nodes, at_end=at_end, real_apps=list(self.unmigrated_apps))
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/db/migrations/graph.py", line 140, in make_state
    for migration in self.forwards_plan(node):
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/db/migrations/graph.py", line 55, in forwards_plan
    return self.dfs(node, lambda x: self.dependencies.get(x, set()))
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/db/migrations/graph.py", line 121, in dfs
    return _dfs(start, get_children, [])
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/db/migrations/graph.py", line 113, in _dfs
    results = _dfs(n, get_children, path) + results
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/db/migrations/graph.py", line 113, in _dfs
    results = _dfs(n, get_children, path) + results
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/db/migrations/graph.py", line 113, in _dfs
    results = _dfs(n, get_children, path) + results
  File "/home/humitos/.virtualenvs/tmp-e997f5cc453b28eb/local/lib/python2.7/site-packages/django/db/migrations/graph.py", line 105, in _dfs
    raise CircularDependencyError(path[path.index(start):] + [start])
django.db.migrations.graph.CircularDependencyError: [('challenge', u'0001_initial'), (u'team', u'0001_initial'), ('challenge', u'0001_initial')]

If I remove the myapp.team.TeamCaptain model and the migration files already created and I run those commands again, it creates the proper migration files and it works.

I've been taking a look at #22325 but it doesn't seem to be the same situation.

I tried this in Django 1.7b4, 1.7c1 and master and I always got the same error.

What other kind of information can I provide?

Attachments (3)

migration_test.tar.bz2 (2.2 KB ) - added by Manuel Kaufmann 10 years ago.
Project example that makes Django to create Circular Migration Dependencies
team.0001_initial.py.diff (969 bytes ) - added by Manuel Kaufmann 10 years ago.
Migration edited by hand
team.0002_auto_20140704_1453.py (769 bytes ) - added by Manuel Kaufmann 10 years ago.
New migration added with --empty and then edited

Download all attachments as: .zip

Change History (9)

by Manuel Kaufmann, 10 years ago

Attachment: migration_test.tar.bz2 added

Project example that makes Django to create Circular Migration Dependencies

comment:1 by Manuel Kaufmann, 10 years ago

Cc: Manuel Kaufmann added

comment:2 by Andrew Godwin <andrew@…>, 10 years ago

Resolution: fixed
Status: newclosed

In e9249bc20b49c7b6721a8a58bc4bb9dd4827855a:

Fixed #22932: Documented circular dependency issues with swappable user

comment:3 by Andrew Godwin, 10 years ago

This is unavoidable, as Django can't detect swappable dependencies to resolve the loop at makemigrations time (thanks, swappable models!), but I've added some documentation to the custom user model docs to highlight that this might occur and how to fix it.

The fix, incidentally, is to manually uncircularise the dependency loop. In this example, the dependency graph is:

    team.TeamCaptain -> challenge.MyUser -> team.Team
                   \_________________________^

As you can see, this isn't actually circular, it's just that the start and end are in the same app and Django can't work out what app the middle one is in (as it's dynamic and can change based on project settings). The solution is to move the TeamCaptain model into a second migration in the team app, and make sure the migration dependencies are modified appropriately (i.e. that the AUTH_USER_MODEL dependency is also moved to that second migration). This will solve this case.

The other possible case is when there actually is a dependency loop (e.g. two models have foreign keys pointing at each other). You can resolve this by splitting one ForeignKey off into a separate migration; just construct this situation without swappable models and run makemigrations and you'll get an example of how it's done.

comment:4 by Manuel Kaufmann, 10 years ago

Well, I followed the solution suggested by andrewgodwin and it worked. I will explain here what are the specific steps that I followed (actually, I followed these steps in the production site and it also worked):

  1. Download the .tar.gz and uncompress it in /tmp, then cd into it
  2. Run dj makemigrations
  3. Remove the swappable_dependency and the migrations.CreateModel(name='TeamCaptain', ... from team.0001_initial.py
  4. Run dj makemigrations --empty team
  5. Edit this new empty migration created by Django by adding the swappable_dependency and the migrations.CreateModel(name='TeamCaptain', ... removed from team.0001_initial.py
  6. Run dj migrate

I'm attaching the .diff and the new migration

by Manuel Kaufmann, 10 years ago

Attachment: team.0001_initial.py.diff added

Migration edited by hand

by Manuel Kaufmann, 10 years ago

New migration added with --empty and then edited

comment:5 by berto, 9 years ago

I am running into this error when squashing migrations. The swappable dep is from Django REST Framework's authkey app. After reading the fix above I'm not exactly sure how to go about fixing this within a squashed migration.

When running the squashed migration it tells me that the authkey tables do not exist. When adding ('authkey', '0001_initial') to the dependencies list, I get the circular dependency error.

I'm seeing this when running tests, and I'm using Django 1.7.10.

Any clues? Thank you!

comment:6 by berto, 9 years ago

Cc: berto added
Note: See TracTickets for help on using tickets.
Back to Top