Uri Shalit
Uri Shalit

Reputation: 2308

Django get all base class objects that their derived class has a mixin

I have models that look something like this:

class SomeModel(model.Model):
    pass


class Base(models.Model):
    field1 = ...


class Mixin1(models.Model):
    some_model = models.ForeignKey('SomeModel', on_delete=models.CASCADE)

    class Meta:
        abstract = True


class Derive1(Base, Mixin1):
    field2 = ...


class Derive2(Base, Mixin1):
    field3 = ...


class Derive3(Base):
    field4 = ...

Now I want to be able to get all objects that derive from Base and Mixin1, without explicitly listing all classes that use Mixin1. Something like:

Base.objects.filter(some_model=X)

Why this doesn't work I understand, but how to actually make it work I do not know.

Solution: Thanks to valentjedi's answer I got to a working solution. I am posting it here in case it will help someone one day.

On top of wanting all the results together, I also wanted them sorted by a date field in Base and the ability to filter them freely. So I created in Mixin1 the function:

 @staticmethod
 def get_mixin1_objects(some_model, q_filter=None):
     mixin_models = set(Mixin1.__subclasses__()) & set(Base.__subclasses__())
     results = []
     for mdl in mixin_models:
         apply_filter = Q(some_model=some_model)
         if q_filter is not None:
             apply_filter &= q_filter
         results.append(list(mdl.objects.filter(apply_filter).order_by('date').all()))

    # Merge sort all the events
    return list(heapq.merge(*results))

While adding to Base the method:

def __gt__(self, other):
    return self.date > other.date

Thus I can apply filters however I like, and can get order_by a constant field results (which suffices my current needs).

Example of usage:

lst = Mixinq.get_mixin1_objects(some_model_instance, q_filter=~Q(some_field_in_base=some_value))

Upvotes: 2

Views: 1505

Answers (1)

valignatev
valignatev

Reputation: 6316

I might not understand your question clearly, but you can use __subclasses__ magic method for something like this:

>>> set(Mixin1.__subclasses__()) & set(Base.__subclasses__())
{Derive2, Derive1}

Then you can filter those models and do whatever you want to them.

Upvotes: 5

Related Questions