def test_joint_conditions(self): """ Test that joint conditions must be on the same aliased instance. In particular P(rel__attr1=val1, rel__attr2=val2) produces only a single join, so the two conditions must be jointly satisfied on the same database row. """ self._assert_P_matches_Q( self.test_obj, p=P(m2ms__int_value=10, m2ms__char_value='bar'), q=Q(m2ms__int_value=10, m2ms__char_value='bar'), ) self._assert_P_matches_Q( self.test_obj, p=P(m2ms__int_value=10, m2ms__char_value='foo'), q=Q(m2ms__int_value=10, m2ms__char_value='foo'), ) self._assert_P_matches_Q( self.test_obj, p=P(m2ms__int_value=10) & OrmP(m2ms__char_value='bar'), q=Q(m2ms__int_value=10) & Q(m2ms__char_value='bar'), ) self._assert_P_matches_Q( self.test_obj, p=P(m2ms__int_value=10) & OrmP(m2ms__char_value='foo'), q=Q(m2ms__int_value=10) & Q(m2ms__char_value='foo'), )
def test_follow_relationship(self): p1 = P(parent__int_value__gt=10) obj = TestObj.objects.filter(parent__int_value__gt=10)[0] self.assertTrue(p1.eval(obj)) p2 = P(parent__parent__int_value__gt=10) obj = TestObj.objects.filter(parent__parent__int_value__gt=10)[0] self.assertTrue(p2.eval(obj))
def test_and(self): p1 = P(char_value__contains='hello') p2 = P(int_value=50) p3 = P(int_value__lt=20) pand1 = p1 & p2 pand2 = p2 & p3 self.assertTrue(pand1.eval(self.testobj)) self.assertFalse(pand2.eval(self.testobj))
def test_or(self): p1 = P(char_value__contains='hello', int_value=50) p2 = P(int_value__gt=80) p3 = P(int_value__lt=20) por1 = p1 | p2 por2 = p2 | p3 self.assertTrue(por1.eval(self.testobj)) self.assertFalse(por2.eval(self.testobj))
def test_or2(self): p1 = P(foo=True) p2 = P(bar=False) d = {'foo': True, 'bar': True} self.assertIn(d, p1) self.assertNotIn(d, p2) self.assertIn(d, (p1 | p1)) self.assertIn(d, (p1 | p2)) self.assertIn(d, (p1 | p2)) self.assertIn(d, (p2 | p1)) self.assertNotIn(d, (p2 | p2)) self.assertNotIn(d, (p2 | P(foo=False))) self.assertNotIn(d, (P(foo=False) | p2))
def test_patch_with_orm_eval(self): """ Tests that the debug patch_with_orm_eval() context works as expected. """ with mock.patch('predicate.debug.original_eval', return_value=False) as patched: self.assertIn(self.test_obj, P(int_value=10)) self.assertFalse(patched.called) with patch_with_orm_eval(): with self.assertRaises(AssertionError): self.test_obj in P(int_value=10) self.assertTrue(patched.called) with patch_with_orm_eval(): self.assertIn(self.test_obj, P(int_value=10))
def test_debug_orm_validations(self): self.assertIn(self.test_obj, P(int_value=10)) with mock.patch('predicate.debug.original_eval', return_value=False) as patched: with self.assertRaises(AssertionError): self.test_obj in OrmP(int_value=10) self.assertTrue(patched.called) with mock.patch('predicate.debug.original_eval', return_value=True) as patched: self.assertIn(self.test_obj, OrmP(int_value=10)) self.assertTrue(patched.called)
def test_dates(self): today = date.today() self.assertTrue(P(date_value__year=today.year).eval(self.testobj)) self.assertTrue(P(date_value__month=today.month).eval(self.testobj)) self.assertTrue(P(date_value__day=today.day).eval(self.testobj)) self.assertTrue( P(date_value__week_day=today.weekday()).eval(self.testobj)) self.assertFalse(P(date_value__year=today.year + 1).eval(self.testobj)) self.assertFalse( P(date_value__month=today.month + 1).eval(self.testobj)) self.assertFalse(P(date_value__day=today.day + 1).eval(self.testobj)) self.assertFalse( P(date_value__week_day=today.weekday() + 1).eval(self.testobj))
def set_similar(self, my_obj=None): dlog("settings similar") if my_obj is None: my_obj = ColorChoice.objects.get(id=self.colorid) my_name = 'similar{}'.format(self.colorid) similar_color = P(hue__range=(max(0, my_obj.hue - 25), min(365, my_obj.hue + 25)), saturation__range=(max(0, my_obj.saturation - 20), min(100, my_obj.saturation + 20)), brightness__range=(max(0, my_obj.brightness - 20), min(100, my_obj.brightness + 20))) dlog(similar_color.children) collections[my_name] = Collection(my_name, similar_color) return my_name
def test_law_of_excluded_middle(self): """ Django appears to obey the law of excluded middle p|~p is always satisfied. However, it's _not_ the case that p OR ~p is always satisfied, so the implementation in P does not match django's in some edge cases. """ p = P(m2ms__int_value=10, m2ms__char_value='bar') q = Q(m2ms__int_value=10, m2ms__char_value='bar') self._assert_P_matches_Q(self.test_obj, p=p, q=q) self.assertNotIn(self.test_obj, p) self.assertNotIn(self.test_obj, ~p) # Since evaluation of the disjunction is just logical OR of the # disjuncts, since the self.assertNotIn(self.test_obj, p | ~p) self._assert_P_matches_Q(self.test_obj, p=p, q=q) self._assert_P_matches_Q(self.test_obj, p=(p | ~p), q=(q | ~q))
def test_dates(self): self.assertTrue( OrmP(date_value__year=self.date_obj.year).eval(self.testobj)) self.assertTrue( OrmP(date_value__month=self.date_obj.month).eval(self.testobj)) self.assertTrue( OrmP(date_value__day=self.date_obj.day).eval(self.testobj)) orm_week_day = self.date_obj.isoweekday() % 7 + 1 self.assertTrue( OrmP(date_value__week_day=orm_week_day).eval(self.testobj)) self.assertFalse( OrmP(date_value__year=self.date_obj.year + 1).eval(self.testobj)) self.assertFalse( OrmP(date_value__month=self.date_obj.month + 1).eval(self.testobj)) self.assertFalse( OrmP(date_value__day=self.date_obj.day + 1).eval(self.testobj)) self.assertFalse( P(date_value__week_day=orm_week_day + 1).eval(self.testobj))
def test_negation_joint_conditions(self): self._assert_P_matches_Q( self.test_obj, p=~P(m2ms__int_value=10, m2ms__char_value='bar'), q=~Q(m2ms__int_value=10, m2ms__char_value='bar'), ) self.assertIn( self.test_obj, TestObj.objects.filter( ~~Q(m2ms__int_value=10, m2ms__char_value='bar'))) self.assertIn(self.test_obj, ~~P(m2ms__int_value=10, m2ms__char_value='bar')) self._assert_P_matches_Q( self.test_obj, p=~~P(m2ms__int_value=10, m2ms__char_value='bar'), q=~~Q(m2ms__int_value=10, m2ms__char_value='bar'), ) self.assertNotIn( self.test_obj, TestObj.objects.filter( ~~~Q(m2ms__int_value=10, m2ms__char_value='bar'))) self.assertNotIn(self.test_obj, ~~~P(m2ms__int_value=10, m2ms__char_value='bar')) self._assert_P_matches_Q( self.test_obj, p=~~~P(m2ms__int_value=10, m2ms__char_value='bar'), q=~~~Q(m2ms__int_value=10, m2ms__char_value='bar'), ) self.assertIn(self.test_obj, ~~P(m2ms__int_value=10, m2ms__char_value='foo')) self._assert_P_matches_Q( self.test_obj, q=~~Q(m2ms__int_value=10, m2ms__char_value='foo'), p=~~P(m2ms__int_value=10, m2ms__char_value='foo'), )
def test_icontains(self): self.assertTrue(P(char_value__icontains='heLLo').eval(self.testobj))
def test_contains(self): self.assertTrue(P(char_value__contains='hello').eval(self.testobj)) self.assertFalse(P(char_value__contains='foobar').eval(self.testobj))
def test_iexact(self): self.assertTrue(P(char_value__iexact='heLLo World').eval(self.testobj)) self.assertFalse(P(char_value__iexact='hello worl').eval(self.testobj))
def test_de_morgan_law(self): """ Tests De Morgan's law as it relates to the Django ORM. Surprisingly, the ORM does _not_ obey De Morgan's law in some cases, which this test manifests. See also: https://github.com/django/django/pull/6005#issuecomment-184016682 since Django may start to obey De Morgan's law in Django >= 1.10. """ expr = P(m2ms__int_value=10) & OrmP(m2ms__char_value='bar') transformed_expr = ~(~P(m2ms__int_value=10) | ~P(m2ms__char_value='bar')) # By De Morgan's law, these expressions are equivalent: # (A ∧ B) ⇔ ¬(¬A ∨ ¬B) # Translated into the ORM, and different queries are constructed for # expr and transformed_expr. # # The original expression compiles to the following SQL: # # SELECT "testapp_testobj"."id" # FROM "testapp_testobj" # INNER JOIN "testapp_testobj_m2ms" # ON ("testapp_testobj"."id" = "testapp_testobj_m2ms"."testobj_id") # INNER JOIN "testapp_m2mmodel" # ON ("testapp_testobj_m2ms"."m2mmodel_id" = "testapp_m2mmodel"."id") # WHERE ("testapp_m2mmodel"."int_value" = 10 # AND "testapp_m2mmodel"."char_value" = bar) # # The transformed version compiles to the following SQL, which is _not_ # equivalent: # # SELECT "testapp_testobj"."id" # FROM "testapp_testobj" # WHERE NOT ((NOT ("testapp_testobj"."id" IN # (SELECT U1."testobj_id" AS Col1 # FROM "testapp_testobj_m2ms" U1 # INNER JOIN "testapp_m2mmodel" U2 # ON (U1."m2mmodel_id" = U2."id") # WHERE U2."int_value" = 10)) # OR NOT ("testapp_testobj"."id" IN # (SELECT U1."testobj_id" AS Col1 # FROM "testapp_testobj_m2ms" U1 # INNER JOIN "testapp_m2mmodel" U2 # ON (U1."m2mmodel_id" = U2."id") # WHERE U2."char_value" = bar)))) # First show that these are not equivalent in the ORM, to verify that this # is not a bug in the predicate.P implementation. self.assertNotIn( self.test_obj, TestObj.objects.filter( Q(m2ms__int_value=10) & Q(m2ms__char_value='bar'))) self.assertNotIn(self.test_obj, TestObj.objects.filter(expr)) self.assertIn( self.test_obj, TestObj.objects.filter(~(~Q(m2ms__int_value=10) | ~Q(m2ms__char_value='bar')))) self.assertIn(self.test_obj, TestObj.objects.filter(transformed_expr)) self.assertNotIn(self.test_obj, expr) self.assertIn(self.test_obj, transformed_expr) # Negate the queries, but weirdly, that doeesn't cause the answer to change. self._assert_P_matches_Q( self.test_obj, ~expr, ~(Q(m2ms__int_value=10) & Q(m2ms__char_value='bar')), ) self.assertNotIn(self.test_obj, TestObj.objects.filter(~expr)) # Test double negation still returns us to where we were originally. self._assert_P_matches_Q( self.test_obj, ~~expr, ~~(Q(m2ms__int_value=10) & Q(m2ms__char_value='bar'))), self.assertIn(self.test_obj, TestObj.objects.filter(~~expr))
def test_in_operator(self): p = P(int_value__lte=50) p2 = P(int_value__lt=10) self.assertTrue(self.testobj in p) self.assertFalse(self.testobj in p2)
def test_iregex(self): self.assertTrue(P(char_value__iregex='Hel*o').eval(self.testobj))
def test_istartswith(self): self.assertTrue(P(char_value__istartswith='heLLo').eval(self.testobj)) self.assertFalse(P(char_value__startswith='world').eval(self.testobj))
def test_lt(self): self.assertFalse(P(int_value__lt=20).eval(self.testobj)) self.assertTrue(P(int_value__lt=80).eval(self.testobj)) self.assertFalse(P(int_value__lt=20.0).eval(self.testobj)) self.assertTrue(P(int_value__lt=80.0).eval(self.testobj)) self.assertFalse(P(int_value__lt=50).eval(self.testobj))
def test_iendswith(self): self.assertFalse(P(char_value__iendswith='hello').eval(self.testobj)) self.assertTrue(P(char_value__iendswith='World').eval(self.testobj))
def test_lte(self): self.assertFalse(P(int_value__lte=20).eval(self.testobj)) self.assertTrue(P(int_value__lte=50).eval(self.testobj))
def test_null(self): self.assertTrue(P(parent__isnull=True).eval(self.testobj)) self.assertFalse(P(parent__isnull=False).eval(self.testobj))
def test_regex(self): self.assertTrue(P(char_value__regex='hel*o').eval(self.testobj)) self.assertFalse(P(char_value__regex='Hel*o').eval(self.testobj))
post_delete.connect(delete_color, ColorChoice) # This is a global collections data structure accessed by all connected sockets # it intially holds any commonly defined collections, but clients can add their # own collections to wire into the signal-collection dispatching. If there were # to be many user registered collections, the signal should be converted to a # queued task instead of in the req/res cycle. collections = {} try: # We do this here in the try because we can't # access these models on startup if we are running in the syncdb # code. Could import this file in other-than-models.py to avoid that blue_colors = P( hue__range=(171, 264), saturation__range=(30,100), brightness__range=(30, 100) ) django1 = P( hue__range=(145, 169), saturation__range=(30, 89), brightness__range=(10, 40) ) django2 = P( hue__range=(75, 105), saturation__range=(65,99), brightness__range=(25,90) )