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

File issue16211+14029-query-expression-extra-operators2.patch, 11.4 KB (added by wdoekes, 3 years ago)

clarify test and add partial docs

  • docs/topics/db/queries.txt

     
    992992    # THIS WILL RAISE A FieldError
    993993    >>> Entry.objects.update(headline=F('blog__name'))
    994994
     995.. versionadded:: 1.4
     996
     997Django also supports the comparison operators ``==``, ``!=``, ``<=``, ``<``,
     998``>``, ``>=`` and the bitwise negation operator ``~``::
     999
     1000    >>> Entry.objects.update(is_heavily_quoted=~(F('n_pingbacks') < 100))
     1001
    9951002.. _topics-db-queries-related:
    9961003
    9971004Related objects
  • 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

     
    863863        can vary between backends (e.g., Oracle with %% and &) and between
    864864        subexpression types (e.g., date expressions)
    865865        """
     866        if connector == 'NOT':
     867            assert len(sub_expressions) == 1
     868            return 'NOT (%s)' % sub_expressions[0]
    866869        conn = ' %s ' % connector
    867870        return conn.join(sub_expressions)
    868871
  • 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

     
    1010class ExpressionsTests(TestCase):
    1111    def test_filter(self):
    1212        Company.objects.create(
    13             name="Example Inc.", num_employees=2300, num_chairs=5,
     13            name="Example Inc.", num_employees=2300, num_chairs=5, is_large=False,
    1414            ceo=Employee.objects.create(firstname="Joe", lastname="Smith")
    1515        )
    1616        Company.objects.create(
    17             name="Foobar Ltd.", num_employees=3, num_chairs=4,
     17            name="Foobar Ltd.", num_employees=3, num_chairs=4, is_large=False,
    1818            ceo=Employee.objects.create(firstname="Frank", lastname="Meyer")
    1919        )
    2020        Company.objects.create(
    21             name="Test GmbH", num_employees=32, num_chairs=1,
     21            name="Test GmbH", num_employees=32, num_chairs=1, is_large=False,
    2222            ceo=Employee.objects.create(firstname="Max", lastname="Mustermann")
    2323        )
    2424
    2525        company_query = Company.objects.values(
    26             "name", "num_employees", "num_chairs"
     26            "name", "num_employees", "num_chairs", "is_large"
    2727        ).order_by(
    28             "name", "num_employees", "num_chairs"
     28            "name", "num_employees", "num_chairs", "is_large"
    2929        )
    3030
    3131        # We can filter for companies where the number of employees is greater
     
    3636                    "num_chairs": 5,
    3737                    "name": "Example Inc.",
    3838                    "num_employees": 2300,
     39                    "is_large": False
    3940                },
    4041                {
    4142                    "num_chairs": 1,
    4243                    "name": "Test GmbH",
    43                     "num_employees": 32
     44                    "num_employees": 32,
     45                    "is_large": False
    4446                },
    4547            ],
    4648            lambda o: o
     
    5456                {
    5557                    "num_chairs": 2300,
    5658                    "name": "Example Inc.",
    57                     "num_employees": 2300
     59                    "num_employees": 2300,
     60                    "is_large": False
    5861                },
    5962                {
    6063                    "num_chairs": 3,
    6164                    "name": "Foobar Ltd.",
    62                     "num_employees": 3
     65                    "num_employees": 3,
     66                    "is_large": False
    6367                },
    6468                {
    6569                    "num_chairs": 32,
    6670                    "name": "Test GmbH",
    67                     "num_employees": 32
     71                    "num_employees": 32,
     72                    "is_large": False
    6873                }
    6974            ],
    7075            lambda o: o
     
    7883                {
    7984                    'num_chairs': 2302,
    8085                    'name': u'Example Inc.',
    81                     'num_employees': 2300
     86                    'num_employees': 2300,
     87                    'is_large': False
    8288                },
    8389                {
    8490                    'num_chairs': 5,
    8591                    'name': u'Foobar Ltd.',
    86                     'num_employees': 3
     92                    'num_employees': 3,
     93                    'is_large': False
    8794                },
    8895                {
    8996                    'num_chairs': 34,
    9097                    'name': u'Test GmbH',
    91                     'num_employees': 32
     98                    'num_employees': 32,
     99                    'is_large': False
    92100                }
    93101            ],
    94102            lambda o: o,
     
    103111                {
    104112                    'num_chairs': 6900,
    105113                    'name': u'Example Inc.',
    106                     'num_employees': 2300
     114                    'num_employees': 2300,
     115                    'is_large': False
    107116                },
    108117                {
    109118                    'num_chairs': 9,
    110119                    'name': u'Foobar Ltd.',
    111                     'num_employees': 3
     120                    'num_employees': 3,
     121                    'is_large': False
    112122                },
    113123                {
    114124                    'num_chairs': 96,
    115125                    'name': u'Test GmbH',
    116                     'num_employees': 32
     126                    'num_employees': 32,
     127                    'is_large': False
    117128                }
    118129            ],
    119130            lambda o: o,
     
    128139                {
    129140                    'num_chairs': 5294600,
    130141                    'name': u'Example Inc.',
    131                     'num_employees': 2300
     142                    'num_employees': 2300,
     143                    'is_large': False
    132144                },
    133145                {
    134146                    'num_chairs': 15,
    135147                    'name': u'Foobar Ltd.',
    136                     'num_employees': 3
     148                    'num_employees': 3,
     149                    'is_large': False
    137150                },
    138151                {
    139152                    'num_chairs': 1088,
    140153                    'name': u'Test GmbH',
    141                     'num_employees': 32
     154                    'num_employees': 32,
     155                    'is_large': False
    142156                }
    143157            ],
    144158            lambda o: o,
    145159        )
    146160
     161        # The comparison operators and the bitwise unary not can be used
     162        # to assign to boolean fields
     163        for expression in (
     164            # Check boundaries
     165            ~(F('num_employees') < 33),
     166            ~(F('num_employees') <= 32),
     167            (F('num_employees') > 2299),
     168            (F('num_employees') >= 2300),
     169            (F('num_employees') == 2300),
     170            ((F('num_employees') + 1 != 4) & (32 != F('num_employees'))),
     171            # Inverted argument order works too
     172            (2299 < F('num_employees')),
     173            (2300 <= F('num_employees'))
     174        ):
     175            # Test update by F-expression
     176            company_query.update(
     177                is_large=expression
     178            )
     179            # Compare results
     180            self.assertQuerysetEqual(
     181                company_query, [
     182                    {
     183                        'num_chairs': 5294600,
     184                        'name': u'Example Inc.',
     185                        'num_employees': 2300,
     186                        'is_large': True
     187                    },
     188                    {
     189                        'num_chairs': 15,
     190                        'name': u'Foobar Ltd.',
     191                        'num_employees': 3,
     192                        'is_large': False
     193                    },
     194                    {
     195                        'num_chairs': 1088,
     196                        'name': u'Test GmbH',
     197                        'num_employees': 32,
     198                        'is_large': False
     199                    }
     200                ],
     201                lambda o: o,
     202            )
     203            # Reset values
     204            company_query.update(
     205                is_large=False
     206            )
     207
     208        # The python boolean operators should be avoided as they yield
     209        # unexpected results
     210        test_gmbh = Company.objects.get(name="Test GmbH")
     211        def test():
     212            test_gmbh.is_large = not F('is_large')
     213        self.assertRaises(TypeError, test)
     214        def test():
     215            test_gmbh.is_large = F('is_large') and F('is_large')
     216        self.assertRaises(TypeError, test)
     217        def test():
     218            test_gmbh.is_large = F('is_large') or F('is_large')
     219        self.assertRaises(TypeError, test)
     220
    147221        # The relation of a foreign key can become copied over to an other
    148222        # foreign key.
    149223        self.assertEqual(
  • tests/modeltests/expressions/models.py

     
    2323        Employee,
    2424        related_name='company_point_of_contact_set',
    2525        null=True)
     26    is_large = models.BooleanField(
     27        blank=True)
    2628
    2729    def __unicode__(self):
    2830        return self.name
Back to Top