Add support for PhoneNumber composites
This commit is contained in:
@@ -4,6 +4,12 @@ Changelog
|
|||||||
Here you can see the full list of changes between each SQLAlchemy-Utils release.
|
Here you can see the full list of changes between each SQLAlchemy-Utils release.
|
||||||
|
|
||||||
|
|
||||||
|
0.32.3 (2016-04-20)
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- Added support for PhoneNumber objects as composites
|
||||||
|
|
||||||
|
|
||||||
0.32.2 (2016-04-20)
|
0.32.2 (2016-04-20)
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@@ -108,6 +108,8 @@ PhoneNumberType
|
|||||||
|
|
||||||
.. module:: sqlalchemy_utils.types.phone_number
|
.. module:: sqlalchemy_utils.types.phone_number
|
||||||
|
|
||||||
|
.. autoclass:: PhoneNumber
|
||||||
|
|
||||||
.. autoclass:: PhoneNumberType
|
.. autoclass:: PhoneNumberType
|
||||||
|
|
||||||
|
|
||||||
|
@@ -93,4 +93,4 @@ from .types import ( # noqa
|
|||||||
WeekDaysType
|
WeekDaysType
|
||||||
)
|
)
|
||||||
|
|
||||||
__version__ = '0.32.2'
|
__version__ = '0.32.3'
|
||||||
|
@@ -25,18 +25,46 @@ class PhoneNumber(BasePhoneNumber):
|
|||||||
.. _Python phonenumbers library:
|
.. _Python phonenumbers library:
|
||||||
https://github.com/daviddrysdale/python-phonenumbers
|
https://github.com/daviddrysdale/python-phonenumbers
|
||||||
|
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from sqlalchemy_utils import PhoneNumber
|
||||||
|
|
||||||
|
|
||||||
|
class User(self.Base):
|
||||||
|
__tablename__ = 'user'
|
||||||
|
id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
|
||||||
|
name = sa.Column(sa.Unicode(255))
|
||||||
|
_phone_number = sa.Column(sa.Unicode(20))
|
||||||
|
country_code = sa.Column(sa.Unicode(8))
|
||||||
|
|
||||||
|
phonenumber = sa.orm.composite(
|
||||||
|
PhoneNumber,
|
||||||
|
_phone_number,
|
||||||
|
country_code
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
user = User(phone_number=PhoneNumber('0401234567', 'FI'))
|
||||||
|
|
||||||
|
user.phone_number.e164 # u'+358401234567'
|
||||||
|
user.phone_number.international # u'+358 40 1234567'
|
||||||
|
user.phone_number.national # u'040 1234567'
|
||||||
|
user.country_code # 'FI'
|
||||||
|
|
||||||
|
|
||||||
:param raw_number:
|
:param raw_number:
|
||||||
String representation of the phone number.
|
String representation of the phone number.
|
||||||
:param country_code:
|
:param region:
|
||||||
Country code of the phone number.
|
Region of the phone number.
|
||||||
'''
|
'''
|
||||||
def __init__(self, raw_number, country_code=None):
|
def __init__(self, raw_number, region=None):
|
||||||
# Bail if phonenumbers is not found.
|
# Bail if phonenumbers is not found.
|
||||||
if phonenumbers is None:
|
if phonenumbers is None:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"'phonenumbers' is required to use 'PhoneNumber'")
|
"'phonenumbers' is required to use 'PhoneNumber'")
|
||||||
|
|
||||||
self._phone_number = phonenumbers.parse(raw_number, country_code)
|
self._phone_number = phonenumbers.parse(raw_number, region)
|
||||||
super(PhoneNumber, self).__init__(
|
super(PhoneNumber, self).__init__(
|
||||||
country_code=self._phone_number.country_code,
|
country_code=self._phone_number.country_code,
|
||||||
national_number=self._phone_number.national_number,
|
national_number=self._phone_number.national_number,
|
||||||
@@ -48,6 +76,7 @@ class PhoneNumber(BasePhoneNumber):
|
|||||||
self._phone_number.preferred_domestic_carrier_code
|
self._phone_number.preferred_domestic_carrier_code
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.region = region
|
||||||
self.national = phonenumbers.format_number(
|
self.national = phonenumbers.format_number(
|
||||||
self._phone_number,
|
self._phone_number,
|
||||||
phonenumbers.PhoneNumberFormat.NATIONAL
|
phonenumbers.PhoneNumberFormat.NATIONAL
|
||||||
@@ -61,6 +90,9 @@ class PhoneNumber(BasePhoneNumber):
|
|||||||
phonenumbers.PhoneNumberFormat.E164
|
phonenumbers.PhoneNumberFormat.E164
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __composite_values__(self):
|
||||||
|
return self.national, self.region
|
||||||
|
|
||||||
def is_valid_number(self):
|
def is_valid_number(self):
|
||||||
return phonenumbers.is_valid_number(self._phone_number)
|
return phonenumbers.is_valid_number(self._phone_number)
|
||||||
|
|
||||||
@@ -96,20 +128,20 @@ class PhoneNumberType(types.TypeDecorator, ScalarCoercible):
|
|||||||
def python_type(self, text):
|
def python_type(self, text):
|
||||||
return self._coerce(text)
|
return self._coerce(text)
|
||||||
|
|
||||||
def __init__(self, country_code='US', max_length=20, *args, **kwargs):
|
def __init__(self, region='US', max_length=20, *args, **kwargs):
|
||||||
# Bail if phonenumbers is not found.
|
# Bail if phonenumbers is not found.
|
||||||
if phonenumbers is None:
|
if phonenumbers is None:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"'phonenumbers' is required to use 'PhoneNumberType'")
|
"'phonenumbers' is required to use 'PhoneNumberType'")
|
||||||
|
|
||||||
super(PhoneNumberType, self).__init__(*args, **kwargs)
|
super(PhoneNumberType, self).__init__(*args, **kwargs)
|
||||||
self.country_code = country_code
|
self.region = region
|
||||||
self.impl = types.Unicode(max_length)
|
self.impl = types.Unicode(max_length)
|
||||||
|
|
||||||
def process_bind_param(self, value, dialect):
|
def process_bind_param(self, value, dialect):
|
||||||
if value:
|
if value:
|
||||||
if not isinstance(value, PhoneNumber):
|
if not isinstance(value, PhoneNumber):
|
||||||
value = PhoneNumber(value, country_code=self.country_code)
|
value = PhoneNumber(value, region=self.region)
|
||||||
|
|
||||||
if self.STORE_FORMAT == 'e164' and value.extension:
|
if self.STORE_FORMAT == 'e164' and value.extension:
|
||||||
return '%s;ext=%s' % (value.e164, value.extension)
|
return '%s;ext=%s' % (value.e164, value.extension)
|
||||||
@@ -120,11 +152,11 @@ class PhoneNumberType(types.TypeDecorator, ScalarCoercible):
|
|||||||
|
|
||||||
def process_result_value(self, value, dialect):
|
def process_result_value(self, value, dialect):
|
||||||
if value:
|
if value:
|
||||||
return PhoneNumber(value, self.country_code)
|
return PhoneNumber(value, self.region)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def _coerce(self, value):
|
def _coerce(self, value):
|
||||||
if value and not isinstance(value, PhoneNumber):
|
if value and not isinstance(value, PhoneNumber):
|
||||||
value = PhoneNumber(value, country_code=self.country_code)
|
value = PhoneNumber(value, region=self.region)
|
||||||
|
|
||||||
return value or None
|
return value or None
|
||||||
|
@@ -148,3 +148,40 @@ class TestPhoneNumberType(object):
|
|||||||
user = User(phone_number='050111222')
|
user = User(phone_number='050111222')
|
||||||
|
|
||||||
assert isinstance(user.phone_number, PhoneNumber)
|
assert isinstance(user.phone_number, PhoneNumber)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif('types.phone_number.phonenumbers is None')
|
||||||
|
class TestPhoneNumberComposite(object):
|
||||||
|
@pytest.fixture
|
||||||
|
def User(self, Base):
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = 'user'
|
||||||
|
id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
|
||||||
|
name = sa.Column(sa.String(255))
|
||||||
|
_phone_number = sa.Column(sa.String(255))
|
||||||
|
country = sa.Column(sa.String(255))
|
||||||
|
phone_number = sa.orm.composite(
|
||||||
|
PhoneNumber,
|
||||||
|
_phone_number,
|
||||||
|
country
|
||||||
|
)
|
||||||
|
return User
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def user(self, session, User):
|
||||||
|
user = User()
|
||||||
|
user.name = u'Someone'
|
||||||
|
user.phone_number = PhoneNumber('+35840111222', 'FI')
|
||||||
|
session.add(user)
|
||||||
|
session.commit()
|
||||||
|
return user
|
||||||
|
|
||||||
|
def test_query_returns_phone_number_object(
|
||||||
|
self,
|
||||||
|
session,
|
||||||
|
User,
|
||||||
|
user
|
||||||
|
):
|
||||||
|
queried_user = session.query(User).first()
|
||||||
|
assert queried_user.phone_number.national == '040 111222'
|
||||||
|
assert queried_user.phone_number.region == 'FI'
|
||||||
|
Reference in New Issue
Block a user