1

Django 1.10.1

On a page I have prepared a lot of controls. Some of them are organized as dynamically changing formsets. So, I don't even know how many of them are present.

I need a chained filters with AND, OR and NOT logical operations.

For example, I need something like this:

Entry.objects.filter(headline__startswith='What').exclude(pub_date__gte=datetime.date.today()).filter(pub_date__gte=datetime(2005, 1, 30)).filter(Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))

Once more the number of filters is changing.

I was planning to ac like that: loop through request.POST and depending on a dozen of conditions prepare a string. The same string:

"Entry.objects.filter(headline__startswith='What').exclude(pub_date__gte=datetime.date.today()).filter(pub_date__gte=datetime(2005, 1, 30)).filter(Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))"

So, the string is correct. But I can't make it work with exec(). I asked here: why it is not working. And the answer was: it will not work, run the Python code directly.

I can construct something like that:

entries = Entry.objects.filter( **kwargs )

But this is just one filter. I can't imagine how to chain such filters.

Could you help me here&

Michael
  • 4,273
  • 3
  • 40
  • 69

1 Answers1

1

You can chain a queryset on multiple lines - adding additional queries depending on view logic. These querysets are not executed as they are created, only when they are called.

I am not sure if it is the "best" or most pythonic way, but something like this works very well for me. This is using Django Rest Framework helper methods but it should be applicable without:

def get(self, request, format=None, *args, **kwargs):
        name = request.GET.get('name', None)
        location = request.GET.get('location', None)
        id = request.GET.get('id', None)

        results = Model.objects.all().order_by('-created')  # this would be the default with no filters on it
        results = results.filter(name__iexact=name) if name else results
        results = results.filter(location__icontains=location) if location else results
        results = results.filter(id__exact=id) if id else results

        # process/serialize/send response

Not knowing which parameters you will get can be tricky, but an idea may be to loop over your kwargs and follow the same pattern in the loop - here's a stab (note I haven't tested it):

def get(self, request, format=None, *args, **kwargs):

        results = Model.objects.all().order_by('-created')  # this would be the default
        for param, value in kwargs.items():
             results = results.filter(***need to convert param to orm field***= value)
erik-sn
  • 2,590
  • 1
  • 20
  • 37
  • Working. But there is still a problem with chaining OR operators. I need something like Article.objects.filter( Q(title__icontains="table") | Q(title__icontains="helm") ). Well, this is a problem - the syntax is filter(**kwargs). But how to pass that "I"? – Michael Oct 03 '16 at 10:09
  • Can you follow a pattern similar to this? http://stackoverflow.com/a/852481/4396787 – erik-sn Oct 03 '16 at 10:25