Added coercion_listener
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.11.0 (2013-05-08)
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- Added coercion_listener
|
||||||
|
|
||||||
|
|
||||||
0.10.0 (2013-04-29)
|
0.10.0 (2013-04-29)
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@@ -6,7 +6,45 @@
|
|||||||
SQLAlchemy-Utils
|
SQLAlchemy-Utils
|
||||||
================
|
================
|
||||||
|
|
||||||
SQLAlchemy-Utils provides various utility classes and functions for SQLAlchemy.
|
SQLAlchemy-Utils provides custom data types and various utility functions for SQLAlchemy.
|
||||||
|
|
||||||
|
Using automatic data coercion
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
SQLAlchemy-Utils provides various new data types for SQLAlchemy and in order to gain full
|
||||||
|
advantage of these datatypes you should use coercion_listener. Setting up the listener is easy:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy_utils import coercion_listener
|
||||||
|
|
||||||
|
|
||||||
|
sa.event.listen(sa.orm.mapper, 'mapper_configured', coercion_listener)
|
||||||
|
|
||||||
|
|
||||||
|
The listener automatically detects SQLAlchemy-Utils compatible data types and coerces all attributes
|
||||||
|
using these types to appropriate objects.
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
::
|
||||||
|
from colour import Color
|
||||||
|
from sqlalchemy_utils import ColorType
|
||||||
|
|
||||||
|
|
||||||
|
class Document(Base):
|
||||||
|
__tablename__ = 'player'
|
||||||
|
id = db.Column(db.Integer, autoincrement=True)
|
||||||
|
name = db.Column(db.Unicode(50))
|
||||||
|
background_color = db.Column(ColorType)
|
||||||
|
|
||||||
|
|
||||||
|
document = Document()
|
||||||
|
document.background_color = 'F5F5F5'
|
||||||
|
document.background_color # Color object
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
ScalarListType
|
ScalarListType
|
||||||
--------------
|
--------------
|
||||||
|
@@ -3,4 +3,3 @@ pytest==2.2.3
|
|||||||
Pygments==1.2
|
Pygments==1.2
|
||||||
Jinja2==2.3
|
Jinja2==2.3
|
||||||
docutils>=0.10
|
docutils>=0.10
|
||||||
phonenumbers>=5.4b1
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
SQLAlchemy>=0.7.8
|
SQLAlchemy>=0.7.8
|
||||||
psycopg2>=2.4.6
|
|
||||||
phonenumbers>=5.4b1
|
phonenumbers>=5.4b1
|
||||||
colour==0.0.2
|
colour==0.0.2
|
||||||
|
5
setup.py
5
setup.py
@@ -2,7 +2,7 @@
|
|||||||
SQLAlchemy-Utils
|
SQLAlchemy-Utils
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
Various utility functions for SQLAlchemy.
|
Various utility functions and custom data types for SQLAlchemy.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from setuptools import setup, Command
|
from setuptools import setup, Command
|
||||||
@@ -24,7 +24,7 @@ class PyTest(Command):
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='SQLAlchemy-Utils',
|
name='SQLAlchemy-Utils',
|
||||||
version='0.10.0',
|
version='0.11.0',
|
||||||
url='https://github.com/kvesteri/sqlalchemy-utils',
|
url='https://github.com/kvesteri/sqlalchemy-utils',
|
||||||
license='BSD',
|
license='BSD',
|
||||||
author='Konsta Vesterinen',
|
author='Konsta Vesterinen',
|
||||||
@@ -39,7 +39,6 @@ setup(
|
|||||||
platforms='any',
|
platforms='any',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'SQLAlchemy>=0.7.8',
|
'SQLAlchemy>=0.7.8',
|
||||||
'psycopg2>=2.4.6',
|
|
||||||
'phonenumbers>=5.4b1',
|
'phonenumbers>=5.4b1',
|
||||||
'colour==0.0.2'
|
'colour==0.0.2'
|
||||||
],
|
],
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
from .functions import sort_query, defer_except, escape_like
|
from .functions import sort_query, defer_except, escape_like
|
||||||
|
from .listeners import coercion_listener
|
||||||
from .merge import merge, Merger
|
from .merge import merge, Merger
|
||||||
from .types import (
|
from .types import (
|
||||||
ColorType,
|
ColorType,
|
||||||
@@ -17,6 +18,7 @@ from .types import (
|
|||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
coercion_listener,
|
||||||
sort_query,
|
sort_query,
|
||||||
defer_except,
|
defer_except,
|
||||||
escape_like,
|
escape_like,
|
||||||
|
19
sqlalchemy_utils/listeners.py
Normal file
19
sqlalchemy_utils/listeners.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def coercion_listener(mapper, class_):
|
||||||
|
"""
|
||||||
|
Auto assigns coercing listener for all class properties which are of coerce
|
||||||
|
capable type.
|
||||||
|
"""
|
||||||
|
for prop in mapper.iterate_properties:
|
||||||
|
try:
|
||||||
|
listener = prop.columns[0].type.coercion_listener
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
sa.event.listen(
|
||||||
|
getattr(class_, prop.key),
|
||||||
|
'set',
|
||||||
|
listener,
|
||||||
|
retval=True
|
||||||
|
)
|
@@ -84,6 +84,11 @@ class PhoneNumberType(types.TypeDecorator):
|
|||||||
return PhoneNumber(value, self.country_code)
|
return PhoneNumber(value, self.country_code)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def coercion_listener(self, target, value, oldvalue, initiator):
|
||||||
|
if value is not None and not isinstance(value, PhoneNumber):
|
||||||
|
value = PhoneNumber(value, country_code=self.country_code)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class ColorType(types.TypeDecorator):
|
class ColorType(types.TypeDecorator):
|
||||||
"""
|
"""
|
||||||
@@ -107,6 +112,11 @@ class ColorType(types.TypeDecorator):
|
|||||||
return Color(value)
|
return Color(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def coercion_listener(self, target, value, oldvalue, initiator):
|
||||||
|
if value is not None and not isinstance(value, Color):
|
||||||
|
value = Color(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class ScalarListException(Exception):
|
class ScalarListException(Exception):
|
||||||
pass
|
pass
|
||||||
@@ -174,6 +184,14 @@ class NumberRangeType(types.TypeDecorator):
|
|||||||
return NumberRange.from_normalized_str(value)
|
return NumberRange.from_normalized_str(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def coercion_listener(self, target, value, oldvalue, initiator):
|
||||||
|
if value is not None and not isinstance(value, NumberRange):
|
||||||
|
if isinstance(value, basestring):
|
||||||
|
value = NumberRange.from_normalized_str(value)
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class NumberRangeException(Exception):
|
class NumberRangeException(Exception):
|
||||||
pass
|
pass
|
||||||
|
39
tests/test_coercion_listener.py
Normal file
39
tests/test_coercion_listener.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
from colour import Color
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy_utils import (
|
||||||
|
ColorType,
|
||||||
|
NumberRangeType,
|
||||||
|
NumberRange,
|
||||||
|
PhoneNumberType,
|
||||||
|
PhoneNumber,
|
||||||
|
coercion_listener
|
||||||
|
)
|
||||||
|
from tests import DatabaseTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestCoercionListener(DatabaseTestCase):
|
||||||
|
def create_models(self):
|
||||||
|
class User(self.Base):
|
||||||
|
__tablename__ = 'user'
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True)
|
||||||
|
fav_color = sa.Column(ColorType)
|
||||||
|
phone_number = sa.Column(PhoneNumberType(country_code='FI'))
|
||||||
|
number_of_friends = sa.Column(NumberRangeType)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'User(%r)' % self.id
|
||||||
|
|
||||||
|
self.User = User
|
||||||
|
sa.event.listen(
|
||||||
|
sa.orm.mapper, 'mapper_configured', coercion_listener
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_scalar_attributes_get_coerced_to_objects(self):
|
||||||
|
user = self.User(
|
||||||
|
fav_color='white',
|
||||||
|
phone_number='050111222',
|
||||||
|
number_of_friends='[12, 18]'
|
||||||
|
)
|
||||||
|
assert isinstance(user.fav_color, Color)
|
||||||
|
assert isinstance(user.phone_number, PhoneNumber)
|
||||||
|
assert isinstance(user.number_of_friends, NumberRange)
|
Reference in New Issue
Block a user