简体   繁体   中英

How to empty a Django Queryset

I have a custom QuerySet object that has several methods to chain filtering. First, the setup for context.

from django.db.models import Manager, Model
from django.db.models.query import QuerySet

class MyQuerySet(QuerySet):
    def some_filter(self, foo):
        return self.filter(some__chain__of__relationships__foo=foo)

class MyModelManager(Manager):
    def get_query_set(self):
        return MyQuerySet(self.model, using=self._db)

class MyModel(Model):
    objects = MyModelManager()

Use Case:

qs = MyModel.objects.get_query_set()
qs = qs.filter_by_name(name).filter_by_color(color).filter_by_date(date)

I have a situation where I need to return an empty queryset of my object, not Django's EmptyQuerySet.

def filter_by_color(self, color):
    if color.is_active:
        return self.filter(some__chain__of__relationships__color=color)
    return self.empty()

How would I define .empty() ? I can't use .none() because the .filter_by_date(date) will throw an error because an EmptyQuerySet has no .filter_by_date() method. I'm currently using the hack where=['1=0']

def empty(self):
    return self.extra(where=['1=0'])

Or...:

def empty(self):
    return self.filter(pk=0)

I would much prefer to do this in a non-hack way.

What is the Pythonic way return an empty queryset of my custom QuerySet object?

If you're using Django <= 1.5, you can subclass both the EmptyQuerySet class and your own custom queryset class. Just override none() to return your custom class:

class MyQuerySet(QuerySet):
    def none(self):
        # prevent circular import
        from . import MyEmptyQuerySet
        return self._clone(klass=MyEmptyQuerySet)

class MyEmptyQuerySet(EmptyQuerySet, MyQuerySet):
    pass

In Django 1.6, the class of your queryset is still the same if you call none() , but through use of metaclasses and overriding __instancecheck__ , calling isinstance(qs.none(), EmptyQuerySet) will still return True . So in Django 1.6, there is no need for custom classes or anything, your new methods on your custom queryset class are still available on an empty queryset.

qs = qs.filter_by_name(name).filter_by_color(color)
qs = qs.filter_by_date(date) if isinstance(qs, MyQuerySet) else qs

That's the simplest thing I can see right now. Or filter by color at the end of the chain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM