From: Mike Bayer Date: Mon, 7 Oct 2013 16:53:04 +0000 (-0400) Subject: - add an option to Bundle single_entity=True to allow for single X-Git-Tag: rel_0_9_0b1~58 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d47a376863bd7c804e4396808841d06b78c0e13a;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - add an option to Bundle single_entity=True to allow for single entity returns without otherwise changing much [ticket:2824] --- diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index 512a07d66e..93c94c9f47 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -33,7 +33,8 @@ def instances(query, cursor, context): for ent in query._entities] filtered = id in filter_fns - single_entity = filtered and len(query._entities) == 1 + single_entity = len(query._entities) == 1 and \ + query._entities[0].supports_single_entity if filtered: if single_entity: @@ -43,7 +44,7 @@ def instances(query, cursor, context): return tuple(fn(x) for x, fn in zip(row, filter_fns)) custom_rows = single_entity and \ - query._entities[0].mapper.dispatch.append_result + query._entities[0].custom_rows (process, labels) = \ list(zip(*[ diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index ebfcf1087a..17fb326639 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -2904,6 +2904,8 @@ class _MapperEntity(_QueryEntity): self.entities = [entity] self.expr = entity + supports_single_entity = True + def setup_entity(self, ext_info, aliased_adapter): self.mapper = ext_info.mapper self.aliased_adapter = aliased_adapter @@ -2918,6 +2920,7 @@ class _MapperEntity(_QueryEntity): else: self._label_name = self.mapper.class_.__name__ self.path = self.entity_zero._path_registry + self.custom_rows = bool(self.mapper.dispatch.append_result) def set_with_polymorphic(self, query, cls_or_mappers, selectable, polymorphic_on): @@ -3102,7 +3105,11 @@ class Bundle(object): """ - def __init__(self, name, *exprs): + single_entity = False + """If True, queries for a single Bundle will be returned as a single + entity, rather than an element within a keyed tuple.""" + + def __init__(self, name, *exprs, **kw): """Construct a new :class:`.Bundle`. e.g.:: @@ -3112,12 +3119,19 @@ class Bundle(object): for row in session.query(bn).filter(bn.c.x == 5).filter(bn.c.y == 4): print(row.mybundle.x, row.mybundle.y) + :param name: name of the bundle. + :param \*exprs: columns or SQL expressions comprising the bundle. + :param single_entity=False: if True, rows for this :class:`.Bundle` + can be returned as a "single entity" outside of any enclosing tuple + in the same manner as a mapped entity. + """ self.name = self._label = name self.exprs = exprs self.c = self.columns = ColumnCollection() self.columns.update((getattr(col, "key", col._label), col) for col in exprs) + self.single_entity = kw.pop('single_entity', self.single_entity) columns = None """A namespace of SQL expressions referred to by this :class:`.Bundle`. @@ -3199,6 +3213,10 @@ class _BundleEntity(_QueryEntity): self.filter_fn = lambda item: item + self.supports_single_entity = self.bundle.single_entity + + custom_rows = False + @property def entity_zero(self): for ent in self._entities: @@ -3330,6 +3348,9 @@ class _ColumnEntity(_QueryEntity): else: self.entity_zero = None + supports_single_entity = False + custom_rows = False + @property def entity_zero_or_selectable(self): if self.entity_zero is not None: diff --git a/test/orm/test_bundle.py b/test/orm/test_bundle.py index 1a60cc685b..29b8e93822 100644 --- a/test/orm/test_bundle.py +++ b/test/orm/test_bundle.py @@ -117,6 +117,37 @@ class BundleTest(fixtures.MappedTest, AssertsCompiledSQL): (('d3d1', 'd3d2'), ('d3d1', 'd3o4'))] ) + def test_single_entity(self): + Data = self.classes.Data + sess = Session() + + b1 = Bundle('b1', Data.d1, Data.d2, single_entity=True) + + eq_( + sess.query(b1). + filter(b1.c.d1.between('d3d1', 'd5d1')). + all(), + [('d3d1', 'd3d2'), ('d4d1', 'd4d2'), ('d5d1', 'd5d2')] + ) + + def test_single_entity_flag_but_multi_entities(self): + Data = self.classes.Data + sess = Session() + + b1 = Bundle('b1', Data.d1, Data.d2, single_entity=True) + b2 = Bundle('b1', Data.d3, single_entity=True) + + eq_( + sess.query(b1, b2). + filter(b1.c.d1.between('d3d1', 'd5d1')). + all(), + [ + (('d3d1', 'd3d2'), ('d3d3',)), + (('d4d1', 'd4d2'), ('d4d3',)), + (('d5d1', 'd5d2'), ('d5d3',)) + ] + ) + def test_bundle_nesting(self): Data = self.classes.Data sess = Session()