]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add `default` parameter for `index_property`
authorJeong YunWon <jeong@youknowone.org>
Sun, 3 Jul 2016 13:23:45 +0000 (22:23 +0900)
committerJeong YunWon <jeong@youknowone.org>
Sun, 10 Jul 2016 17:57:51 +0000 (02:57 +0900)
And force to use keyword arguments for trivial parameters in index_property

Change-Id: I12a178128182f77a2d06b52d7e36f59a36b45a33

doc/build/changelog/changelog_11.rst
lib/sqlalchemy/ext/indexable.py
test/ext/test_indexable.py

index 8ed600639c3bec879ad5d46c95a031141bba6905..279cc13f68b2bbdd252fbdf870fae964e7983f48 100644 (file)
         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
index 7d634852416856696e1d677f0c9a84d12f54205a..d0495fe5f404ddad6ac9759b6decb5b216b559d0 100644 (file)
@@ -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
 
index 56f2e1786b32c7f7726665de6248c36fc1c2df10..b9e942e4b21a375aa7c948ddb82878268bd61f0e 100644 (file)
@@ -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):