Commits

Mike Bayer  committed 8b4c41c

Added :meth:`.ColumnOperators.notin_`,
:meth:`.ColumnOperators.notlike`,
:meth:`.ColumnOperators.notilike` to :class:`.ColumnOperators`.
[ticket:2580]

  • Participants
  • Parent commits 9ad8349

Comments (0)

Files changed (4)

File doc/build/changelog/changelog_08.rst

       name was omitted which apparently creates the
       index in the default schema, rather than that
       of the table.
+
+    .. change::
+        :tags: sql, feature
+        :tickets: 2580
+
+        Added :meth:`.ColumnOperators.notin_`,
+        :meth:`.ColumnOperators.notlike`,
+        :meth:`.ColumnOperators.notilike` to :class:`.ColumnOperators`.
+

File lib/sqlalchemy/sql/expression.py

         "eq": (_boolean_compare, operators.ne),
         "like_op": (_boolean_compare, operators.notlike_op),
         "ilike_op": (_boolean_compare, operators.notilike_op),
+        "notlike_op": (_boolean_compare, operators.like_op),
+        "notilike_op": (_boolean_compare, operators.ilike_op),
         "contains_op": (_boolean_compare, operators.notcontains_op),
         "startswith_op": (_boolean_compare, operators.notstartswith_op),
         "endswith_op": (_boolean_compare, operators.notendswith_op),
         "nullsfirst_op": (_scalar, nullsfirst),
         "nullslast_op": (_scalar, nullslast),
         "in_op": (_in_impl, operators.notin_op),
+        "notin_op": (_in_impl, operators.in_op),
         "is_": (_boolean_compare, operators.is_),
         "isnot": (_boolean_compare, operators.isnot),
         "collate": (_collate_impl,),

File lib/sqlalchemy/sql/operators.py

         """
         return self.operate(in_op, other)
 
+    def notin_(self, other):
+        """implement the ``NOT IN`` operator.
+
+        This is equivalent to using negation with :meth:`.ColumnOperators.in_`,
+        i.e. ``~x.in_(y)``.
+
+        .. versionadded:: 0.8
+
+        .. seealso::
+
+            :meth:`.ColumnOperators.in_`
+
+        """
+        return self.operate(notin_op, other)
+
+    def notlike(self, other, escape=None):
+        """implement the ``NOT LIKE`` operator.
+
+        This is equivalent to using negation with :meth:`.ColumnOperators.like`,
+        i.e. ``~x.like(y)``.
+
+        .. versionadded:: 0.8
+
+        .. seealso::
+
+            :meth:`.ColumnOperators.like`
+
+        """
+        return self.operate(notlike_op, other, escape=escape)
+
+    def notilike(self, other, escape=None):
+        """implement the ``NOT ILIKE`` operator.
+
+        This is equivalent to using negation with :meth:`.ColumnOperators.ilike`,
+        i.e. ``~x.ilike(y)``.
+
+        .. versionadded:: 0.8
+
+        .. seealso::
+
+            :meth:`.ColumnOperators.ilike`
+
+        """
+        return self.operate(notilike_op, other, escape=escape)
+
     def is_(self, other):
         """Implement the ``IS`` operator.
 
     return a.like(b, escape=escape)
 
 def notlike_op(a, b, escape=None):
-    return ~a.like(b, escape=escape)
+    return a.notlike(b, escape=escape)
 
 def ilike_op(a, b, escape=None):
     return a.ilike(b, escape=escape)
 
 def notilike_op(a, b, escape=None):
-    return ~a.ilike(b, escape=escape)
+    return a.notilike(b, escape=escape)
 
 def between_op(a, b, c):
     return a.between(b, c)
     return a.in_(b)
 
 def notin_op(a, b):
-    return ~a.in_(b)
+    return a.notin_(b)
 
 def distinct_op(a):
     return a.distinct()

File test/sql/test_operators.py

-from sqlalchemy.testing import fixtures, eq_
+from sqlalchemy.testing import fixtures, eq_, is_
 from sqlalchemy import testing
 from sqlalchemy.testing import assert_raises_message
 from sqlalchemy.sql import column, desc, asc, literal, collate
 
 from sqlalchemy import text, literal_column
 
+class LoopOperate(operators.ColumnOperators):
+    def operate(self, op, *other, **kwargs):
+        return op
+
 class DefaultColumnComparatorTest(fixtures.TestBase):
 
     def _do_scalar_test(self, operator, compare_to):
         assert left.comparator.operate(operator).compare(
             compare_to(left)
         )
+        self._loop_test(operator)
 
     def _do_operate_test(self, operator, right=column('right')):
         left = column('left')
             BinaryExpression(left, right, operator)
         )
 
+        self._loop_test(operator, right)
+
+    def _loop_test(self, operator, *arg):
+        l = LoopOperate()
+        is_(
+            operator(l, *arg),
+            operator
+        )
+
     def test_desc(self):
         self._do_scalar_test(operators.desc_op, desc)
 
     def test_isnot_null(self):
         self._do_operate_test(operators.isnot, None)
 
+    def test_like(self):
+        self._do_operate_test(operators.like_op)
+
+    def test_notlike(self):
+        self._do_operate_test(operators.notlike_op)
+
+    def test_ilike(self):
+        self._do_operate_test(operators.ilike_op)
+
+    def test_notilike(self):
+        self._do_operate_test(operators.notilike_op)
+
     def test_is(self):
         self._do_operate_test(operators.is_)
 
                     operators.in_op
                 )
             )
+        self._loop_test(operators.in_op, [1, 2, 3])
+
+    def test_notin(self):
+        left = column('left')
+        assert left.comparator.operate(operators.notin_op, [1, 2, 3]).compare(
+                BinaryExpression(
+                    left,
+                    Grouping(ClauseList(
+                        literal(1), literal(2), literal(3)
+                    )),
+                    operators.notin_op
+                )
+            )
+        self._loop_test(operators.notin_op, [1, 2, 3])
 
     def test_collate(self):
         left = column('left')