Jonathan Livni
Jonathan Livni

Reputation: 107092

Using Django custom manager function on an already filtered queryset

Consider the following case:

class MyModelManager(models.Manager):
    def my_filter(self):
     return [some code here].filter(field_A__gt=3)

class MyModel(models.Model):
    # some fields
    objects = MyModelManager()


# The way I'd like to use it:
qs = MyModel.objects.filter(field_B__lt=1)
# some code
qs = qs.my_filter()

Notice that I'd like to use the custom my_filter() function on the already filtered queryset. What code should I place in the [some code here] above? Is the structure as a whole correct?

Upvotes: 3

Views: 2463

Answers (3)

Dima Bildin
Dima Bildin

Reputation: 1553

I guess there is no way to do it exactly this way. MyModel.objects.filter(field_B__lt=1) will return QuerySet object, not MyModelManager. You may either pass it to my_filter method as Ignacio Vazquez-Abrams mentioned or first apply my_filter to your manager, that will return QuerySet object, that you can filter further

Upvotes: 2

Mp0int
Mp0int

Reputation: 18727

Your approach is wrong because you can only use Model.Manager while you retreive data from database, so using two managers or trying to use a manager ona a queryset causes error.

data = SomeModel.objects.my_manager.all()

or

data = SomeModel.objects.all()
data = data.my_manager.filter(...)

is wrong since you can not use two managers...

One possible waw is defining a function that gets a queryset as a parameter and returns a filtered queryset...

def extra_filter(queryset):
    queryset = queryset.filter(...)

but i am not sure if this helps.

Best approach is defining another model Manager and use it instread of objects if required...

class SomeManager(models.Manager):
    def get_query_set(self):
        return super(SomeManager, self).get_query_set().filter(<filteer criteria>)

class MyModel(models.Model):
    # some fields
    objects = models.Manager()
    mymanager = SomeManager()

data = MyModel.mymanager.all() 

And use just one...

UPDATE: In django, querysets are lazy. That means, you can apply as many filters as you wish and data will not be retreived from the database unliess you try to get a specific record from the filtered or you try to slice it...Documentation is here

So, there is no diffrence between

qs = MyModel.objects.filter(field_B__lt=1)
qs = qs.filter(field_A__gt=3)

and

qs = MyModel.objects.filter(field_A__gt=3)
qs = qs.filter(field_B__lt=1)

So defining a manager to apply a specific filtering is the main reason for using managers...

You define your manager :

class SomeManager(models.Manager):
    def get_query_set(self):
        return super(SomeManager, self).get_query_set().filter(field_A__gt=3)

then you call it with other filtering criteria:

Somemodel.mymodelmanager.filter(field_B__lt=1, ....)

And django will add all filters and evluate the query when you want it to do.

So if it is a filter that you use oftenly, manager i the best choice...

As for an example, i have valid_ field in my most database models and a val manager that filters invalid entries. So When i wish to filter all valid data (90% of the time) i use

Somemodel.val.filter(...)

And in the situation that i need all the data i just use basic django manager:

Somemodel.objects.filter(...)

Upvotes: 1

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 798626

class MyModelManager(models.Manager):
    def my_filter(self, qs=None):
        if qs is None:
            qs = [however you got it before]
        return qs.filter(field_A__gt=3)

Then just pass your queryset to it.

Upvotes: 0

Related Questions