Added CaseInsensitiveComparator + Email type

This commit is contained in:
Konsta Vesterinen
2013-04-11 16:58:20 +03:00
parent f786ca040c
commit 7bc28ce085
8 changed files with 157 additions and 1 deletions

View File

@@ -4,6 +4,13 @@ Changelog
Here you can see the full list of changes between each SQLAlchemy-Utils release.
0.9.0 (2013-04-11)
^^^^^^^^^^^^^^^^^^
- Added CaseInsensitiveComparator
- Added Email type
0.8.4 (2013-04-08)
^^^^^^^^^^^^^^^^^^

View File

@@ -24,7 +24,7 @@ class PyTest(Command):
setup(
name='SQLAlchemy-Utils',
version='0.8.4',
version='0.9',
url='https://github.com/kvesteri/sqlalchemy-utils',
license='BSD',
author='Konsta Vesterinen',

View File

@@ -1,6 +1,7 @@
from .functions import sort_query, defer_except, escape_like
from .merge import merge, Merger
from .types import (
Email,
instrumented_list,
InstrumentedList,
PhoneNumber,
@@ -20,6 +21,7 @@ __all__ = (
escape_like,
instrumented_list,
merge,
Email,
InstrumentedList,
Merger,
NumberRange,

View File

@@ -0,0 +1,50 @@
import sqlalchemy as sa
class CaseInsensitiveComparator(sa.Unicode.Comparator):
@classmethod
def lowercase_arg(cls, func):
def operation(self, other, **kwargs):
if other is None:
return getattr(sa.Unicode.Comparator, func)(
self, other, **kwargs
)
return getattr(sa.Unicode.Comparator, func)(
self, sa.func.lower(other), **kwargs
)
return operation
def in_(self, other):
if isinstance(other, list) or isinstance(other, tuple):
other = map(sa.func.lower, other)
return sa.Unicode.Comparator.in_(self, other)
def notin_(self, other):
if isinstance(other, list) or isinstance(other, tuple):
other = map(sa.func.lower, other)
return sa.Unicode.Comparator.notin_(self, other)
string_operator_funcs = [
'__eq__',
'__ne__',
'__lt__',
'__le__',
'__gt__',
'__ge__',
'concat',
'contains',
'ilike',
'like',
'notlike',
'notilike',
'startswith',
'endswith',
]
for func in string_operator_funcs:
setattr(
CaseInsensitiveComparator,
func,
CaseInsensitiveComparator.lowercase_arg(func)
)

View File

@@ -3,6 +3,7 @@ from functools import wraps
import sqlalchemy as sa
from sqlalchemy.orm.collections import InstrumentedList as _InstrumentedList
from sqlalchemy import types
from .operators import CaseInsensitiveComparator
class PhoneNumber(phonenumbers.phonenumber.PhoneNumber):
@@ -118,6 +119,16 @@ class ScalarList(types.TypeDecorator):
)
class Email(sa.types.TypeDecorator):
impl = sa.Unicode(255)
comparator_factory = CaseInsensitiveComparator
def process_bind_param(self, value, dialect):
if value is not None:
return value.lower()
return value
class NumberRangeRawType(types.UserDefinedType):
"""
Raw number range type, only supports PostgreSQL for now.

View File

@@ -0,0 +1,44 @@
import sqlalchemy as sa
from sqlalchemy_utils import Email
from tests import DatabaseTestCase
class TestCaseInsensitiveComparator(DatabaseTestCase):
def create_models(self):
class User(self.Base):
__tablename__ = 'user'
id = sa.Column(sa.Integer, primary_key=True)
email = sa.Column(Email)
def __repr__(self):
return 'Building(%r)' % self.id
self.User = User
def test_supports_equals(self):
query = (
self.session.query(self.User)
.filter(self.User.email == u'email@example.com')
)
assert '"user".email = lower(:lower_1)' in str(query)
def test_supports_in_(self):
query = (
self.session.query(self.User)
.filter(self.User.email.in_([u'email@example.com', u'a']))
)
assert (
'"user".email IN (lower(:lower_1), lower(:lower_2))'
in str(query)
)
def test_supports_notin_(self):
query = (
self.session.query(self.User)
.filter(self.User.email.notin_([u'email@example.com', u'a']))
)
assert (
'"user".email NOT IN (lower(:lower_1), lower(:lower_2))'
in str(query)
)

27
tests/test_email.py Normal file
View File

@@ -0,0 +1,27 @@
import sqlalchemy as sa
from sqlalchemy_utils import Email
from tests import DatabaseTestCase
class TestEmailType(DatabaseTestCase):
def create_models(self):
class User(self.Base):
__tablename__ = 'user'
id = sa.Column(sa.Integer, primary_key=True)
email = sa.Column(Email)
def __repr__(self):
return 'Building(%r)' % self.id
self.User = User
def test_saves_email_as_lowercased(self):
user = self.User(
email=u'Someone@example.com'
)
self.session.add(user)
self.session.commit()
user = self.session.query(self.User).first()
assert user.email == u'someone@example.com'

View File

@@ -28,6 +28,21 @@ class TestNumberRangeType(DatabaseTestCase):
assert building.persons_at_night.min_value == 1
assert building.persons_at_night.max_value == 3
def test_nullify_number_range(self):
building = self.Building(
persons_at_night=NumberRange(1, 3)
)
self.session.add(building)
self.session.commit()
building = self.session.query(self.Building).first()
building.persons_at_night = None
self.session.commit()
building = self.session.query(self.Building).first()
assert building.persons_at_night is None
class TestNumberRange(object):
def test_equality_operator(self):