Django - выполнить задачу на уровне набора запросов перед удалением
У меня есть следующие модели:
class Camera(models.Model)
deleted_images_counter = models.IntegerField(...)
class Image(models.Model)
image = models.ImageField(....)
camera = models.ForeignKey(Camera)
Теперь я хочу обновить поле Camera_Direct_Images_Counter при удалении одного или нескольких изображений.
Я думал об использовании сигналов:
@receiver(pre_delete, sender=Image,dispatch_uid="test" )
def update_deleted_images_counter(sender, instance, using, **kwargs):
camera = instance.camera
camera.counter = camera.counter + 1
camera.save()
Но проблема в том, что он работает на уровне объекта, поэтому, если я хочу удалить 200000 изображений, он должен сделать такое же количество обновлений для камеры.
Есть ли способ создать новый метод или сигнал, где я могу сделать что-то подобное?
images_to_delete = Images.objects.filter(...)
images_to_delete.delete()
#some method of something
def delete(queryset, ....):
#update the counter
camera.deleted_images_counter = camera.deleted_images_counter + queryset.count()
camera.save()
#continue with the delete...
queryset.delete()
Спасибо!
Изменить 1:
Я только что заметил недостаток в том, что я хочу сделать. Я предполагал, что удаляемые изображения принадлежат одной и той же камере, однако, если они не принадлежат одной и той же камере, такой подход не будет работать. Думаю, единственный способ добиться того, чего я хочу, - это слушатель...
1 ответ
Вы можете запросить annotate
метод, чтобы получить количество изображений для каждой камеры в наборе запросов.
Смотрите Django Docs для деталей. В их примере используются модели, очень похожие на вашу:
# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
[<Publisher BaloneyPress>, <Publisher SalamiPress>, ...]
>>> pubs[0].num_books
73
# The top 5 publishers, in order by number of books.
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323
С верхней части моей шляпы, вы можете сделать что-то вроде
# this should return a list of dicts of format {'camera': int, 'images_to_delete': int}
aggregated_queryset = image_queryset.values('camera__id').annotate(images_to_delete=Count('id'))
for record in aggregated_queryset:
camera = Camera.objects.get(id=record.get('camera'))
images_to_delete = record.get('images_to_delete')
camera.deleted_images_counter += images_to_delete
camera.save()
image_queryset.delete()