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.
|
||||
|
||||
|
||||
0.11.0 (2013-05-08)
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Added coercion_listener
|
||||
|
||||
|
||||
0.10.0 (2013-04-29)
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@@ -6,7 +6,45 @@
|
||||
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
|
||||
--------------
|
||||
|
@@ -3,4 +3,3 @@ pytest==2.2.3
|
||||
Pygments==1.2
|
||||
Jinja2==2.3
|
||||
docutils>=0.10
|
||||
phonenumbers>=5.4b1
|
||||
|
@@ -1,4 +1,3 @@
|
||||
SQLAlchemy>=0.7.8
|
||||
psycopg2>=2.4.6
|
||||
phonenumbers>=5.4b1
|
||||
colour==0.0.2
|
||||
|
5
setup.py
5
setup.py
@@ -2,7 +2,7 @@
|
||||
SQLAlchemy-Utils
|
||||
----------------
|
||||
|
||||
Various utility functions for SQLAlchemy.
|
||||
Various utility functions and custom data types for SQLAlchemy.
|
||||
"""
|
||||
|
||||
from setuptools import setup, Command
|
||||
@@ -24,7 +24,7 @@ class PyTest(Command):
|
||||
|
||||
setup(
|
||||
name='SQLAlchemy-Utils',
|
||||
version='0.10.0',
|
||||
version='0.11.0',
|
||||
url='https://github.com/kvesteri/sqlalchemy-utils',
|
||||
license='BSD',
|
||||
author='Konsta Vesterinen',
|
||||
@@ -39,7 +39,6 @@ setup(
|
||||
platforms='any',
|
||||
install_requires=[
|
||||
'SQLAlchemy>=0.7.8',
|
||||
'psycopg2>=2.4.6',
|
||||
'phonenumbers>=5.4b1',
|
||||
'colour==0.0.2'
|
||||
],
|
||||
|
@@ -1,4 +1,5 @@
|
||||
from .functions import sort_query, defer_except, escape_like
|
||||
from .listeners import coercion_listener
|
||||
from .merge import merge, Merger
|
||||
from .types import (
|
||||
ColorType,
|
||||
@@ -17,6 +18,7 @@ from .types import (
|
||||
|
||||
|
||||
__all__ = (
|
||||
coercion_listener,
|
||||
sort_query,
|
||||
defer_except,
|
||||
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 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):
|
||||
"""
|
||||
@@ -107,6 +112,11 @@ class ColorType(types.TypeDecorator):
|
||||
return Color(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):
|
||||
pass
|
||||
@@ -174,6 +184,14 @@ class NumberRangeType(types.TypeDecorator):
|
||||
return NumberRange.from_normalized_str(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):
|
||||
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