From 85988ab7680bf3796e3b43959c7a58a4a301d8ba Mon Sep 17 00:00:00 2001 From: tsauerwein Date: Fri, 16 Oct 2015 17:03:01 +0200 Subject: [PATCH] Support arrays --- colanderalchemy/schema.py | 83 +++++++++++++++++++++++---------------- tests/models.py | 2 + tests/test_schema.py | 10 +++-- 3 files changed, 58 insertions(+), 37 deletions(-) diff --git a/colanderalchemy/schema.py b/colanderalchemy/schema.py index 4925e76..1f04131 100644 --- a/colanderalchemy/schema.py +++ b/colanderalchemy/schema.py @@ -24,6 +24,7 @@ String, Numeric, Time) +from sqlalchemy.dialects.postgresql import ARRAY from sqlalchemy.schema import (FetchedValue, ColumnDefault, Column) from sqlalchemy.orm import (ColumnProperty, RelationshipProperty) @@ -298,42 +299,16 @@ def get_schema_from_column(self, prop, overrides): log.debug('Column %s: type overridden via TypeDecorator: %s.', name, type_) - elif isinstance(column_type, Boolean): - type_ = colander.Boolean() - - elif isinstance(column_type, Date): - type_ = colander.Date() - - elif isinstance(column_type, DateTime): - type_ = colander.DateTime(default_tzinfo=None) - - elif isinstance(column_type, Enum): - type_ = colander.String() - kwargs["validator"] = colander.OneOf(column.type.enums) - - elif isinstance(column_type, Float): - type_ = colander.Float() - - elif isinstance(column_type, Integer): - type_ = colander.Integer() - - elif isinstance(column_type, String): - type_ = colander.String() - kwargs["validator"] = colander.Length(0, column.type.length) - - elif isinstance(column_type, Numeric): - type_ = colander.Decimal() - - elif isinstance(column_type, Time): - type_ = colander.Time() + elif isinstance(column_type, ARRAY): + name_arr = name + '_array_typ' + kwargs_arr = dict(name=name_arr) + node = colander.SchemaNode( + self.get_type(name_arr, column_type.item_type, kwargs_arr)) + children = [node] + type_ = Sequence() else: - raise NotImplementedError( - 'Not able to derive a colander type from sqlalchemy ' - 'type: %s Please explicitly provide a colander ' - '`typ` for the "%s" Column.' - % (repr(column_type), name) - ) + type_ = self.get_type(name, column_type, kwargs) """ Add default values @@ -400,6 +375,46 @@ def get_schema_from_column(self, prop, overrides): return colander.SchemaNode(type_, *children, **kwargs) + def get_type(self, name, column_type, kwargs): + if isinstance(column_type, Boolean): + type_ = colander.Boolean() + + elif isinstance(column_type, Date): + type_ = colander.Date() + + elif isinstance(column_type, DateTime): + type_ = colander.DateTime(default_tzinfo=None) + + elif isinstance(column_type, Enum): + type_ = colander.String() + kwargs["validator"] = colander.OneOf(column_type.enums) + + elif isinstance(column_type, Float): + type_ = colander.Float() + + elif isinstance(column_type, Integer): + type_ = colander.Integer() + + elif isinstance(column_type, String): + type_ = colander.String() + kwargs["validator"] = colander.Length(0, column_type.length) + + elif isinstance(column_type, Numeric): + type_ = colander.Decimal() + + elif isinstance(column_type, Time): + type_ = colander.Time() + + else: + raise NotImplementedError( + 'Not able to derive a colander type from sqlalchemy ' + 'type: %s Please explicitly provide a colander ' + '`typ` for the "%s" Column.' + % (repr(column_type), name) + ) + + return type_ + def check_overrides(self, name, arg, typedecorator_overrides, declarative_overrides, overrides): msg = None diff --git a/tests/models.py b/tests/models.py index c64433e..4b9fea0 100644 --- a/tests/models.py +++ b/tests/models.py @@ -22,6 +22,7 @@ Unicode, event ) +from sqlalchemy.dialects.postgresql import ARRAY from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import (relationship, mapper) @@ -64,6 +65,7 @@ class Person(Base): gender = Column(Enum('M', 'F'), nullable=False) birthday = Column(Date, nullable=True) age = Column(Integer, nullable=True) + phone_numbers = Column(ARRAY(Unicode(32))) addresses = relationship( 'Address', info={ diff --git a/tests/test_schema.py b/tests/test_schema.py index 4158e9b..6b772d2 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -370,7 +370,9 @@ class UseListOverrides(Base): def _prep_schema(self): overrides = { 'person': { - 'includes': ['name', 'surname', 'gender', 'addresses'], + 'includes': [ + 'name', 'surname', 'gender', 'addresses', 'phone_numbers' + ], 'overrides': { 'addresses': { 'includes': ['street', 'city'], @@ -403,7 +405,8 @@ def test_dictify(self): address = Address(**address_args) person_args = dict(name='My Name', surname='My Surname', - gender='M', addresses=[address]) + gender='M', addresses=[address], + phone_numbers=['+12345', '+234567']) person = Person(**person_args) account_args = dict(email='mailbox@domain.tld', @@ -476,7 +479,8 @@ def test_objectify(self): 'city': 'My City', 'street': 'My Street' }], - 'name': 'My Name' + 'name': 'My Name', + 'phone_numbers': ['+123', '+234'] }, 'enabled': True, 'email': 'mailbox@domain.tld',