[1.8.x] Fixed #25786 -- Fixed set_FOO_order() crash with order_with_respect_to referencing OneToOneField pk.

Partial backport of 7bec480fe2 from master
This commit is contained in:
Tim Graham 2015-11-20 11:31:39 -05:00
parent 8f724817f3
commit 6d9f061b07
5 changed files with 47 additions and 4 deletions

View File

@ -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):

View File

@ -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):
"""

View File

@ -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`).

View File

@ -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'

View File

@ -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'))