ChatGPT解决这个技术问题 Extra ChatGPT

How do I do an OR filter in a Django query?

I want to be able to list the items that either a user has added (they are listed as the creator) or the item has been approved.

So I basically need to select:

item.creator = owner or item.moderated = False

How would I do this in Django? (preferably with a filter or queryset).


p
phoenix

There is Q objects that allow to complex lookups. Example:

from django.db.models import Q

Item.objects.filter(Q(creator=owner) | Q(moderated=False))

how could this be done programmatically? So, for example be able to have for f in filters: Item.objects.filter(Q(creator=f1) | Q(creator=f2) | ...)
@AlexisK Use something like reduce(lambda q, f: q | Q(creator=f), filters, Q()) to create the big Q object.
@alexis: you could also do Item.objects.filter(creator__in=creators), for example.
If you wondering (like me) where | being used as OR operator comes from, it's actually the set union operator. It's also used (not here) as bitwise OR: stackoverflow.com/questions/5988665/pipe-character-in-python
A
Andy Baker

You can use the | operator to combine querysets directly without needing Q objects:

result = Item.objects.filter(item.creator = owner) | Item.objects.filter(item.moderated = False)

(edit - I was initially unsure if this caused an extra query but @spookylukey pointed out that lazy queryset evaluation takes care of that)


To find out which queries are executed on a given request, you can use the debug-toolbar Django application. It's made of awesome and win.
do 'from django.db import connection' and use 'connection.queries'. This requires DEBUG=True. BTW, you should know that QuerySets are lazy and this hits the DB just once.
Could exclude be used with negated comparisons?
can this result in duplicates in the result queryset?
More specifically query sets tend to hit the DB only when you try to index into them, otherwise you're just building a query.
m
marxin

It is worth to note that it's possible to add Q expressions.

For example:

from django.db.models import Q

query = Q(first_name='mark')
query.add(Q(email='mark@test.com'), Q.OR)
query.add(Q(last_name='doe'), Q.AND)

queryset = User.objects.filter(query)

This ends up with a query like :

(first_name = 'mark' or email = 'mark@test.com') and last_name = 'doe'

This way there is no need to deal with or operators, reduce's etc.


But it's easier to write query |= Q(email='mark@test.com')?
@Alex78191, different folks have different coding style preferences, but besides that, this usage allows the operator (Q.OR or Q.AND) to be given as an argument to a function that may be required to handle both scenarios.
A
Abhishek Chauhan

You want to make filter dynamic then you have to use Lambda like

from django.db.models import Q

brands = ['ABC','DEF' , 'GHI']

queryset = Product.objects.filter(reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]))

reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]) is equivalent to

Q(brand=brands[0]) | Q(brand=brands[1]) | Q(brand=brands[2]) | .....

Perfect answer for me! For python3, do from functools import reduce beforehand.
Why not to use operator.or_ instead of lambda x, y: x | y?
f
frnhr

Similar to older answers, but a bit simpler, without the lambda...

To filter these two conditions using OR:

Item.objects.filter(Q(field_a=123) | Q(field_b__in=(3, 4, 5, ))

To get the same result programmatically:

filter_kwargs = {
    'field_a': 123,
    'field_b__in': (3, 4, 5, ),
}
list_of_Q = [Q(**{key: val}) for key, val in filter_kwargs.items()]
Item.objects.filter(reduce(operator.or_, list_of_Q))

operator is in standard library: import operator
From docstring:

or_(a, b) -- Same as a | b.

For Python3, reduce is not a builtin any more but is still in the standard library: from functools import reduce

P.S.

Don't forget to make sure list_of_Q is not empty - reduce() will choke on empty list, it needs at least one element.


F
Furkan Siddiqui

Multiple ways to do so.

1. Direct using pipe | operator.

from django.db.models import Q

Items.objects.filter(Q(field1=value) | Q(field2=value))

2. using __or__ method.

Items.objects.filter(Q(field1=value).__or__(field2=value))

3. By changing default operation. (Be careful to reset default behavior)

Q.default = Q.OR # Not recommended (Q.AND is default behaviour)
Items.objects.filter(Q(field1=value, field2=value))
Q.default = Q.AND # Reset after use.

4. By using Q class argument _connector.

logic = Q(field1=value, field2=value, field3=value, _connector=Q.OR)
Item.objects.filter(logic)

Snapshot of Q implementation

class Q(tree.Node):
    """
    Encapsulate filters as objects that can then be combined logically (using
    `&` and `|`).
    """
    # Connection types
    AND = 'AND'
    OR = 'OR'
    default = AND
    conditional = True

    def __init__(self, *args, _connector=None, _negated=False, **kwargs):
        super().__init__(children=[*args, *sorted(kwargs.items())], connector=_connector, negated=_negated)

    def _combine(self, other, conn):
        if not(isinstance(other, Q) or getattr(other, 'conditional', False) is True):
            raise TypeError(other)

        if not self:
            return other.copy() if hasattr(other, 'copy') else copy.copy(other)
        elif isinstance(other, Q) and not other:
            _, args, kwargs = self.deconstruct()
            return type(self)(*args, **kwargs)

        obj = type(self)()
        obj.connector = conn
        obj.add(self, conn)
        obj.add(other, conn)
        return obj

    def __or__(self, other):
        return self._combine(other, self.OR)

    def __and__(self, other):
        return self._combine(other, self.AND)
    .............

Ref. Q implementation


very good overview over the options +1
D
Dorin Rusu

This might be useful https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships

Basically it sounds like they act as OR


R
Raj Kushwaha R
Item.objects.filter(field_name__startswith='yourkeyword')

Please add some explanation to your answer such that others can learn from it
The question is specifically about building a query in Django to get records with field1 = 'value1' OR field2 == 'value2'. Your answer doesn't answer the question.