3

In this django project, there are a lot of models and we want to add a custom queryset and a manager to each using the queryset.as_manager without having to specify it in each model by hand.

All the queryset/managers are organized in a parallel structure to each model in a manager.py folder in the individual django apps.

Since the managers are a class level attribute, adding them in the init does not work and django does some pretty fancy meta programming so what's the right way to do this? Both Django 1.7 and 1.8 are acceptable. Thanks

(I get that the pythonic way is explicit better than implicit but it seems to be a a lot of crud code here so is there a Meta way of doing this.)

eskhool
  • 657
  • 1
  • 11
  • 24

2 Answers2

4

You could write a factory function to create proxy models like this one:

def model_with_queryset(model_class, queryset_class):
    class Proxy(model_class):
        objects = queryset_class.as_manager()
        class Meta:
            proxy = True
            app_label = model_class._meta.app_label
    return Proxy

Here model_class would be any model you want to change the queryset on.

You could then create model classes dynamically:

SomeModelWithSomeQS = model_with_queryset(SomeModel, SomeQS)

and use them as any other (proxy) model.

Ivan
  • 5,803
  • 2
  • 29
  • 46
2

The django-way would be to use the db.signals.class_prepared signal and draw inspiration from django.db.managers.ensure_default_manager() to properly add the manager to the class (mainly using model.add_to_class() to make sure the managers's contribute_to_class() method is properly invoked). Then you'll have to write some utility function to retrieve the appropriate queryset for a given model class.

Or you could write your own custom metaclass (based on django.db.models.base.ModelBase) and model class (using your custom metaclass) and make all your model classes inherit from it.

This being said, you'd really need to have a lot of models to even consider either of these solutions as economically valid...

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • To develop the second idea further (custom metaclass), since we already have all our model classes inheriting from a common base model class (which was empty to begin with) for just this type of thing..the problem I face is that the django metaclass does not really support being subclassed...or I am not doing this right. Care to write up a small snippet or pseudo code for approach 2? Its an enterprise app, I'd say a 100 models or so...economic enough I think :) Also keeps the changes centralized and reduces boiler plate – eskhool Sep 17 '15 at 10:15
  • 1
    I was able to figure it out using the django metaclass code as a source. I can write it up as my own answer and accept it, however as per SO protocol the first opportunity for the solution should be yours :)...If you do put the code up, I'll happily accept it as the answer or I'll write up my own. Let me know either ways. – eskhool Sep 17 '15 at 11:27
  • @eskhool well since you got the job done please feel free to post your answer - I don't have time to write a working django 1.7+ example right now anyway ;) – bruno desthuilliers Sep 17 '15 at 11:32