5

I have a Django query that fetches from MyModel based on certain conditions:

if beta:
    MyModel.object.filter(x=alpha, y=beta)
else:
    MyModel.object.filter(x=alpha)

Is it possible to eliminate the if beta: check and do it in a single line i.e. make the query filter on y only when beta is not None

Is this a good (Djangonic) way:

MyModel.object.filter(**{'x':alpha, 'b':beta} if beta else **{'x':alpha})

Or is it possible to do something like this (I know the following is wrong, but can it be fixed to give the correct meaning?):

MyModel.object.filter(Q('x'=alpha) & (beta && Q('y'=beta)))
user
  • 17,781
  • 20
  • 98
  • 124

4 Answers4

8

In such situations I'm using solution like below:

filter_kwargs = {'x': alpha}
if beta:
    filter_kwargs['y'] = beta

 MyModel.objects.filter(**filter_kwargs)

It is easy to expand when new conditions come in into the project requirements, but unfortunately its not a one-liner solution.

Kamil Rykowski
  • 1,449
  • 13
  • 26
1

One way to do this is to use Q objects, see this answer.

In your case :

query = Q(x=alpha)
if beta:
    query = query & Q(y=beta)
MyModel.object.filter(query)

Not shorter than other examples, but it could be cleaner if you were to add more variables to test.

HTH,

Community
  • 1
  • 1
Ambroise
  • 1,649
  • 1
  • 13
  • 16
0

In my opinion it isn't good solution. I have problem to read and understand the code.

You can also do something like this:

objects_ = MyModel.objects.filter(x=alpha, y=beta) if beta else MyModel.objects.filter(x=alpha)

return objects_

For me it's more clean.. but it's only my opinion.

Sławek Kabik
  • 669
  • 8
  • 13
  • Yeah.. it's your opinion.. but it's not more repetitive. Python programmer always has to look for simple and clean solution. – Sławek Kabik May 20 '14 at 08:19
  • A line of python code longer than 80 characters is always unreadable. It is my opinion after 20 years. – Tonio May 12 '23 at 18:42
0

I would use

objects = MyObject.objects.filter(x=alpha)
if beta:
    # Additional filter because of ...
    objects = objects.filter(y=beta)

Your way is simply harder to read. Python should be easy to read. Note that this only works right for simple filters (no multi-valued relations), as buffer mentions. Otherwise, your original query looks best to me.

RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79
  • 1
    Careful with this. [For multi-valued relations](https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships) `Everything inside a single filter() call is applied simultaneously to filter out items matching all those requirements. Successive filter() calls further restrict the set of objects, but for multi-valued relations, they apply to any object linked to the primary model, not necessarily those objects that were selected by an earlier filter() call.` – user May 20 '14 at 14:15
  • Thanks, I didn't actually know the details of that. Updated answer a bit. – RemcoGerlich May 20 '14 at 14:20