0

I have the following models:

class Camera(models.Model)
    deleted_images_counter = models.IntegerField(...)

class Image(models.Model)
    image = models.ImageField(....)
    camera = models.ForeignKey(Camera)

Now, i want to update the camera deleted_images_counter field when i delete one or more images.

I though about using signals:

@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()

But the problem is that it works at object level, so if i want to delete 200,000 images, it have to make the same amount of updates to the camera.

Is there any way i create a new method or a signal where i can do something like this?

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()

Thanks!

Edit 1:

I have just noticed a flaw on what i want to do. I was assuming that the images to delete belonged to the same camera, however if they are not from the same camera then this approach would not work. The only way to achieve what i want would be in the listener i guess...

Agey
  • 891
  • 8
  • 17
  • Does it have to happen at the model level? Can it happen where you delete images in batch? Or I should ask how are the images deleted? – Shang Wang Oct 13 '15 at 13:55
  • What version of Django are you using? [Since 1.9 `delete`](https://docs.djangoproject.com/en/1.9/ref/models/querysets/#delete) returns a dictionary with `model label -> deleted rows count`. – Ivan Oct 13 '15 at 14:00
  • Well, what i want to do is that when i use .delete(), the counter is updated, so when an image(s) are deleted either in the admin, in the shell, etc, the counter is automatically updated. – Agey Oct 13 '15 at 14:03
  • I'm using django 1.5 – Agey Oct 13 '15 at 14:10

1 Answers1

0

You can the queryset annotate method to get number of images for each camera within the queryset.

See Django docs for details. Their example uses models very similar to yours:

# 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

Off the top of my hat, you can do something like

# 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()
K. Tomov
  • 140
  • 1
  • 8