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 12:23:23 -0500
commitbc7d201bdbaeac14a49f51a9ef292d6312b4c45e (patch)
treec57fb348dbf867c1639fa1c0be1fbebbbc622d92
parent2ed2db2ea3246c7f985ddfbb780850a23e851f9d (diff)
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.
-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.py43
3 files changed, 63 insertions, 6 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index c877ec1708..162a84793d 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -34,7 +34,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.
@@ -49,12 +49,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
@@ -301,6 +300,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):
"""
@@ -1553,6 +1557,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 14b56139de..9a27dd4670 100644
--- a/tests/model_fields/tests.py
+++ b/tests/model_fields/tests.py
@@ -251,6 +251,25 @@ class ForeignKeyTests(test.TestCase):
"Pending lookup added for a foreign key on an abstract model"
)
+ @isolate_apps('model_fields', 'model_fields.tests')
+ def test_abstract_model_app_relative_foreign_key(self):
+ class Refered(models.Model):
+ class Meta:
+ 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:
+ app_label = 'tests'
+
+ self.assertEqual(ConcreteReferent._meta.get_field('reference').related_model, Refered)
+
class ManyToManyFieldTests(test.SimpleTestCase):
def test_abstract_model_pending_operations(self):
@@ -273,6 +292,30 @@ class ManyToManyFieldTests(test.SimpleTestCase):
"Pending lookup added for a many-to-many field on an abstract model"
)
+ @isolate_apps('model_fields', 'model_fields.tests')
+ def test_abstract_model_app_relative_foreign_key(self):
+ class Refered(models.Model):
+ class Meta:
+ 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 AbstractReferent(models.Model):
+ reference = models.ManyToManyField('Refered', through='Through')
+
+ class Meta:
+ app_label = 'model_fields'
+ abstract = True
+
+ class ConcreteReferent(AbstractReferent):
+ class Meta:
+ 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):