12

I'd like to warn or prevent a user from deleting an object instance which is referred to by other instances. Is there a nice way to do this?

One way would be to get a list of models which include the referent and then try reverse look-ups on them. Is there a way to get that list of models? Or is there a better way?

While investigating the collector suggestion, I found some related information and wrote the following which finds the classes which have the referent as a foreign key:

def find_related(cl, app):
    """Find all classes which are related to the class cl (in app) by 
    having it as a foreign key."""

    from django.db import models

    all_models = models.get_models()
    ci_model = models.get_model(app, cl)
    for a_model in all_models:
        for f in a_model._meta.fields:
            if isinstance(f, ForeignKey) and (f.rel.to == ci_model):
                print a_model.__name__

Based on suggestion to use the code in collect:

def find_related(instance):
"""Find all objects which are related to instance."""

for related in instance._meta.get_all_related_objects():
    acc_name = related.get_accessor_name()
    referers = getattr(instance, acc_name).all()
    if referers:
        print related
Mitch
  • 2,350
  • 7
  • 29
  • 48
  • Thanks for updating this question with your final solution. Excellent work. – Spike Jan 18 '12 at 18:42
  • You have `find_related` taking `(cl, app)`, while `get_model` takes `(app, cl)`. It's really confusing! Also, if anyone else is reading this, `cl` and `app` are strings - don't pass the objects! – Casebash Feb 05 '14 at 05:38

1 Answers1

4

Django has something called Collector class. It is used by Django when performing a model deletion. What it does seems like exactly what you want. By calling collect() it finds all the references to the object in the model graph. Additionally it offers a way to delete all the found objects, with a delete() call.

That said I've never used this class myself, I just know it exists. The API is somewhat convoluted, but if you're willing to dig into the internals of Django a little bit, it might save you a lot of coding.

Paolo
  • 20,112
  • 21
  • 72
  • 113
julx
  • 8,694
  • 6
  • 47
  • 86
  • That does look promising but I'm using 1.0 which only has the predecessor of Collector - CollectedObjects which doesn't look like it has a similar method. CollectedObjects is used in (http://stackoverflow.com/questions/437166/duplicating-model-instances-and-their-related-objects-in-django-algorithm-for-r) to find objects referred to by the instance to be deleted. – Mitch Sep 24 '11 at 16:42
  • Can we assume that (a) there is a good reason you can't upgrade to a more recent version and (b) likewise you can't just use the collector on its own? – Marcin Sep 24 '11 at 16:57
  • @Mitch: Yea, I'd agree with Marcin here, that if you can't upgrade you can always make an attempt to use `Collector.collect()` implementation as a base for your own code. Django code in general is far from clean, but after some analysis, it should be still usable. – julx Sep 24 '11 at 19:41
  • Thanks. I looked at collect and only needed to use a small part to do what I wanted. Another reference is: http://stackoverflow.com/questions/2233883/get-all-related-django-model-objects – Mitch Sep 25 '11 at 02:47