On 9/8/15 12:03 PM, Mike Bayer wrote:
On 9/8/15 4:13 AM, Jacob Magnusson wrote:
Michael,
Is there any chance we might see support for /with_labels/ in this
use case? Even though my "solution" works, it's somewhat of an
annoyance with all these warnings being spit out:
/SAWarning: Column 'id' on table
<sqlalchemy.sql.selectable.Select at 0x110cd5d30; Select object>
being replaced by Column('id', Integer(), table=<Select object>,
primary_key=True, nullable=False), which has the same key.
Consider use_labels for select() statements./
It's not clear how with_labels() can be made to work with this without
a lot of re-thinking of many of the internal aspects of the Query and
related objects. OTOH, the Bundle object is fully customizable and
as I recommended, a custom approach that applies labels to Column
objects as they are passed and translates back as needed on the result
side is feasible right now; I'd rather add a labeling/nesting feature
to Bundle itself based on the discoveries gained by working with that
approach as a recipe.
I'm looking at this again, with_labels() doesn't even apply here because
that doesn't override a call to label(). the Query already invokes
with_labels on all select() statements anyway:
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
x = Column(Integer)
y = Column(Integer)
s = Session()
print s.query(Bundle('q', A.x, A.y))
produces:
SELECT a.x AS a_x, a.y AS a_y
FROM a
the original request referred to the use of label() explicitly:
print s.query(Bundle('q', A.x, A.y.label('y')))
which of course, has to render *exactly* that label(), anything else
would be a super surprise:
SELECT a.x AS a_x, a.y AS y
FROM a
Like i said originally, you just need a "key" argument on label(), so
you can say:
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
x = Column(Integer)
y = Column(Integer)
class B(Base):
__tablename__ = 'b'
id = Column(Integer, primary_key=True)
x = Column(Integer)
y = Column(Integer)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
s = Session(e)
s.add_all([A(x=1, y=2), B(x=3, y=4)])
s.commit()
for row in s.query(
Bundle('root', Bundle('a', A.x, A.y.label(None, key='y')),
Bundle('b', B.x, B.y.label(None, key='y')))
):
print row.root.a.y, row.root.b.y
where you get a query like:
SELECT a.x AS a_x, a.y AS y_1, b.x AS b_x, b.y AS y_2
FROM a, b
and the result:
2 4
the patch is below. This is an easy feature add, if desired.
diff --git a/lib/sqlalchemy/orm/attributes.py
b/lib/sqlalchemy/orm/attributes.py
index 5440d6b..ef44d71 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -168,8 +168,8 @@ class QueryableAttribute(interfaces._MappedAttribute,
self._parententity,
of_type=cls)
- def label(self, name):
- return self._query_clause_element().label(name)
+ def label(self, name, key=None):
+ return self._query_clause_element().label(name, key=key)
def operate(self, op, *other, **kwargs):
return op(self.comparator, *other, **kwargs)
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 618b987..4bcb897 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -856,7 +856,7 @@ class ColumnElement(operators.ColumnOperators,
ClauseElement):
"""
return Cast(self, type_)
- def label(self, name):
+ def label(self, name, key=None):
"""Produce a column label, i.e. ``<columnname> AS <name>``.
This is a shortcut to the :func:`~.expression.label` function.
@@ -864,7 +864,7 @@ class ColumnElement(operators.ColumnOperators,
ClauseElement):
if 'name' is None, an anonymous label name will be generated.
"""
- return Label(name, self, self.type)
+ return Label(name, self, self.type, key=key)
@util.memoized_property
def anon_label(self):
@@ -3274,7 +3274,7 @@ class Label(ColumnElement):
__visit_name__ = 'label'
- def __init__(self, name, element, type_=None):
+ def __init__(self, name, element, type_=None, key=None):
"""Return a :class:`Label` object for the
given :class:`.ColumnElement`.
@@ -3304,7 +3304,11 @@ class Label(ColumnElement):
'%%(%d %s)s' % (id(self), getattr(element, 'name',
'anon'))
)
- self.key = self._label = self._key_label = self.name
+ self._label = self.name
+ if key is not None:
+ self.key = self._key_label = key
+ else:
+ self.key = self._key_label = self.name
self._element = element
self._type = type_
self._proxies = [element]
diff --git a/test/orm/test_bulk.py b/test/orm/test_bulk.py
index e2a1464..c209096 100644
--- a/test/orm/test_bulk.py
+++ b/test/orm/test_bulk.py
@@ -60,7 +60,7 @@ class BulkInsertUpdateTest(BulkTest,
_fixtures.FixtureTest):
objects = [
User(name="u1"),
User(name="u2"),
- User(name="u3")
+ User(name="u3"),
]
assert 'id' not in objects[0].__dict__
On Friday, April 10, 2015 at 12:19:31 AM UTC+2, Michael Bayer wrote:
On 4/9/15 1:50 PM, Jacob Magnusson wrote:
I have this case with a bundle that looks something like this:
|primate_bundle = Bundle( 'primate', Primate.name,
Bundle('wooden_tool', *[ WoodenTool.id, WoodenTool.name,
WoodenToolCategory.name.label('category'), ]),
Bundle('solid_tool', *[ SolidTool.id, SolidTool.name,
SolidToolCategory.name.label('category'), ]) ) |
Then I query it like this:
|session.query(primate_bundle) .select_from(Primate)
.join(WoodenTool, Primate.main_tool) .join(WoodenToolCategory,
WoodenTool.category_id == WoodenToolCategory.id)
.join(SolidTool, Primate.secondary_tool)
.join(SolidToolCategory, SolidTool.category_id ==
SolidToolCategory.id) .all() |
However, since the label for category name is the same within
both sub-bundles it will throw |Ambiguous column name| (because
the compiled SQL labels will be exactly the same). Adding
|.with_labels()| doesn’t fix it. Full traceback can be seen by
running the included examples. Commenting out one of the
|.label()| lines in the example makes it runnable. Do you guys
have a clean solution to support this use case? I really like
this feature of creating your own custom made results so it
would be a shame to not be able to do this.
OK well here's one hacky way, that we could better accommodate by
adding a "key" parameter to label(), this is sort of like your
other approach: ay = A.y.label(None) ay.key = 'foobar' bp =
B.p.label(None) bp.key = 'foobar' ab = Bundle( 'ab',
Bundle('a', *[ A.id, A.x, ay, ]),
Bundle('b', *[ B.id, B.q, bp, ]) )
The other way is to provide create_row_processor() as we describe
here:
http://docs.sqlalchemy.org/en/rel_0_9/orm/loading_columns.html?highlight=bundle#column-bundles
<http://docs.sqlalchemy.org/en/rel_0_9/orm/loading_columns.html?highlight=bundle#column-bundles>.
That way you could just make this work any way you wanted: from
sqlalchemy import util class LabelBundle(Bundle): def
__init__(self, *arg, **kw): self.extra_labels =
kw.pop('extra_labels', {}) super(LabelBundle,
self).__init__(*arg, **kw) def create_row_processor(self,
query, procs, labels): # or use a NamedTuple here
keyed_tuple = util.lightweight_named_tuple( 'result',
[self.extra_labels.get(l, l) for l in labels]) def
proc(row): return keyed_tuple([proc(row) for proc in
procs]) return proc ab = LabelBundle( 'ab',
LabelBundle( 'a', extra_labels={'y': 'foobar'},
*[A.id, A.x, A.y]), LabelBundle( 'b',
extra_labels={'p': 'foobar'}, *[B.id, B.q, B.p]) )
Tested on SQLAlchemy 1.0.0b5 and 0.9.9. Python 3.
Thank you so much for any potential help you can give me on
this. I’ve followed the source code for |Bundle| but I can’t
think of a clean way to this…
-- You received this message because you are subscribed to the
Google Groups "sqlalchemy" group. To unsubscribe from this group
and stop receiving emails from it, send an email to
[email protected] <javascript:>. To post to this
group, send email to [email protected] <javascript:>.
Visit this group at http://groups.google.com/group/sqlalchemy
<http://groups.google.com/group/sqlalchemy>. For more options,
visit https://groups.google.com/d/optout
<https://groups.google.com/d/optout>.
-- You received this message because you are subscribed to the Google
Groups "sqlalchemy" group. To unsubscribe from this group and stop
receiving emails from it, send an email to
[email protected]
<mailto:[email protected]>. To post to this
group, send email to [email protected]
<mailto:[email protected]>. Visit this group at
http://groups.google.com/group/sqlalchemy. For more options, visit
https://groups.google.com/d/optout.
-- You received this message because you are subscribed to the Google
Groups "sqlalchemy" group. To unsubscribe from this group and stop
receiving emails from it, send an email to
[email protected]
<mailto:[email protected]>. To post to this
group, send email to [email protected]
<mailto:[email protected]>. Visit this group at
http://groups.google.com/group/sqlalchemy. For more options, visit
https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.