Django query filter combining AND and OR with Q objects don't return the expected results

前端 未结 4 1851
心在旅途
心在旅途 2021-02-05 01:51

I try to combine AND and OR in a filter using Q objects. It looks like that the | behave like an AND. This is related to the previous annotate which is run in the same query and

相关标签:
4条回答
  • 2021-02-05 02:05

    Try adding parentheses to explicitly specify your grouping? As you already figured out, multiple params to filter() are just joined via AND in the underlying SQL.

    Originally you had this for the filter:

    [...].filter(
        Q(hide=False) & Q(deleted=False),
        Q(stock=False) | Q(quantity__gte=1))
    

    If you wanted (A & B) & (C | D) then this should work:

    [...].filter(
        Q(hide=False) & Q(deleted=False) &
        (Q(stock=False) | Q(quantity__gte=1)))
    
    0 讨论(0)
  • 2021-02-05 02:11

    OK, no success here or on #django. So I choose to use a raw SQL query to solve this problem...

    Here the working code:

    types_list = Type.objects.raw('SELECT * FROM equipment_type
        LEFT JOIN (                                            
            SELECT type_id, SUM(quantity) AS qty               
            FROM equipment_item                                
            GROUP BY type_id                                   
        ) T1                                                   
        ON id = T1.type_id                                     
        WHERE hide=0 AND deleted=0 AND (T1.qty > 0 OR stock=0) 
        ')
    
    0 讨论(0)
  • 2021-02-05 02:16

    Here's my example of complicated query, I hope you find it helpful

    or_condition = Q()
    and_condition = Q(company=request.user.profile.company)
    
    for field in MyModel._meta.get_fields():
        if field.name != 'created_on' and field.name != 'company':
            or_condition.add(
                Q(**{"{}__icontains".format(field.name): query}), Q.OR)
    
    and_condition.add(or_condition2, Q.AND)
    MyModel.objects.filter(and_condition)
    

    The problem with this method is that you get an empty (AND: ) case in your or_condition. It doesn't affect the query at all, but it annoys me! My current solution is as follows

    import operator
    from functools import reduce
    
    and_condition = Q(company=request.user.profile.company)
    or_condition = reduce(operator.or_, (Q(**{"{}__icontains".format(field.name): query})
                                         for field in MyModel._meta.get_fields() if field.name != 'created_on' and field.name != 'company'))
    
    and_condition.add(or_condition, Q.AND)
    MyModel.objects.filter(and_condition)
    
    0 讨论(0)
  • 2021-02-05 02:21

    This answer is late but could be helpful to a lot of guys out there.

    [...].filter(hide=False & deleted=False)
    .filter(Q(stock=False) | Q(quantity__gte=1))
    

    This will generate something similar to

    WHERE (hide=0 AND deleted=0 AND (T1.qty > 0 OR stock=0))
    
    0 讨论(0)
提交回复
热议问题