Fixed #28838 -- Fixed Model.save() crash if the base manager annotates with a related field.

This commit is contained in:
shanghui 2018-01-14 10:58:21 +08:00 committed by Tim Graham
parent cbac11f962
commit 8dc675d90f
3 changed files with 31 additions and 1 deletions

View File

@ -701,6 +701,8 @@ class QuerySet:
"Cannot update a query once a slice has been taken."
query = self.query.chain(sql.UpdateQuery)
query.add_update_fields(values)
# Clear any annotations so that they won't be present in subqueries.
query._annotations = None
self._result_cache = None
return query.get_compiler(self.db).execute_sql(CURSOR)
_update.alters_data = True

View File

@ -25,6 +25,13 @@ class PublishedBookManager(models.Manager):
return super().get_queryset().filter(is_published=True)
class AnnotatedBookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
favorite_avg=models.Avg('favorite_books__favorite_thing_id')
)
class CustomQuerySet(models.QuerySet):
def filter(self, *args, **kwargs):
queryset = super().filter(fun=True)
@ -131,7 +138,6 @@ class Book(models.Model):
title = models.CharField(max_length=50)
author = models.CharField(max_length=30)
is_published = models.BooleanField(default=False)
published_objects = PublishedBookManager()
authors = models.ManyToManyField(Person, related_name='books')
fun_authors = models.ManyToManyField(FunPerson, related_name='books')
favorite_things = GenericRelation(
@ -145,6 +151,12 @@ class Book(models.Model):
object_id_field='favorite_thing_id',
)
published_objects = PublishedBookManager()
annotated_objects = AnnotatedBookManager()
class Meta:
base_manager_name = 'annotated_objects'
def __str__(self):
return self.title

View File

@ -617,6 +617,22 @@ class CustomManagersRegressTestCase(TestCase):
book.refresh_from_db()
self.assertEqual(book.title, 'Hi')
def test_save_clears_annotations_from_base_manager(self):
"""Model.save() clears annotations from the base manager."""
self.assertEqual(Book._meta.base_manager.name, 'annotated_objects')
book = Book.annotated_objects.create(title='Hunting')
Person.objects.create(
first_name='Bugs', last_name='Bunny', fun=True,
favorite_book=book, favorite_thing_id=1,
)
book = Book.annotated_objects.first()
self.assertEqual(book.favorite_avg, 1) # Annotation from the manager.
book.title = 'New Hunting'
# save() fails if annotations that involve related fields aren't
# cleared before the update query.
book.save()
self.assertEqual(Book.annotated_objects.first().title, 'New Hunting')
def test_delete_related_on_filtered_manager(self):
"""Deleting related objects should also not be distracted by a
restricted manager on the related object. This is a regression