2

I'm working on a django application, and I want to create a mixin that will always be applied to model definitions. The functionality added by the mixin isn't important for the question.

However, I need to be able to retrieve a list of all the class definitions that inherit from this mixin. These classes can be in different apps within the django project.

Possible answers I've found so far are:

How to find all the subclasses of a class given its name?

How to auto register a class when it's defined

I'm mostly wondering what the best method would be to accomplish this goal.

Swan
  • 72
  • 5
  • 2
    you want to do this in code, e.g. your application needs to do it, or you want to do it to check those classes, if the later i would do a git grep MixinName – E.Serra May 29 '20 at 10:02
  • There's basically no way to do this *programmatically*, as there can be an infinite number of other modules importing and using that mixin, and your program can't really discover them all. To do this "manually", a good IDE will help. – deceze May 29 '20 at 10:03
  • In this case the mixins will only be applied to actual django model definitions, I edited the question to reflect this. Does this impact the discoverability of the classes? – Swan May 29 '20 at 10:10
  • So did you try `__subclasses__` from your link? Or by defining `__init_subclass__`? – juanpa.arrivillaga May 29 '20 at 10:29
  • I did try ``__subclasses__``, however it is only able to reference models that are actually imported in the section of the code where it's used. So while in most cases it was enough, in some cases certain subclasses in the project were skipped. – Swan May 29 '20 at 14:48

1 Answers1

1

Typically the way you would get a list of all the classes that inherit a particular class would be by registering the classes with a meta-class (as is explained in one of the questions you have linked). Django models however use their own meta class to achieve a lot of what they do automatically. I wouldn't recommend adding another meta-class into the mix - things could go wrong here!!

Fortunately however, django has something called the content-types framework which is essentially a registry of all of your models in a particular project.

To get a list of all of your models you can do this:

from django.contrib.contenttypes.models import ContentType

content_types = ContentType.objects.all()

This won't get the actual models, but rather a queryset of ContentType instances. To then get the model classes from this you can do

models = [x.model_class() for x in content_types]

So we now have a list of models. Then we can just filter the list down to those models which inherit your mixin:

models_with_mixin = [x for x in models if issubclass(x, MyMixin)]

We can simplify all of the above into the following:

from django.contrib.contenttypes.models import ContentType

models_with_mixin = [
    x.model_class()
    for x in ContentType.objects.all()
    if issubclass(x.model_class(), MyMixin)
]
tim-mccurrach
  • 6,395
  • 4
  • 23
  • 41
  • This answer solved the problem for me. I was already familiar with ContentType, but I hadn't realized it could be used like this. There's 1 mistake in the code snippets however. The arguments to ``issubclass`` should be the other way around: ``issubclass(x, MyMixin)`` – Swan May 29 '20 at 14:49
  • ah, sorry for the typo. I'll correct that. Glad to help :) – tim-mccurrach May 29 '20 at 14:50
  • You may need to throw in `if x.model_class() and issubclass(...)`, because it's possible for `model_class()` to return `None` which will throw `TypeError: issubclass() arg 1 must be a class` if passed to `issubclass`. – Ben Quigley Aug 30 '23 at 16:26