Ticket #16211: issue16211+14029-query-expression-extra-operators.patch

File issue16211+14029-query-expression-extra-operators.patch, 10.6 KB (added by wdoekes, 4 years ago)
  • django/db/models/expressions.py

     
    1818    AND = '&'
    1919    OR = '|'
    2020
     21    # Unary operator (needs special attention in combine_expression)
     22    NOT = 'NOT'
     23
     24    # Comparison operators
     25    EQ = '='
     26    GE = '>='
     27    GT = '>'
     28    LE = '<='
     29    LT = '<'
     30    NE = '<>'
     31
    2132    def __init__(self, children=None, connector=None, negated=False):
    2233        if children is not None and len(children) > 1 and connector is None:
    2334            raise TypeError('You have to specify a connector.')
     
    91102    def __ror__(self, other):
    92103        return self._combine(other, self.OR, True)
    93104
     105    def __invert__(self):
     106        obj = ExpressionNode([self], connector=self.NOT, negated=True)
     107        return obj
     108
     109    def __eq__(self, other):
     110        return self._combine(other, self.EQ, False)
     111
     112    def __ge__(self, other):
     113        return self._combine(other, self.GE, False)
     114
     115    def __gt__(self, other):
     116        return self._combine(other, self.GT, False)
     117
     118    def __le__(self, other):
     119        return self._combine(other, self.LE, False)
     120
     121    def __lt__(self, other):
     122        return self._combine(other, self.LT, False)
     123
     124    def __ne__(self, other):
     125        return self._combine(other, self.NE, False)
     126
     127    def __nonzero__(self):
     128        raise TypeError('Boolean operators should be avoided. Use bitwise operators.')
     129
    94130    def prepare_database_save(self, unused):
    95131        return self
    96132
  • 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 comparison operators and the logical unary not can be used to
     160        # assign 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') + 1 != 4) & (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
     200        # The python boolean operators should be avoided as they yield
     201        # unexpected results
     202        test_gmbh = Company.objects.get(name="Test GmbH")
     203        def test():
     204            test_gmbh.is_large = not F('is_large')
     205        self.assertRaises(TypeError, test)
     206        def test():
     207            test_gmbh.is_large = F('is_large') and F('is_large')
     208        self.assertRaises(TypeError, test)
     209        def test():
     210            test_gmbh.is_large = F('is_large') or F('is_large')
     211        self.assertRaises(TypeError, test)
     212
    145213        # The relation of a foreign key can become copied over to an other
    146214        # foreign key.
    147215        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
Back to Top