[sqlalchemy] Re: Trying to find a way to sort a query given the name of one of its columns

2023-06-10 Thread Lele Gaifax
"Mike Bayer"  writes:

> it looks like you're trying to add an ORDER BY to the table that's
> only there via joinedload(). That's *really* not something we
> anticipate and it would be better if people proposed perhaps ad-hoc
> order_by expressions to be added to common loader options like
> joinedload() and selectinload(), in the same way that we offer ad-hoc
> WHERE criteria for these options now. as you are probably aware, the
> current way to do "joinedload with custom criteria / ordering / etc"
> is to write the query using outerjoin() and order_by() normally, then
> use contains_eager().

Oh, thanks a lot for this!. Accordingly with the git history, I
introduced that test to address a deprecation warning issued by SA 1.4+
load_only()...

I will study the contains_eager() alternative and try to modernize my
code that still uses that idiom.

bye, lele.
-- 
nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri
real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia.
l...@metapensiero.it  | -- Fortunato Depero, 1929.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
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 sqlalchemy+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/87zg576sun.fsf%40metapensiero.it.


Re: [sqlalchemy] Trying to find a way to sort a query given the name of one of its columns

2023-06-10 Thread Mike Bayer
it looks like you're trying to add an ORDER BY to the table that's only there 
via joinedload().That's *really* not something we anticipate and it would 
be better if people proposed perhaps ad-hoc order_by expressions to be added to 
common loader options like joinedload() and selectinload(), in the same way 
that we offer ad-hoc WHERE criteria for these options now.   as you are 
probably aware, the current way to do "joinedload with custom criteria / 
ordering / etc" is to write the query using outerjoin() and order_by() 
normally, then use contains_eager().

the general thing we see here looks like when your "col_by_name()" routine 
runs, it's getting a Column object that's associated with a different alias 
than the one that is used when joinedload() writes out the joins.   I'm not 
sure offhand if the issue is in col_by_name() or something architectural that 
makes the joinedload() join receive additional processing that isn't including 
the order_by.It's hard for us to support this kind of transformation for 
joined eager loads.



On Sat, Jun 10, 2023, at 12:34 PM, Lele Gaifax wrote:
> Hi,
>
> I spent some more time to improve support for SA 2 of one of my SA-based
> libraries[1] (the most ancient one, born with SA 0.5, fifteen years ago!):
> its goal is to provide a layer that made it easier to "expose" a SA
> query (either an ORM one or a Core select()) thru a web service,
> handling particular arguments to apply different kind of "refinements"
> such as ordering and filtering.
>
> One of the tests that exercise the "ordering" refinements fails, and I
> could not understand what is going wrong nor find a workaround.
>
> The following simple script builds an ORM query and prints its SQL:
>
> import sqlalchemy as sa
>
>
> SQLALCHEMY_VERSION = tuple(int(p) if p.isdigit() else p for p in 
> sa.__version__.split('.'))
>
>
> metadata = sa.MetaData()
>
>
> if SQLALCHEMY_VERSION > (2, 0):
> from sqlalchemy.orm import DeclarativeBase
>
> class Base(DeclarativeBase):
> metadata = metadata
> else:
> from sqlalchemy.orm import declarative_base
>
> Base = declarative_base(metadata=metadata)
>
>
> class Person(Base):
> __tablename__ = 'persons'
>
> id = sa.Column(sa.Integer, primary_key=True)
> firstname = sa.Column(sa.String)
>
>
> class Pet(Base):
> __tablename__ = 'pets'
>
> id = sa.Column(sa.Integer, primary_key=True)
> name = sa.Column(sa.String)
> person_id = sa.Column(sa.Integer, sa.ForeignKey('persons.id'))
>
> person = sa.orm.relationship(Person, 
> backref=sa.orm.backref('pets', order_by=id))
>
>
> engine = sa.create_engine('sqlite:///:memory:')
> Session = sa.orm.sessionmaker(bind=engine)
>
> metadata.create_all(engine)
>
>
> query = 
> sa.orm.Query([Pet]).options(sa.orm.joinedload(Pet.person).load_only(Person.firstname))
> print(str(query))
>
> It emits the following output under both SA 1.4 and SA 2.0:
>
> SELECT pets.id AS pets_id, pets.name AS pets_name, pets.person_id 
> AS pets_person_id, persons_1.id AS persons_1_id, persons_1.firstname AS 
> persons_1_firstname 
> FROM pets LEFT OUTER JOIN persons AS persons_1 ON persons_1.id = 
> pets.person_id
>
>
> And now the problem: in short, when the library is asked to apply an
> "order by" on some column(s), it uses an utility function[2] to find a
> column given it's name in the particular query, and then it applies an
> `order_by()` the original query[3].
>
> So, appending the following lines to the script above:
>
> from metapensiero.sqlalchemy.proxy.utils import col_by_name
>
> col = col_by_name(query.statement, 'firstname')
>
> sorted_query = query.order_by(col)
> print(str(sorted_query))
>
> I get the following, expected, output under SA 1.4:
>
> SELECT pets.id AS pets_id, pets.name AS pets_name, pets.person_id 
> AS pets_person_id, persons_1.id AS persons_1_id, persons_1.firstname AS 
> persons_1_firstname 
> FROM pets LEFT OUTER JOIN persons AS persons_1 ON persons_1.id = 
> pets.person_id ORDER BY persons_1.firstname
>
> while under SA 2.0.15 I get this:
>
> SELECT pets.id AS pets_id, pets.name AS pets_name, pets.person_id 
> AS pets_person_id, persons_1.id AS persons_1_id, persons_1.firstname AS 
> persons_1_firstname 
> FROM pets LEFT OUTER JOIN persons AS persons_1 ON persons_1.id = 
> pets.person_id ORDER BY persons_2.firstname
>
> that obviously fails, with the following error:
>
> sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such 
> column: persons_2.firstname
>
> Putting a breakpoint just before applying that `.order_by()` and
> inspecting the `col` returned by the function, I cannot spot any
> difference in the object, under both SA I see
>
> (Pdb) p col
> Column('firstname', String(), table=)
>
> Can you shed some light on what I can try to smooth this different
> behaviour?
>
> Thanks a lot in 

[sqlalchemy] Trying to find a way to sort a query given the name of one of its columns

2023-06-10 Thread Lele Gaifax
Hi,

I spent some more time to improve support for SA 2 of one of my SA-based
libraries[1] (the most ancient one, born with SA 0.5, fifteen years ago!):
its goal is to provide a layer that made it easier to "expose" a SA
query (either an ORM one or a Core select()) thru a web service,
handling particular arguments to apply different kind of "refinements"
such as ordering and filtering.

One of the tests that exercise the "ordering" refinements fails, and I
could not understand what is going wrong nor find a workaround.

The following simple script builds an ORM query and prints its SQL:

import sqlalchemy as sa


SQLALCHEMY_VERSION = tuple(int(p) if p.isdigit() else p for p in 
sa.__version__.split('.'))


metadata = sa.MetaData()


if SQLALCHEMY_VERSION > (2, 0):
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
metadata = metadata
else:
from sqlalchemy.orm import declarative_base

Base = declarative_base(metadata=metadata)


class Person(Base):
__tablename__ = 'persons'

id = sa.Column(sa.Integer, primary_key=True)
firstname = sa.Column(sa.String)


class Pet(Base):
__tablename__ = 'pets'

id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)
person_id = sa.Column(sa.Integer, sa.ForeignKey('persons.id'))

person = sa.orm.relationship(Person, backref=sa.orm.backref('pets', 
order_by=id))


engine = sa.create_engine('sqlite:///:memory:')
Session = sa.orm.sessionmaker(bind=engine)

metadata.create_all(engine)


query = 
sa.orm.Query([Pet]).options(sa.orm.joinedload(Pet.person).load_only(Person.firstname))
print(str(query))

It emits the following output under both SA 1.4 and SA 2.0:

SELECT pets.id AS pets_id, pets.name AS pets_name, pets.person_id AS 
pets_person_id, persons_1.id AS persons_1_id, persons_1.firstname AS 
persons_1_firstname 
FROM pets LEFT OUTER JOIN persons AS persons_1 ON persons_1.id = 
pets.person_id


And now the problem: in short, when the library is asked to apply an
"order by" on some column(s), it uses an utility function[2] to find a
column given it's name in the particular query, and then it applies an
`order_by()` the original query[3].

So, appending the following lines to the script above:

from metapensiero.sqlalchemy.proxy.utils import col_by_name

col = col_by_name(query.statement, 'firstname')

sorted_query = query.order_by(col)
print(str(sorted_query))

I get the following, expected, output under SA 1.4:

SELECT pets.id AS pets_id, pets.name AS pets_name, pets.person_id AS 
pets_person_id, persons_1.id AS persons_1_id, persons_1.firstname AS 
persons_1_firstname 
FROM pets LEFT OUTER JOIN persons AS persons_1 ON persons_1.id = 
pets.person_id ORDER BY persons_1.firstname

while under SA 2.0.15 I get this:

SELECT pets.id AS pets_id, pets.name AS pets_name, pets.person_id AS 
pets_person_id, persons_1.id AS persons_1_id, persons_1.firstname AS 
persons_1_firstname 
FROM pets LEFT OUTER JOIN persons AS persons_1 ON persons_1.id = 
pets.person_id ORDER BY persons_2.firstname

that obviously fails, with the following error:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: 
persons_2.firstname

Putting a breakpoint just before applying that `.order_by()` and
inspecting the `col` returned by the function, I cannot spot any
difference in the object, under both SA I see

(Pdb) p col
Column('firstname', String(), table=)

Can you shed some light on what I can try to smooth this different
behaviour?

Thanks a lot in advance,
bye, lele.

[1] https://gitlab.com/metapensiero/metapensiero.sqlalchemy.proxy
[2] 
https://gitlab.com/metapensiero/metapensiero.sqlalchemy.proxy/-/blob/8db2c398aaf8a0bd679557f630c5c8433a1f4572/src/metapensiero/sqlalchemy/proxy/utils.py#L44-98
[3] 
https://gitlab.com/metapensiero/metapensiero.sqlalchemy.proxy/-/blob/8db2c398aaf8a0bd679557f630c5c8433a1f4572/src/metapensiero/sqlalchemy/proxy/sorters.py#L156-210
-- 
nickname: Lele Gaifax | Dire che Emacs è "conveniente" è come
real: Emanuele Gaifas | etichettare l'ossigeno come "utile"
l...@etour.tn.it  |   -- Rens Troost

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
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 sqlalchemy+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/87a5x7e0oc.fsf%40metapensiero.it.