2

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))
Community
  • 1
  • 1
Uri Shalit
  • 2,198
  • 2
  • 19
  • 29
  • If Derive classes don't add any attributes, have you considered the proxy model approach? https://docs.djangoproject.com/en/1.10/topics/db/models/#proxy-models – abrunet Feb 15 '17 at 08:57
  • I do though use fields in all classes. Edited the question. – Uri Shalit Feb 15 '17 at 09:02

1 Answers1

5

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.

valignatev
  • 6,020
  • 8
  • 37
  • 61
  • Suppose I do this, I need then to run a query on each of the resulting models in the set to get all objects correlating to the foreign key I want to search by? – Uri Shalit Feb 15 '17 at 09:05
  • yes, and benefit of it is that you'll get correct correct instances e.g. `Derive1` and `Derive2` and not `Base` – valignatev Feb 15 '17 at 09:22
  • The problem with this is I lose the QuerySet functionalities. In Django 1.11 there is supposed to be [qs.union](http://stackoverflow.com/a/42186970/5019818) - but in the meantime this will have to do - therefore marking as correct answer – Uri Shalit Feb 15 '17 at 12:37
  • Why you think so? You still can do something llike `{Derive2, Derive1}[0].objects.get()` etc. And you also can union querysets with `qs1 | qs2`. – valignatev Feb 15 '17 at 13:58
  • 1
    You can't union querysets of different models. Look at the solution I posted in my answer - I want to be able to get 1 list in the end. – Uri Shalit Feb 15 '17 at 14:02