From: Jeong YunWon Date: Sun, 3 Jul 2016 13:23:45 +0000 (+0900) Subject: Add `default` parameter for `index_property` X-Git-Tag: rel_1_1_0b3~8^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=21349b23dcfd0857785aa53007cc6636259d2395;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Add `default` parameter for `index_property` And force to use keyword arguments for trivial parameters in index_property Change-Id: I12a178128182f77a2d06b52d7e36f59a36b45a33 --- diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst index 8ed600639c..279cc13f68 100644 --- a/doc/build/changelog/changelog_11.rst +++ b/doc/build/changelog/changelog_11.rst @@ -27,6 +27,12 @@ sqlalchemy.ext.indexable will intercept IndexError as well as KeyError when raising as AttributeError. + .. change:: + :tags: feature, ext + + Added a "default" parameter to the new sqlalchemy.ext.indexable + extension. + .. changelog:: :version: 1.1.0b2 :released: July 1, 2016 diff --git a/lib/sqlalchemy/ext/indexable.py b/lib/sqlalchemy/ext/indexable.py index 7d63485241..d0495fe5f4 100644 --- a/lib/sqlalchemy/ext/indexable.py +++ b/lib/sqlalchemy/ext/indexable.py @@ -92,6 +92,21 @@ A missing key will produce ``AttributeError``:: ... AttributeError: 'name' +Unless you set a default value:: + + >>> class Person(Base): + >>> __tablename__ = 'person' + >>> + >>> id = Column(Integer, primary_key=True) + >>> data = Column(JSON) + >>> + >>> name = index_property('data', 'name', default=None) # See default + + >>> person = Person() + >>> print(person.name) + None + + The attributes are also accessible at the class level. Below, we illustrate ``Person.name`` used to generate an indexed SQL criteria:: @@ -232,9 +247,11 @@ class index_property(hybrid_property): # noqa """ + _NO_DEFAULT_ARGUMENT = object() + def __init__( - self, attr_name, index, datatype=None, - mutable=True, onebased=True): + self, attr_name, index, default=_NO_DEFAULT_ARGUMENT, + datatype=None, mutable=True, onebased=True): """Create a new :class:`.index_property`. :param attr_name: @@ -243,6 +260,9 @@ class index_property(hybrid_property): # noqa :param index: The index to be used for getting and setting this value. This should be the Python-side index value for integers. + :param default: + A value which will be returned instead of `AttributeError` + when there is not a value at given index. :param datatype: default datatype to use when the field is empty. By default, this is derived from the type of index used; a Python list for an integer index, or a Python dictionary for @@ -265,6 +285,7 @@ class index_property(hybrid_property): # noqa ) self.attr_name = attr_name self.index = index + self.default = default is_numeric = isinstance(index, int) onebased = is_numeric and onebased @@ -277,15 +298,21 @@ class index_property(hybrid_property): # noqa self.datatype = dict self.onebased = onebased + def _fget_default(self): + if self.default == self._NO_DEFAULT_ARGUMENT: + raise AttributeError(self.attr_name) + else: + return self.default + def fget(self, instance): attr_name = self.attr_name column_value = getattr(instance, attr_name) if column_value is None: - raise AttributeError(self.attr_name) + return self._fget_default() try: value = column_value[self.index] except (KeyError, IndexError): - raise AttributeError(self.attr_name) + return self._fget_default() else: return value diff --git a/test/ext/test_indexable.py b/test/ext/test_indexable.py index 56f2e1786b..b9e942e4b2 100644 --- a/test/ext/test_indexable.py +++ b/test/ext/test_indexable.py @@ -138,6 +138,31 @@ class IndexPropertyTest(fixtures.TestBase): j.field = 10 eq_(j.field, 10) + def test_get_default_value(self): + Base = declarative_base() + + class J(Base): + __tablename__ = 'j' + id = Column(Integer, primary_key=True) + json = Column(JSON, default={}) + default = index_property('json', 'field', default='default') + none = index_property('json', 'field', default=None) + + j = J() + assert j.json is None + + assert j.default == 'default' + assert j.none is None + j.json = {} + assert j.default == 'default' + assert j.none is None + j.default = None + assert j.default is None + assert j.none is None + j.none = 10 + assert j.default is 10 + assert j.none == 10 + class IndexPropertyArrayTest(fixtures.DeclarativeMappedTest):