Updating a model when an object it references through a ManyToMany relation is deleted

Viewed 44

I have a model, for instance

class ModelA(models.Model):
  name = models.CharField()

class ModelB(models.Model):
  last_modified_time = models.DateTimeField()
  as = models.ManyToManyField('ModelA')

when I delete an instance of ModelA I would like to also update the last_modified_time field on all ModelB instances that referred to ModelA. I can use a predelete signal for this, i.e.

def pre_delete_handler(sender=None, instance=None, *args, **kwargs):
   # update all using instance.modelbs...

models.signals.pre_delete.connect(pre_delete_handler, sender=ModelA)

but then every time a new model references it via a ManyToMany this bit of code needs to be updated which isn't the best from a maintenance point of view. I am looking for a way to enumerate all models that ModelA is referenced by so I can have a single update that covers all cases but can't figure out the best way to do this. What is the proper way to enumerate all objects that ModelA is related to?

1 Answers

If all the models that refer to A as many-to-many have the field last_modified_time, then something like this can work:

from django.db.models.fields.reverse_related import ManyToManyRel
from django.utils import timezone


def pre_delete_handler(sender=None, instance=None, *args, **kwargs):
    if not instance:
        return

    # Get all reverse m2m fields
    reverse_m2m_fields = [m for m in instance._meta.model._meta.get_fields() if isinstance(m, ManyToManyRel)]
    # or [m for m in ModelA._meta.get_fields() if isinstance(m, ManyToManyRel)]

    for f in reverse_m2m_fields:
        # Find the name of the reverse m2m field from this model
        field_name = f.related_name or f.related_query_name or f'{f.name}_set'
        getattr(instance, field_name).update(last_modified_time=timezone.now())


models.signals.pre_delete.connect(pre_delete_handler, sender=ModelA)
Related