summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2016-01-09 18:05:57 -0500
committerSimon Charette <charette.s@gmail.com>2016-01-11 14:08:50 -0500
commit27ef6403c8b9f332feb1f9fe52053ded0b91da85 (patch)
tree84749120ce9da4be7d83b9c66df2641b4bb0db72
parent76b4015ea9b02fdbc3acebe71ed79bcc902697da (diff)
[1.9.x] Fixed #25858 -- Bound abstract model application relative relationships.
Thanks to Karl Hobley for the report and Markus, Shai, Aymeric for their input and Tim for the review. Backport of bc7d201bdbaeac14a49f51a9ef292d6312b4c45e from master
-rw-r--r--django/db/models/fields/related.py21
-rw-r--r--docs/releases/1.9.2.txt5
-rw-r--r--tests/model_fields/tests.py54
3 files changed, 74 insertions, 6 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 80b8e63211..ff820ae498 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -39,7 +39,7 @@ from .reverse_related import (
RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
-def resolve_relation(scope_model, relation):
+def resolve_relation(scope_model, relation, resolve_recursive_relationship=True):
"""
Transform relation into a model or fully-qualified model string of the form
"app_label.ModelName", relative to scope_model.
@@ -54,12 +54,11 @@ def resolve_relation(scope_model, relation):
"""
# Check for recursive relations
if relation == RECURSIVE_RELATIONSHIP_CONSTANT:
- relation = scope_model
-
+ if resolve_recursive_relationship:
+ relation = scope_model
# Look for an "app.Model" relation
- if isinstance(relation, six.string_types):
- if "." not in relation:
- relation = "%s.%s" % (scope_model._meta.app_label, relation)
+ elif isinstance(relation, six.string_types) and '.' not in relation:
+ relation = "%s.%s" % (scope_model._meta.app_label, relation)
return relation
@@ -306,6 +305,11 @@ class RelatedField(Field):
field.remote_field.model = related
field.do_related_class(related, model)
lazy_related_operation(resolve_related_class, cls, self.remote_field.model, field=self)
+ else:
+ # Bind a lazy reference to the app in which the model is defined.
+ self.remote_field.model = resolve_relation(
+ cls, self.remote_field.model, resolve_recursive_relationship=False
+ )
def get_forward_related_filter(self, obj):
"""
@@ -1577,6 +1581,11 @@ class ManyToManyField(RelatedField):
lazy_related_operation(resolve_through_model, cls, self.remote_field.through, field=self)
elif not cls._meta.swapped:
self.remote_field.through = create_many_to_many_intermediary_model(self, cls)
+ else:
+ # Bind a lazy reference to the app in which the model is defined.
+ self.remote_field.through = resolve_relation(
+ cls, self.remote_field.through, resolve_recursive_relationship=False
+ )
# Add the descriptor for the m2m relation.
setattr(cls, self.name, ManyToManyDescriptor(self.remote_field, reverse=False))
diff --git a/docs/releases/1.9.2.txt b/docs/releases/1.9.2.txt
index d7857052ad..e68a1b5cf9 100644
--- a/docs/releases/1.9.2.txt
+++ b/docs/releases/1.9.2.txt
@@ -30,3 +30,8 @@ Bugfixes
``db_index=True`` or ``unique=True`` to a ``CharField`` or ``TextField`` that
already had the other specified, or when removing one of them from a field
that had both (:ticket:`26034`).
+
+* Fixed a regression where defining a relation on an abstract model's field
+ using a string model name without an app_label no longer resolved that
+ reference to the abstract model's app if using that model in another
+ application (:ticket`25858`). \ No newline at end of file
diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py
index 34793eacdb..5aafdb2844 100644
--- a/tests/model_fields/tests.py
+++ b/tests/model_fields/tests.py
@@ -7,6 +7,7 @@ from decimal import Decimal
from django import forms, test
from django.apps import apps
+from django.apps.registry import Apps
from django.core import checks, validators
from django.core.exceptions import ValidationError
from django.db import IntegrityError, connection, models, transaction
@@ -247,6 +248,28 @@ class ForeignKeyTests(test.TestCase):
"Pending lookup added for a foreign key on an abstract model"
)
+ def test_abstract_model_app_relative_foreign_key(self):
+ test_apps = Apps(['model_fields', 'model_fields.tests'])
+
+ class Refered(models.Model):
+ class Meta:
+ apps = test_apps
+ app_label = 'model_fields'
+
+ class AbstractReferent(models.Model):
+ reference = models.ForeignKey('Refered', on_delete=models.CASCADE)
+
+ class Meta:
+ app_label = 'model_fields'
+ abstract = True
+
+ class ConcreteReferent(AbstractReferent):
+ class Meta:
+ apps = test_apps
+ app_label = 'tests'
+
+ self.assertEqual(ConcreteReferent._meta.get_field('reference').related_model, Refered)
+
class ManyToManyFieldTests(test.SimpleTestCase):
def test_abstract_model_pending_operations(self):
@@ -269,6 +292,37 @@ class ManyToManyFieldTests(test.SimpleTestCase):
"Pending lookup added for a many-to-many field on an abstract model"
)
+ def test_abstract_model_app_relative_foreign_key(self):
+ test_apps = Apps(['model_fields', 'model_fields.tests'])
+
+ class Refered(models.Model):
+ class Meta:
+ apps = test_apps
+ app_label = 'model_fields'
+
+ class Through(models.Model):
+ refered = models.ForeignKey('Refered', on_delete=models.CASCADE)
+ referent = models.ForeignKey('tests.ConcreteReferent', on_delete=models.CASCADE)
+
+ class Meta:
+ apps = test_apps
+ app_label = 'model_fields'
+
+ class AbstractReferent(models.Model):
+ reference = models.ManyToManyField('Refered', through='Through')
+
+ class Meta:
+ app_label = 'model_fields'
+ abstract = True
+
+ class ConcreteReferent(AbstractReferent):
+ class Meta:
+ apps = test_apps
+ app_label = 'tests'
+
+ self.assertEqual(ConcreteReferent._meta.get_field('reference').related_model, Refered)
+ self.assertEqual(ConcreteReferent.reference.through, Through)
+
class TextFieldTests(test.TestCase):
def test_to_python(self):