summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeong YunWon <jeong@youknowone.org>2016-07-03 22:23:45 +0900
committerJeong YunWon <jeong@youknowone.org>2016-07-11 02:57:51 +0900
commit21349b23dcfd0857785aa53007cc6636259d2395 (patch)
treeaee70fe2e7e81e1dca0598361834bb78c737b56c
parent6b6bdb354252830a1a099c92cb98064337240a1a (diff)
downloadsqlalchemy-21349b23dcfd0857785aa53007cc6636259d2395.tar.gz
Add `default` parameter for `index_property`
And force to use keyword arguments for trivial parameters in index_property Change-Id: I12a178128182f77a2d06b52d7e36f59a36b45a33
-rw-r--r--doc/build/changelog/changelog_11.rst6
-rw-r--r--lib/sqlalchemy/ext/indexable.py35
-rw-r--r--test/ext/test_indexable.py25
3 files changed, 62 insertions, 4 deletions
diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst
index 8ed600639..279cc13f6 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 7d6348524..d0495fe5f 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 56f2e1786..b9e942e4b 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):