mirror of https://github.com/django/django.git
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."
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue