Fixed #28838 -- Fixed Model.save() crash if the base manager annotates with a related field.
This commit is contained in:
parent
cbac11f962
commit
8dc675d90f
|
@ -701,6 +701,8 @@ class QuerySet:
|
||||||
"Cannot update a query once a slice has been taken."
|
"Cannot update a query once a slice has been taken."
|
||||||
query = self.query.chain(sql.UpdateQuery)
|
query = self.query.chain(sql.UpdateQuery)
|
||||||
query.add_update_fields(values)
|
query.add_update_fields(values)
|
||||||
|
# Clear any annotations so that they won't be present in subqueries.
|
||||||
|
query._annotations = None
|
||||||
self._result_cache = None
|
self._result_cache = None
|
||||||
return query.get_compiler(self.db).execute_sql(CURSOR)
|
return query.get_compiler(self.db).execute_sql(CURSOR)
|
||||||
_update.alters_data = True
|
_update.alters_data = True
|
||||||
|
|
|
@ -25,6 +25,13 @@ class PublishedBookManager(models.Manager):
|
||||||
return super().get_queryset().filter(is_published=True)
|
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):
|
class CustomQuerySet(models.QuerySet):
|
||||||
def filter(self, *args, **kwargs):
|
def filter(self, *args, **kwargs):
|
||||||
queryset = super().filter(fun=True)
|
queryset = super().filter(fun=True)
|
||||||
|
@ -131,7 +138,6 @@ class Book(models.Model):
|
||||||
title = models.CharField(max_length=50)
|
title = models.CharField(max_length=50)
|
||||||
author = models.CharField(max_length=30)
|
author = models.CharField(max_length=30)
|
||||||
is_published = models.BooleanField(default=False)
|
is_published = models.BooleanField(default=False)
|
||||||
published_objects = PublishedBookManager()
|
|
||||||
authors = models.ManyToManyField(Person, related_name='books')
|
authors = models.ManyToManyField(Person, related_name='books')
|
||||||
fun_authors = models.ManyToManyField(FunPerson, related_name='books')
|
fun_authors = models.ManyToManyField(FunPerson, related_name='books')
|
||||||
favorite_things = GenericRelation(
|
favorite_things = GenericRelation(
|
||||||
|
@ -145,6 +151,12 @@ class Book(models.Model):
|
||||||
object_id_field='favorite_thing_id',
|
object_id_field='favorite_thing_id',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
published_objects = PublishedBookManager()
|
||||||
|
annotated_objects = AnnotatedBookManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
base_manager_name = 'annotated_objects'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
|
|
@ -617,6 +617,22 @@ class CustomManagersRegressTestCase(TestCase):
|
||||||
book.refresh_from_db()
|
book.refresh_from_db()
|
||||||
self.assertEqual(book.title, 'Hi')
|
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):
|
def test_delete_related_on_filtered_manager(self):
|
||||||
"""Deleting related objects should also not be distracted by a
|
"""Deleting related objects should also not be distracted by a
|
||||||
restricted manager on the related object. This is a regression
|
restricted manager on the related object. This is a regression
|
||||||
|
|
Loading…
Reference in New Issue