Commit 6d9f061b authored by Tim Graham's avatar Tim Graham
Browse files

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

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

Partial backport of 7bec480f from master
parent 8f724817
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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):
+13 −0
Original line number Diff line number Diff line
@@ -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):
        """
+4 −0
Original line number Diff line number Diff line
@@ -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`).
+16 −0
Original line number Diff line number Diff line
@@ -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'
+11 −1
Original line number Diff line number Diff line
@@ -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'))