#22325 closed Bug (fixed)
Defining a custom user model in an app and having relations between it and other models in the app causes circular dependencies
Reported by: | Stephen Burrows | Owned by: | nobody |
---|---|---|---|
Component: | Migrations | Version: | 1.7-beta-1 |
Severity: | Release blocker | Keywords: | |
Cc: | Stephen Burrows, ondrowan@…, Jonas von Poser, chtimbo@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
I *think* the issue is that swappable_dependency resolves to app_label.first, which doesn't make a lot of sense when the app with the migrations is itself being migrated. Removing that line seems to temporarily resolve the issue, but if my app were ever used with a *different* AUTH_USER_MODEL, it would then presumably break again. (Also, I still get a circular dependency in the next migration that defines a relation to AUTH_USER_MODEL.)
Here are the important bits from *one* migration:
class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('app_label', '0002_blah_blah'), ] operations = [ migrations.CreateModel( # This is the AUTH_USER_MODEL name='Person', fields=[ ... ], options={ u'verbose_name': u'person', u'verbose_name_plural': u'people', }, bases=(models.Model,), ), migrations.CreateModel( name='PersonDiscount', fields=[ ... ('person', models.ForeignKey(to=settings.AUTH_USER_MODEL, to_field=u'id')), ], options={ }, bases=(models.Model,), ), ]
Traceback:
Traceback (most recent call last): File "./manage.py", line 10, in <module> execute_from_command_line(sys.argv) File ".../django/django/core/management/__init__.py", line 427, in execute_from_command_line utility.execute() File ".../django/django/core/management/__init__.py", line 419, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File ".../django/django/core/management/base.py", line 288, in run_from_argv self.execute(*args, **options.__dict__) File ".../django/django/core/management/base.py", line 337, in execute output = self.handle(*args, **options) File ".../django/django/core/management/commands/migrate.py", line 103, in handle plan = executor.migration_plan(targets) File ".../django/django/db/migrations/executor.py", line 46, in migration_plan for migration in self.loader.graph.forwards_plan(target): File ".../django/django/db/migrations/graph.py", line 53, in forwards_plan return self.dfs(node, lambda x: self.dependencies.get(x, set())) File ".../django/django/db/migrations/graph.py", line 119, in dfs return _dfs(start, get_children, []) File ".../django/django/db/migrations/graph.py", line 111, in _dfs results = _dfs(n, get_children, path) + results File ".../django/django/db/migrations/graph.py", line 111, in _dfs results = _dfs(n, get_children, path) + results File ".../django/django/db/migrations/graph.py", line 111, in _dfs results = _dfs(n, get_children, path) + results File ".../django/django/db/migrations/graph.py", line 103, in _dfs raise CircularDependencyError(path[path.index(start):] + [start]) django.db.migrations.graph.CircularDependencyError: [('brambling', '0003_blah_blah'), ('brambling', '0003_blah_blah')]
Attachments (1)
Change History (16)
comment:1 by , 11 years ago
Cc: | added |
---|
by , 11 years ago
Attachment: | circular_dependency.patch added |
---|
comment:2 by , 11 years ago
As far as I can see the error is in the build_graph
method of the MigrationLoader
class.
The self.graph.root_nodes(...)
method seems to be invoked before the graph is fully built resulting in some extra nodes in the returned set that are root nodes in the current partial graph but are not root nodes in the final graph. Since the parent of the dependency relationship is drawn from this set of nodes one can end up with an incorrect loop from the node to itself in the graph. This results in a CircularDependencyError exception at a later stage.
The problem seem to be fixed by building the graph in two steps: first handling all normal nodes and then the "__first__"
nodes (that get remapped to a root node). Someone with a deeper understanding of the relevant code will have to be the judge of whether this is a partial or complete fix. At least the patch above fixes the problem for me.
comment:3 by , 11 years ago
Cc: | added |
---|
comment:4 by , 11 years ago
Has patch: | set |
---|
comment:5 by , 11 years ago
Version: | master → 1.7-beta-1 |
---|
comment:6 by , 11 years ago
Cc: | added |
---|
I can confirm this bug and that the patch resolves it. Will be happy to assist with further tests.
comment:7 by , 11 years ago
Triage Stage: | Unreviewed → Accepted |
---|
Triaging this per the previous comment
comment:8 by , 11 years ago
I submitted a pull request with a test here: https://github.com/django/django/pull/2581
As mentioned in the pull request, the patch does not work for the test I added - while it does work for my actual code. This is the first test I wrote for Django itself so it might be due to some error on my part. Trying it for a while, I couldn't find it though. Would appreciate some help in getting this mergeable.
comment:9 by , 11 years ago
Cc: | added |
---|
comment:10 by , 11 years ago
Patch needs improvement: | set |
---|---|
Severity: | Normal → Release blocker |
comment:11 by , 11 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
comment:13 by , 11 years ago
Jonas: I'm reasonably sure the patch I just pushed is the real fix, based on what I know of the problem, but would appreciate if you could make sure it works for your code.
(I forgot to add the test to that commit, it's in one just afterward. Thanks for helping with that)
comment:14 by , 11 years ago
I can't speak for Jonas, but I had similar symptoms which as of this commit are nonexistent. Thanks!
Fix circular dependency by building dependency graph in 2 stages