'''models.py''':
{{{
from django.db import models
class Parent(models.Model):
pass
class RelatedA(models.Model):
parent = models.ForeignKey('Parent')
class RelatedB(models.Model):
parent = models.ForeignKey('Parent')
}}}
A custom data migration '''0002_populate.py''' used to populate the DB
with some initial data:
{{{
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
def populate(apps, schema_editor):
Parent = apps.get_model("foo", "Parent")
if not Parent.objects.filter().exists():
p = Parent()
p.save()
RelatedX = apps.get_model("foo", <<<either "RelatedA" or
"RelatedB">>>)
if not RelatedX.objects.filter(parent = p).exists():
print("\n ********** :-) :-) :-) **********\n")
class Migration(migrations.Migration):
dependencies = [
('foo', '0001_initial'),
]
operations = [
migrations.RunPython(populate)
]
}}}
Line 13 of the data migration has been tested like this:
{{{RelatedX = apps.get_model("foo", "RelatedA")}}}
And like this:
{{{RelatedX = apps.get_model("foo", "RelatedB")}}}
In Django 1.8 and 1.8.1; this gives rise to 4 different tests:
- '''proj180a''': Uses Django 1.8 and imports "RelatedA"
- '''proj180b''': Uses Django 1.8 and imports "RelatedB"
- '''proj181a''': Uses Django 1.8.1 and imports "RelatedA"
- '''proj181b''': Uses Django 1.8.1 and imports "RelatedB"
The test involves running the command:
{{{python manage.py migrate}}}
When the SQLite database '''still doesn't exist'''.
The results are as follows:
- '''proj180a''': Fails on first run, succeeds if {{{python manage.py
migrate}}} is run again.
- '''proj180b''': Pass.
- '''proj181a''': Pass.
- '''proj181b''': Pass.
The failure of '''proj180a''' is as follows:
{{{
File
"C:\Users\dario\Desktop\Repos\Programming\Django\Varie\venv-34-180\lib
\site-packages\django\db\models\sql\query.py", line 1170, in build_filter
self.check_related_objects(field, value, opts)
File
"C:\Users\dario\Desktop\Repos\Programming\Django\Varie\venv-34-180\lib
\site-packages\django\db\models\sql\query.py", line 1068, in
check_related_objects
self.check_query_object_type(value, opts)
File
"C:\Users\dario\Desktop\Repos\Programming\Django\Varie\venv-34-180\lib
\site-packages\django\db\models\sql\query.py", line 1052, in
check_query_object_type
(value, opts.object_name))
ValueError: Cannot query "Parent object": Must be "Parent" instance.
}}}
If the DB exists and migration '''0001_initial.py''' has already been
applied then also the '''proj180a''' test passes on the first run. I'm
attaching the full output (obviously with stderr included) of three
consecutive runs of {{{migrate}}}.
In synthesis, there seems to be a problem with chaining migrations in a
single '''migrate''' command (which should be a fairly typical case). Why
this happens with {{{RelatedA}}} and not with {{{RelatedB}}} is beyond me,
but must have something to do with the order in which the models are
declared in '''models.py'''.
I'm attaching an archive with four projects; two for Django 1.8 and two
for Django 1.8.1. To reproduce the issue you should use a virtualenv with
Diango 1.8 to {{{migrate}}} proj180a and proj180b and a virtualenv with
Django 1.8.1 to {{{migrate}}} proj181a and proj181b.
Finally, I should mention that I derived the discussed SSCCEs from the
code of a real application I'm working on.
In my application the role of {{{Parent}}} and {{{RelatedA}}} is played by
{{{Organization}}} and {{{Link}}} (my application is a CMS that can serve
multiple independent organizations, and specifically each one of them can
mantain its own set of links to other resources and pages).
In the real thing, the line that causes problems is:
{{{if not Link.objects.filter(organization = organization, slug =
slug).exists():}}}
Which results in the following error (like proj180a) on the first
{{{migrate}}} run.
{{{ValueError: Cannot query "Organization object": Must be "Organization"
instance.}}}
It is interesting to note that using related managers doesn't solve the
issue (actually, I discovered the bug while using RMs, but then I learned
that RMs can create problems in migrations, so I opted for the joint
condition filter).
However the error message is not the same, and also in this case the
second {{{migrate}}} runs fine.
{{{if not organization.link_set.filter(slug = slug).exists():}}}
Results in:
{{{AttributeError: 'Organization' object has no attribute 'link_set'}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/24808>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* Attachment "Tests.zip" added.
4 different test cases, plus the output of the one that fails.
* status: new => closed
* needs_docs: => 0
* resolution: => invalid
* needs_tests: => 0
* needs_better_patch: => 0
Comment:
Should we assume from your report that the problem cannot be reproduced on
Django 1.8.1? If it's the case, then why bother? Sorry if I misunderstood
the issue.
--
Ticket URL: <https://code.djangoproject.com/ticket/24808#comment:1>
* resolution: invalid => duplicate
Comment:
For the record, this sounds like #24573 and has been fixed in 1.8.1 and is
properly addressed in the release notes.
--
Ticket URL: <https://code.djangoproject.com/ticket/24808#comment:2>