Code

Ticket #16211: issue16211-query-expression-logical-operators.patch

File issue16211-query-expression-logical-operators.patch, 9.9 KB (added by wdoekes, 3 years ago)
  • django/db/models/expressions.py

     
    1818    AND = '&' 
    1919    OR = '|' 
    2020 
     21    # Logical operators 
     22    NOT = 'NOT' # unary, needs special attention in combine_expression 
     23    EQ = '=' 
     24    GE = '>=' 
     25    GT = '>' 
     26    LE = '<=' 
     27    LT = '<' 
     28    NE = '<>' 
     29 
    2130    def __init__(self, children=None, connector=None, negated=False): 
    2231        if children is not None and len(children) > 1 and connector is None: 
    2332            raise TypeError('You have to specify a connector.') 
     
    91100    def __ror__(self, other): 
    92101        return self._combine(other, self.OR, True) 
    93102 
     103    def __invert__(self): 
     104        obj = ExpressionNode([self], connector=self.NOT, negated=True) 
     105        return obj 
     106 
     107    def __eq__(self, other): 
     108        return self._combine(other, self.EQ, False) 
     109 
     110    def __ge__(self, other): 
     111        return self._combine(other, self.GE, False) 
     112 
     113    def __gt__(self, other): 
     114        return self._combine(other, self.GT, False) 
     115 
     116    def __le__(self, other): 
     117        return self._combine(other, self.LE, False) 
     118 
     119    def __lt__(self, other): 
     120        return self._combine(other, self.LT, False) 
     121 
     122    def __ne__(self, other): 
     123        return self._combine(other, self.NE, False) 
     124 
    94125    def prepare_database_save(self, unused): 
    95126        return self 
    96127 
  • django/db/backends/__init__.py

     
    771771        can vary between backends (e.g., Oracle with %% and &) and between 
    772772        subexpression types (e.g., date expressions) 
    773773        """ 
     774        if connector == 'NOT': 
     775            assert len(sub_expressions) == 1 
     776            return 'NOT (%s)' % sub_expressions[0] 
    774777        conn = ' %s ' % connector 
    775778        return conn.join(sub_expressions) 
    776779 
  • django/utils/tree.py

     
    8787        Otherwise, the whole tree is pushed down one level and a new root 
    8888        connector is created, connecting the existing tree and the new node. 
    8989        """ 
    90         if node in self.children and conn_type == self.connector: 
    91             return 
     90        # Using for loop with 'is' instead of 'if node in children' so node 
     91        # __eq__ method doesn't get called 
     92        for child in self.children: 
     93            if node is child and conn_type == self.connector: 
     94                return 
    9295        if len(self.children) < 2: 
    9396            self.connector = conn_type 
    9497        if self.connector == conn_type: 
  • tests/modeltests/expressions/tests.py

     
    88class ExpressionsTests(TestCase): 
    99    def test_filter(self): 
    1010        Company.objects.create( 
    11             name="Example Inc.", num_employees=2300, num_chairs=5, 
     11            name="Example Inc.", num_employees=2300, num_chairs=5, is_large=False, 
    1212            ceo=Employee.objects.create(firstname="Joe", lastname="Smith") 
    1313        ) 
    1414        Company.objects.create( 
    15             name="Foobar Ltd.", num_employees=3, num_chairs=4, 
     15            name="Foobar Ltd.", num_employees=3, num_chairs=4, is_large=False, 
    1616            ceo=Employee.objects.create(firstname="Frank", lastname="Meyer") 
    1717        ) 
    1818        Company.objects.create( 
    19             name="Test GmbH", num_employees=32, num_chairs=1, 
     19            name="Test GmbH", num_employees=32, num_chairs=1, is_large=False, 
    2020            ceo=Employee.objects.create(firstname="Max", lastname="Mustermann") 
    2121        ) 
    2222 
    2323        company_query = Company.objects.values( 
    24             "name", "num_employees", "num_chairs" 
     24            "name", "num_employees", "num_chairs", "is_large" 
    2525        ).order_by( 
    26             "name", "num_employees", "num_chairs" 
     26            "name", "num_employees", "num_chairs", "is_large" 
    2727        ) 
    2828 
    2929        # We can filter for companies where the number of employees is greater 
     
    3434                    "num_chairs": 5, 
    3535                    "name": "Example Inc.", 
    3636                    "num_employees": 2300, 
     37                    "is_large": False 
    3738                }, 
    3839                { 
    3940                    "num_chairs": 1, 
    4041                    "name": "Test GmbH", 
    41                     "num_employees": 32 
     42                    "num_employees": 32, 
     43                    "is_large": False 
    4244                }, 
    4345            ], 
    4446            lambda o: o 
     
    5254                { 
    5355                    "num_chairs": 2300, 
    5456                    "name": "Example Inc.", 
    55                     "num_employees": 2300 
     57                    "num_employees": 2300, 
     58                    "is_large": False 
    5659                }, 
    5760                { 
    5861                    "num_chairs": 3, 
    5962                    "name": "Foobar Ltd.", 
    60                     "num_employees": 3 
     63                    "num_employees": 3, 
     64                    "is_large": False 
    6165                }, 
    6266                { 
    6367                    "num_chairs": 32, 
    6468                    "name": "Test GmbH", 
    65                     "num_employees": 32 
     69                    "num_employees": 32, 
     70                    "is_large": False 
    6671                } 
    6772            ], 
    6873            lambda o: o 
     
    7681                { 
    7782                    'num_chairs': 2302, 
    7883                    'name': u'Example Inc.', 
    79                     'num_employees': 2300 
     84                    'num_employees': 2300, 
     85                    'is_large': False 
    8086                }, 
    8187                { 
    8288                    'num_chairs': 5, 
    8389                    'name': u'Foobar Ltd.', 
    84                     'num_employees': 3 
     90                    'num_employees': 3, 
     91                    'is_large': False 
    8592                }, 
    8693                { 
    8794                    'num_chairs': 34, 
    8895                    'name': u'Test GmbH', 
    89                     'num_employees': 32 
     96                    'num_employees': 32, 
     97                    'is_large': False 
    9098                } 
    9199            ], 
    92100            lambda o: o, 
     
    101109                { 
    102110                    'num_chairs': 6900, 
    103111                    'name': u'Example Inc.', 
    104                     'num_employees': 2300 
     112                    'num_employees': 2300, 
     113                    'is_large': False 
    105114                }, 
    106115                { 
    107116                    'num_chairs': 9, 
    108117                    'name': u'Foobar Ltd.', 
    109                     'num_employees': 3 
     118                    'num_employees': 3, 
     119                    'is_large': False 
    110120                }, 
    111121                { 
    112122                    'num_chairs': 96, 
    113123                    'name': u'Test GmbH', 
    114                     'num_employees': 32 
     124                    'num_employees': 32, 
     125                    'is_large': False 
    115126                } 
    116127            ], 
    117128            lambda o: o, 
     
    126137                { 
    127138                    'num_chairs': 5294600, 
    128139                    'name': u'Example Inc.', 
    129                     'num_employees': 2300 
     140                    'num_employees': 2300, 
     141                    'is_large': False 
    130142                }, 
    131143                { 
    132144                    'num_chairs': 15, 
    133145                    'name': u'Foobar Ltd.', 
    134                     'num_employees': 3 
     146                    'num_employees': 3, 
     147                    'is_large': False 
    135148                }, 
    136149                { 
    137150                    'num_chairs': 1088, 
    138151                    'name': u'Test GmbH', 
    139                     'num_employees': 32 
     152                    'num_employees': 32, 
     153                    'is_large': False 
    140154                } 
    141155            ], 
    142156            lambda o: o, 
    143157        ) 
    144158 
     159        # The logical operators (including a unary not) can be used to assign 
     160        # to boolean fields 
     161        for expression in ( 
     162            # Check boundaries 
     163            ~(F('num_employees') < 33), 
     164            ~(F('num_employees') <= 32), 
     165            (F('num_employees') > 2299), 
     166            (F('num_employees') >= 2300), 
     167            (F('num_employees') == 2300), 
     168            ((F('num_employees') != 3) & (32 != F('num_employees'))), 
     169            # Inverted argument order works too 
     170            (2299 < F('num_employees')), 
     171            (2300 <= F('num_employees')) 
     172        ): 
     173            company_query.update( 
     174                is_large=expression 
     175            ) 
     176            self.assertQuerysetEqual( 
     177                company_query, [ 
     178                    { 
     179                        'num_chairs': 5294600, 
     180                        'name': u'Example Inc.', 
     181                        'num_employees': 2300, 
     182                        'is_large': True 
     183                    }, 
     184                    { 
     185                        'num_chairs': 15, 
     186                        'name': u'Foobar Ltd.', 
     187                        'num_employees': 3, 
     188                        'is_large': False 
     189                    }, 
     190                    { 
     191                        'num_chairs': 1088, 
     192                        'name': u'Test GmbH', 
     193                        'num_employees': 32, 
     194                        'is_large': False 
     195                    } 
     196                ], 
     197                lambda o: o, 
     198            ) 
     199 
    145200        # The relation of a foreign key can become copied over to an other 
    146201        # foreign key. 
    147202        self.assertEqual( 
  • tests/modeltests/expressions/models.py

     
    2222        Employee, 
    2323        related_name='company_point_of_contact_set', 
    2424        null=True) 
     25    is_large = models.BooleanField( 
     26        blank=True) 
    2527 
    2628    def __unicode__(self): 
    2729        return self.name