A queryset that happens to return nothing if evaluated, when updated, may erroneously update all rows if the queryset refers to an inherited model and the update columns are in the base table. I'm testing with django 1.1.X on rev 11752. This is a bug with potentially very serious consequences. (Note: I believe this bug to be distinct, although similar in its effects, to #12142.)
Here is test code to produce the problem. One of the below tests will fail (test_empty_update_with_inheritance).
#models.py
from django.db import models
# Create your models here.
class A(models.Model):
x=models.IntegerField(default=10)
class B(models.Model):
a=models.ForeignKey(A)
y=models.IntegerField(default=10)
class C(models.Model):
y=models.IntegerField(default=10)
class D(C):
a=models.ForeignKey(A)
# tests.py
from django.test import TestCase
from models import A, B, D
class SimpleTest(TestCase):
def setUp(self):
self.a1=A.objects.create()
self.a2=A.objects.create()
for x in range(20):
B.objects.create(a=self.a1)
D.objects.create(a=self.a1)
def tearDown(self):
B.objects.all().delete()
A.objects.all().delete()
def test_nonempty_update(self):
"""
Test that update changes that right number of rows for a nonempty queryset
"""
num_updated=self.a1.b_set.update(y=100)
self.failUnlessEqual(num_updated, 20)
cnt=B.objects.filter(y=100).count()
self.failUnlessEqual(cnt, 20)
def test_empty_update(self):
"""
Test that update changes that right number of rows for an empty queryset
"""
num_updated=self.a2.b_set.update(y=100)
self.failUnlessEqual(num_updated, 0)
cnt=B.objects.filter(y=100).count()
self.failUnlessEqual(cnt, 0)
def test_nonempty_update_with_inheritance(self):
"""
Test that update changes that right number of rows for an empty queryset
when the update affects only a base table
"""
num_updated=self.a1.d_set.update(y=100)
self.failUnlessEqual(num_updated, 20)
cnt=D.objects.filter(y=100).count()
self.failUnlessEqual(cnt, 20)
def test_empty_update_with_inheritance(self):
"""
Test that update changes that right number of rows for an empty queryset
when the update affects only a base table
"""
num_updated=self.a2.d_set.update(y=100)
self.failUnlessEqual(num_updated, 0)
cnt=D.objects.filter(y=100).count()
self.failUnlessEqual(cnt, 0)
Fix for related updates query construction