summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2015-11-20 11:31:39 -0500
committerTim Graham <timograham@gmail.com>2015-11-23 11:05:18 -0500
commit6d9f061b07ce7aa1a9da6799b3104971ee73998b (patch)
tree4c8b861d3d6bdb741ee56b23a82e44867f64965d
parent8f724817f3744c626923fe0bf83c48b3fceab774 (diff)
[1.8.x] Fixed #25786 -- Fixed set_FOO_order() crash with order_with_respect_to referencing OneToOneField pk.
Partial backport of 7bec480fe2ace94c8e7f0c88485442bfa74436b4 from master
-rw-r--r--django/db/models/base.py6
-rw-r--r--django/db/models/fields/related.py13
-rw-r--r--docs/releases/1.8.7.txt4
-rw-r--r--tests/order_with_respect_to/models.py16
-rw-r--r--tests/order_with_respect_to/tests.py12
5 files changed, 47 insertions, 4 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 7687eea598..e94c14f869 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -1674,13 +1674,13 @@ class Model(six.with_metaclass(ModelBase)):
def method_set_order(ordered_obj, self, id_list, using=None):
if using is None:
using = DEFAULT_DB_ALIAS
- rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
- order_name = ordered_obj._meta.order_with_respect_to.name
+ order_wrt = ordered_obj._meta.order_with_respect_to
+ filter_args = order_wrt.get_forward_related_filter(self)
# FIXME: It would be nice if there was an "update many" version of update
# for situations like this.
with transaction.atomic(using=using, savepoint=False):
for i, j in enumerate(id_list):
- ordered_obj.objects.filter(**{'pk': j, order_name: rel_val}).update(_order=i)
+ ordered_obj.objects.filter(pk=j, **filter_args).update(_order=i)
def method_get_order(ordered_obj, self):
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 47b479c763..f64ca22b5d 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -314,6 +314,19 @@ class RelatedField(Field):
else:
self.do_related_class(other, cls)
+ def get_forward_related_filter(self, obj):
+ """
+ Return the keyword arguments that when supplied to
+ self.model.object.filter(), would select all instances related through
+ this field to the remote obj. This is used to build the querysets
+ returned by related descriptors. obj is an instance of
+ self.related_field.model.
+ """
+ return {
+ '%s__%s' % (self.name, rh_field.name): getattr(obj, rh_field.attname)
+ for _, rh_field in self.related_fields
+ }
+
@property
def swappable_setting(self):
"""
diff --git a/docs/releases/1.8.7.txt b/docs/releases/1.8.7.txt
index df0ff41159..b11ea9781a 100644
--- a/docs/releases/1.8.7.txt
+++ b/docs/releases/1.8.7.txt
@@ -42,3 +42,7 @@ Bugfixes
* Fixed a duplicate query regression in 1.8 on proxied model deletion
(:ticket:`25685`).
+
+* Fixed ``set_FOO_order()`` crash when the ``ForeignKey`` of a model with
+ ``order_with_respect_to`` references a model with a ``OneToOneField``
+ primary key (:ticket:`25786`).
diff --git a/tests/order_with_respect_to/models.py b/tests/order_with_respect_to/models.py
index 18dbd08dbc..b655d29046 100644
--- a/tests/order_with_respect_to/models.py
+++ b/tests/order_with_respect_to/models.py
@@ -33,3 +33,19 @@ class Post(models.Model):
def __str__(self):
return self.title
+
+
+# order_with_respect_to points to a model with a OneToOneField primary key.
+class Entity(models.Model):
+ pass
+
+
+class Dimension(models.Model):
+ entity = models.OneToOneField('Entity', primary_key=True)
+
+
+class Component(models.Model):
+ dimension = models.ForeignKey('Dimension')
+
+ class Meta:
+ order_with_respect_to = 'dimension'
diff --git a/tests/order_with_respect_to/tests.py b/tests/order_with_respect_to/tests.py
index 60181cca4b..82c323b93b 100644
--- a/tests/order_with_respect_to/tests.py
+++ b/tests/order_with_respect_to/tests.py
@@ -5,7 +5,7 @@ from operator import attrgetter
from django.db import models
from django.test import TestCase
-from .models import Answer, Post, Question
+from .models import Answer, Dimension, Entity, Post, Question
class OrderWithRespectToTests(TestCase):
@@ -103,3 +103,13 @@ class OrderWithRespectToTests2(TestCase):
count += 1
self.assertEqual(count, 1)
+
+
+class TestOrderWithRespectToOneToOnePK(TestCase):
+ def test_set_order(self):
+ e = Entity.objects.create()
+ d = Dimension.objects.create(entity=e)
+ c1 = d.component_set.create()
+ c2 = d.component_set.create()
+ d.set_component_order([c1.id, c2.id])
+ self.assertQuerysetEqual(d.component_set.all(), [c1.id, c2.id], attrgetter('pk'))