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()
Другие вопросы по тегам