> > yes so, SQLAlchemy 2.0's approach is frankly at odds with the spirit of > Flask-SQLAlchemy. The Query and "dynamic" loaders are staying around > largely so that Flask can come on board, however the patterns in F-S are > pretty much the ones I want to get away from.
2.0's spirit is one where the act of creating a SELECT statement is a > standalone thing that is separate from being attached to any specific class > (really all of SQLAlchemy was like this, but F-S has everyone doing the > Model.query thing that I've always found to be more misleading than > helpful), but SELECT statements are now also disconnected from any kind of > "engine" or "Session" when constructed. as for with_parent(), with_parent is what the dynamic loader actually uses > to create the query. so this is a matter of code organization. > F-S would have you say: > user = User.query.filter_by(name='name').first() > address = user.addresses.filter_by(email='email').first() > noting above, there's no "Session" anywhere. where is it? Here's a > Hacker News comment lamenting the real world implications of this: > https://news.ycombinator.com/item?id=26183936 > SQLAlchemy 2.0 would have you say instead: > with Session(engine) as session: > user = session.execute( > select(User).filter_by(name='name') > ).scalars().first() > > address = session.execute( > select(Address).where(with_parent(user, > Address.user)).filter_by(email='email') > ).scalars().first() > Noting above, a web framework integration may still wish to provide the > "session" to data-oriented methods and manage its scope, but IMO it should > be an explicit object passed around. The database connection / transaction > shouldn't be made to appear to be inside the ORM model object, since that's > not what's actually going on. The newer design indeed provides a clearer view of the session. If you look at any commentary anywhere about SQLAlchemy, the top complaints > are: > 1. too magical, too implicit > 2. what's wrong with just writing SQL? > SQLAlchemy 2.0 seeks to streamline the act of ORMing such that the user > *is* writing SQL, they're running it into an execute() method, and they are > managing the scope of connectivity and transactions in an obvious way. > People don't necessarily want bloat and verbosity but they do want to see > explicitness when the computer is being told to do something, especially > running a SQL query. We're trying to hit that balance as closely as > possible. > The above style also has in mind compatibility with asyncio, which we now > support. With asyncio, it's very important that the boundary where IO > occurs is very obvious. Hence the Session.execute() method now becomes the > place where users have to "yield". With the older Query interface, the > "yields" would be all over the place and kind of arbirary, since some Query > methods decide to execute at one point or another. > Flask-SQLAlchemy therefore has to decide where it wants to go with this > direction, and there are options, including sticking with the legacy query > / dynamic loader, perhaps vendoring a new interface that behaves in the > flask-sqlalchemy style but uses 2.0-style patterns under the hood, or it > can go along with the 2.0 model for future releases. From > SQLAlchemy's point of view, the Query was always not well thought out and > was inconsistent with how Core worked, and I've wanted for years to resolve > that problem. I'm not authorized to talk on behalf of F-S but IMO, these options could be milestones applied in parallel toward migration to 2.0. However, a question arises here, that you might have already seen, which is: given the major leap in how SQLAlchemy 2.0 is designed, is it better to think of rebuilding medium+ projects for 2.0 while maintaining existing codebases for 1.3? In other words, how much will 2.0 be backward compatible with 1.3? A. On Fri, Feb 26, 2021, 5:18 PM Mike Bayer <mike...@zzzcomputing.com> wrote: > > > On Fri, Feb 26, 2021, at 8:04 AM, Ahmed wrote: > > Hi Mike - Thank you for your insights. Actually, this is part of upgrading > Flask-SQLAlchemy library dependency to 1.4.0b3 and eventually 2.0. The > snippet above is extracted from a test case that didn't pass against > 1.4.0b3. > > I've checked sqlalchemy.orm.with_parent > <https://docs.sqlalchemy.org/en/14/orm/query.html?highlight=with_parent#sqlalchemy.orm.with_parent> > (Python > function, in Query API) documentation entry, however, it's not clear to me > how with_parent construct can fit in the implementation instead of Query. > I guess it would require a major change in how the library > (Flask-SQLAlchemy) is currently designed as it functionally extends > sqlalchemy.orm.Query and pass the extended class to relationship and > other constructs as well. > > > yes so, SQLAlchemy 2.0's approach is frankly at odds with the spirit of > Flask-SQLAlchemy. The Query and "dynamic" loaders are staying around > largely so that Flask can come on board, however the patterns in F-S are > pretty much the ones I want to get away from. > > 2.0's spirit is one where the act of creating a SELECT statement is a > standalone thing that is separate from being attached to any specific class > (really all of SQLAlchemy was like this, but F-S has everyone doing the > Model.query thing that I've always found to be more misleading than > helpful), but SELECT statements are now also disconnected from any kind of > "engine" or "Session" when constructed. > > as for with_parent(), with_parent is what the dynamic loader actually uses > to create the query. so this is a matter of code organization. > > F-S would have you say: > > user = User.query.filter_by(name='name').first() > address = user.addresses.filter_by(email='email').first() > > noting above, there's no "Session" anywhere. where is it? Here's a > Hacker News comment lamenting the real world implications of this: > https://news.ycombinator.com/item?id=26183936 > > SQLAlchemy 2.0 would have you say instead: > > with Session(engine) as session: > user = session.execute( > select(User).filter_by(name='name') > ).scalars().first() > > address = session.execute( > select(Address).where(with_parent(user, > Address.user)).filter_by(email='email') > ).scalars().first() > > Noting above, a web framework integration may still wish to provide the > "session" to data-oriented methods and manage its scope, but IMO it should > be an explicit object passed around. The database connection / transaction > shouldn't be made to appear to be inside the ORM model object, since that's > not what's actually going on. > > If you look at any commentary anywhere about SQLAlchemy, the top > complaints are: > > 1. too magical, too implicit > > 2. what's wrong with just writing SQL? > > SQLAlchemy 2.0 seeks to streamline the act of ORMing such that the user > *is* writing SQL, they're running it into an execute() method, and they are > managing the scope of connectivity and transactions in an obvious way. > People don't necessarily want bloat and verbosity but they do want to see > explicitness when the computer is being told to do something, especially > running a SQL query. We're trying to hit that balance as closely as > possible. > > The above style also has in mind compatibility with asyncio, which we now > support. With asyncio, it's very important that the boundary where IO > occurs is very obvious. Hence the Session.execute() method now becomes the > place where users have to "yield". With the older Query interface, the > "yields" would be all over the place and kind of arbirary, since some Query > methods decide to execute at one point or another. > > Flask-SQLAlchemy therefore has to decide where it wants to go with this > direction, and there are options, including sticking with the legacy query > / dynamic loader, perhaps vendoring a new interface that behaves in the > flask-sqlalchemy style but uses 2.0-style patterns under the hood, or it > can go along with the 2.0 model for future releases. From > SQLAlchemy's point of view, the Query was always not well thought out and > was inconsistent with how Core worked, and I've wanted for years to resolve > that problem. > > - mike > > > > > On Thursday, February 25, 2021 at 2:21:43 PM UTC-8 Mike Bayer wrote: > > > this will be fixed in https://github.com/sqlalchemy/sqlalchemy/issues/5981 > where I've reverted entirely some changes to AppenderQuery that made it > work more in 2.0 style. As Query is going to be present in 2.0, "dynamic" > relationships will remain also as legacy. They are superseded by explicit > use of the with_parent() filtering construct. > > > > On Thu, Feb 25, 2021, at 3:25 PM, Ahmed wrote: > > Hello, > > It seems that SQLAlchemy 1.4.0b3 ignores relationship() query_class > parameter. Here's the snippet that works with 1.3 but doesn't with 1.4: > > class Parent(db.Model): > __tablename__ = "todo" > id = db.Column(db.Integer, primary_key=True) > # ... Column mappings > children = db.relationship("Child", > backref="todo", query_class=DerivedQuery, lazy="dynamic") > > class Child(db.Model): > __tablename__ = "todo" > # ... Column mappings > parent_id = db.Column(db.Integer, db.ForeignKey("todo.id")) > > assert isinstance(p.children, DerivedQuery) > > In 1.4, children attribute is always an instance of AppenderQuery > regardless of the query_class value. I might have missed something above > though. > > > > -- > 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+...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/sqlalchemy/4454277c-b3a1-484e-b0e5-aef3e72eeb01n%40googlegroups.com > <https://groups.google.com/d/msgid/sqlalchemy/4454277c-b3a1-484e-b0e5-aef3e72eeb01n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > > > -- > 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/087934f8-d7fb-4062-8b2a-9d623a2e7941n%40googlegroups.com > <https://groups.google.com/d/msgid/sqlalchemy/087934f8-d7fb-4062-8b2a-9d623a2e7941n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > > > -- > 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/f161e6b7-b8b1-457c-bd96-b4e2494b52f8%40www.fastmail.com > <https://groups.google.com/d/msgid/sqlalchemy/f161e6b7-b8b1-457c-bd96-b4e2494b52f8%40www.fastmail.com?utm_medium=email&utm_source=footer> > . > -- 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/CAKwjfOB%2BeToncrsTp7AC8%2Bfa5T-bdDuq%2Bbia81_A9YM1Lp10cw%40mail.gmail.com.