I have following code:
'''app_A/models.py:'''
{{{
class AbstractModel1(Model):
class Meta:
abstract = True
class AbstractModel2(Model):
model1 = ForeignKey('Model1')
class Meta:
abstract = True
class Model1(AbstractModel1):
pass
class Model2(AbstractModel12):
pass
}}}
'''app_B/models.py:'''
{{{
from app_A.models import AbstractModel1, AbstractModel2
class Model1(AbstractModel1):
pass
class Model2(AbstractModel12):
pass
}}}
in Django 1.8, the `app_B.Model2.model1` would point to `app_B.Model1`,
but in Django 1.9.2 it points to `app_A.Model1` so my code no longer
works.
--
Ticket URL: <https://code.djangoproject.com/ticket/26186>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* status: new => closed
* needs_better_patch: => 0
* resolution: => invalid
* needs_tests: => 0
* needs_docs: => 0
Comment:
Hi skyjur,
While I can reproduce on Django 1.9 and 1.9.1 (where it was considered a
regression #25858) I cannot reproduce on any version of Django 1.8.
I even tried swapping the order of `'app_A'` and `'app_B'` in
`INSTALLED_APPS` with no success.
You can find the test applications I used in my attempts to reproduce
[https://github.com/charettes/django-
ticketing/commit/d293d7cfcb8ba72dc1ebb0c0c7645af83339fa34 here].
Are you sure you managed to reproduce against Django 1.8? If it's the case
please reopen this ticket with more details.
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:1>
Comment (by skyjur):
I have updated your test case:
https://github.com/charettes/django-ticketing/pull/1
In my app my abstract models are defined in `a/abstract.py` and then
imported into `a/models.py`, I didn't realise this was important.
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:2>
* status: closed => new
* resolution: invalid =>
Old description:
> This might be related to https://code.djangoproject.com/ticket/25858
>
> I have following code:
>
> '''app_A/models.py:'''
>
> {{{
> class AbstractModel1(Model):
> class Meta:
> abstract = True
>
> class AbstractModel2(Model):
> model1 = ForeignKey('Model1')
>
> class Meta:
> abstract = True
>
> class Model1(AbstractModel1):
> pass
>
> class Model2(AbstractModel12):
> pass
> }}}
>
> '''app_B/models.py:'''
>
> {{{
> from app_A.models import AbstractModel1, AbstractModel2
>
> class Model1(AbstractModel1):
> pass
>
> class Model2(AbstractModel12):
> pass
> }}}
>
> in Django 1.8, the `app_B.Model2.model1` would point to `app_B.Model1`,
> but in Django 1.9.2 it points to `app_A.Model1` so my code no longer
> works.
New description:
This might be related to https://code.djangoproject.com/ticket/25858
I have following code:
'''app_A/models.py:'''
{{{
class AbstractModel1(Model):
class Meta:
abstract = True
class AbstractModel2(Model):
model1 = ForeignKey('Model1')
class Meta:
abstract = True
}}}
'''app_A/models.py:'''
{{{
from .abstract import AbstractModel1, AbstractModel12
class Model1(AbstractModel1):
pass
class Model2(AbstractModel12):
pass
}}}
'''app_B/models.py:'''
{{{
from app_A.abstract import AbstractModel1, AbstractModel2
class Model1(AbstractModel1):
pass
class Model2(AbstractModel12):
pass
}}}
in Django 1.8, the `app_B.Model2.model1` would point to `app_B.Model1`,
but in Django 1.9.2 it points to `app_A.Model1` so my code no longer
works.
Test case can be found: https://github.com/skyjur/django-
ticketing/tree/master/ticket_26186
--
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:3>
Old description:
> This might be related to https://code.djangoproject.com/ticket/25858
>
> I have following code:
>
> '''app_A/models.py:'''
>
> {{{
> class AbstractModel1(Model):
> class Meta:
> abstract = True
>
> class AbstractModel2(Model):
> model1 = ForeignKey('Model1')
>
> class Meta:
> abstract = True
> }}}
>
> '''app_A/models.py:'''
>
> {{{
> from .abstract import AbstractModel1, AbstractModel12
>
> class Model1(AbstractModel1):
> pass
>
> class Model2(AbstractModel12):
> pass
> }}}
>
> '''app_B/models.py:'''
>
> {{{
> from app_A.abstract import AbstractModel1, AbstractModel2
>
> class Model1(AbstractModel1):
> pass
>
> class Model2(AbstractModel12):
> pass
> }}}
>
> in Django 1.8, the `app_B.Model2.model1` would point to `app_B.Model1`,
> but in Django 1.9.2 it points to `app_A.Model1` so my code no longer
> works.
>
> Test case can be found: https://github.com/skyjur/django-
> ticketing/tree/master/ticket_26186
New description:
This might be related to https://code.djangoproject.com/ticket/25858
I have following code:
'''app_A/abstract.py:'''
{{{
class AbstractModel1(Model):
class Meta:
abstract = True
class AbstractModel2(Model):
model1 = ForeignKey('Model1')
class Meta:
abstract = True
}}}
'''app_A/models.py:'''
{{{
from .abstract import AbstractModel1, AbstractModel12
class Model1(AbstractModel1):
pass
class Model2(AbstractModel12):
pass
}}}
'''app_B/models.py:'''
{{{
from app_A.abstract import AbstractModel1, AbstractModel2
class Model1(AbstractModel1):
pass
class Model2(AbstractModel12):
pass
}}}
in Django 1.8, the `app_B.Model2.model1` would point to `app_B.Model1`,
but in Django 1.9.2 it points to `app_A.Model1` so my code no longer
works.
Test case can be found: https://github.com/skyjur/django-
ticketing/tree/master/ticket_26186
--
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:4>
Comment (by skyjur):
I have added another test case showing that in 1.9 it's impossible to
extend from abstract models if they contain foreign keys to abstract model
that is not inside an installed app.
In 1.8 this was possible:
https://github.com/skyjur/django-ticketing/tree/master/ticket_26186_2
tox output:
{{{
django189 runtests: commands[0] | django-admin test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Destroying test database for alias 'default'...
django192 runtests: commands[0] | django-admin test
Creating test database for alias 'default'...
Traceback (most recent call last):
<...>
self._related_fields = self.resolve_related_fields()
File "django-ticketing/ticket_26186/.tox/django192/local/lib/python2.7
/site-packages/django/db/models/fields/related.py", line 567, in
resolve_related_fields
raise ValueError('Related model %r cannot be resolved' %
self.remote_field.model)
ValueError: Related model u'None.Model1' cannot be resolved
ERROR: InvocationError: 'django-
ticketing/ticket_26186_2/.tox/django192/bin/django-admin test'
ERROR: InvocationError: 'django-
ticketing/ticket_26186_2/.tox/django192/bin/django-admin test'
______________summary__________________________ __________
django189: commands succeeded
ERROR: django192: commands failed
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:5>
Comment (by timgraham):
Does it raise a deprecation warning in 1.8? There was a deprecation that
ended in 1.9that may be relevant -- from the release notes: "All models
need to be defined inside an installed application or declare an explicit
`app_label`." To confirm this, can you bisect to the commit in Django
where the behavior changed?
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:6>
Comment (by charettes):
In your example both `app_B.Model2.model1` and `app_A.Model2.model1` are
pointing to `app_B.Model1` because `app_B`'s models module was imported
first (because of the order of `INSTALLED_APPS`) and triggered the import
of `app_A.abstract`.
No version of Django allowed you to make `Model2` point to their
respective `Model1`. The only thing that changed in Django 1.9.2 is that
lazy related references defined on abstract models are now bound to the
application in which they are defined.
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:7>
* status: new => assigned
* owner: nobody => charettes
* severity: Normal => Release blocker
* stage: Unreviewed => Accepted
Comment:
Well I stand corrected. It looks like the reported behavior is possible as
long as the application containing the abstract module is not yet
imported.
Here's what happens on '''Django 1.8'''.
The `b` application is imported by the app loading mechanism and triggers
and the import of `a.abstract`. At this point the abstract models are not
bound to any application and thus the `AbstractModel2.model1` field is not
resolved to any model which allows the `b.Model2.model1` field to resolves
to `b.Model1`. When the `a` application is loaded the abstract models are
bound to it (because they are defined in a `a` submodule) and thus
`a.Model2` automatically resolves to `a.Model1`.
In this case if the `a` application is loaded before the `b` application
`b.Model2` will point to `a.Model1` because the abstract models were bound
to the `a` application when `b.models` will import `a.abstract` (which was
imported by `a.models` first).
Here's what happen before #25858 was fixed ('''1.9, 1.9.1'''):
App relative lazy relationships defined on abstract models were never
resolved thus the behavior described above if the `b` application appeared
before the `a` one on Django `1.8` was always present and not dependent on
`ÌNSTALLED_APPS` ordering.
Here's what happen since #25858 is fixed ('''1.9.2''' , '''master'''):
It's not possible anymore as app relative lazy relationships defined on
abstract models are bound to the application in which they are defined.
I'll take this to the ML again to gather feedback but I'm inclined to
close this as ''wontfix'' because the behavior was only achievable in 1.8
through import side effects.
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:8>
Comment (by charettes):
Posted to the [https://groups.google.com/d/msg/django-
developers/jRut8aYEet0/xmwmFk3sCAAJ mailing list].
I suggested we revert the fix for #25858 and instead document how app
relative relationships defined on
abstract models are resolved.
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:9>
* has_patch: 0 => 1
Comment:
Here's a [https://github.com/django/django/pull/6178 PR] with the
suggested changes.
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:10>
* needs_better_patch: 0 => 1
Comment:
Left comments for improvement.
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:11>
* needs_better_patch: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:12>
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:13>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"0223e213dd690b6b6e0669f836a20efb10998c83" 0223e213]:
{{{
#!CommitTicketReference repository=""
revision="0223e213dd690b6b6e0669f836a20efb10998c83"
Fixed #26186 -- Documented how app relative relationships of abstract
models behave.
This partially reverts commit bc7d201bdbaeac14a49f51a9ef292d6312b4c45e.
Thanks Tim for the review.
Refs #25858.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:14>
Comment (by Simon Charette <charette.s@…>):
In [changeset:"48cf7516409c668bf11e24e6b7cd8eaea13ae3b8" 48cf7516]:
{{{
#!CommitTicketReference repository=""
revision="48cf7516409c668bf11e24e6b7cd8eaea13ae3b8"
[1.9.x] Fixed #26186 -- Documented how app relative relationships of
abstract models behave.
This partially reverts commit bc7d201bdbaeac14a49f51a9ef292d6312b4c45e.
Thanks Tim for the review.
Refs #25858.
Backport of 0223e213dd690b6b6e0669f836a20efb10998c83 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/26186#comment:15>