Commits

Mike Bayer committed d47a376

- add an option to Bundle single_entity=True to allow for single
entity returns without otherwise changing much [ticket:2824]

  • Participants
  • Parent commits 9b2d90a

Comments (0)

Files changed (3)

File lib/sqlalchemy/orm/loading.py

                 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:
                 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(*[

File lib/sqlalchemy/orm/query.py

         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
         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):
 
     """
 
-    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.::
             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`.
 
         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:
         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:

File test/orm/test_bundle.py

                 (('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()