-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flask-SQLAlchemy
: Make model query non-generic
#8455
Conversation
This comment has been minimized.
This comment has been minimized.
Cc. @kkirsche |
Flask-SQLAlchemy
: Use typing.Self instead of Generic in model query
Ah, that makes sense as in 99% of cases, |
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
Flask-SQLAlchemy
: Use typing.Self instead of Generic in model queryFlask-SQLAlchemy
: Use _typeshed.Self instead of Generic in model query
Flask-SQLAlchemy
: Use _typeshed.Self instead of Generic in model queryFlask-SQLAlchemy
: Use _typeshed.Self
instead of Generic
in model query
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks plausible to me, but I don't really feel qualified to review this -- I don't know my way around the Flask-SQLAlchemy
codebase or the SQLAlchemy
codebase very well. I'd appreciate it if another maintainer could also take a look.
I think using a property may cause issues for code like this: user = User.query.filter(User.username == username.lower()).first() In which case I'm seeing: overloaded function has no attribute `filter` and Pylance in VSCode is telling me:
When I've set this up using the following declarative base for my project locally: from sqlalchemy import MetaData
from sqlalchemy.orm import DeclarativeMeta, registry, Query
from typing import TypeVar
# The following naming conventions are in use for constraints, but additional
# naming conventions may be imposed by the team if multiple sub-applications exist.
convention = {
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
metadata = MetaData(naming_convention=convention)
mapper_registry = registry(metadata=metadata)
Self = TypeVar("Self", bound="Base")
class Base(metaclass=DeclarativeMeta):
"""The base class of SQLAlchemy models.
See the link below for details about this class:
https://docs.sqlalchemy.org/en/14/orm/declarative_styles.html#creating-an-explicit-base-non-dynamically-for-use-with-mypy-similar # noqa
"""
__abstract__ = True
registry = mapper_registry
metadata = mapper_registry.metadata
# These aren't actually properties at runtime, but we treat them as such so we can
# use `self` type without using typing.Self before Python 3.11
@property
def query_class(self: Self) -> type["Query[Self]"] | None:
...
@query_class.setter
def query_class(self: Self, value: type["Query[Self]"] | None) -> None:
...
@property
def query(self: Self) -> "Query[Self]" | None:
...
@query.setter
def query(self: Self, value: "Query[Self]" | None) -> None:
...
__init__ = mapper_registry.constructor |
Is it idiomatic to access |
I would agree with that. It is idiomatic and is demonstrated in their documentation: What's happening is normally, SQLAlchemy models do not have the query_property unless you add it: Flask-SQLAlchemy does the work of automating this process for you, so that (historically only when a request is created and dispatched to the WSGI route but now anytime), the query property will be there. This is done here: So the intent of this was to make the As a result, I would lean towards reverting the changes to model from #8389 until either a more appropriate solution can be determined or the behavior of Self is sufficiently expanded/matured to support this type of use case. |
The main problem that I was looking to solve, just to close the loop was the need for cast: cast("Query[User]", User.query).filter(User.username == username.lower()).first() That often run rampant throughout a codebase when using this style of retrieval without having the generic class attribute |
Thanks @kkirsche, that's really helpful :) @mano7onam, given this, could you possibly change your PR so that it's a simple revert of the changes made to |
This comment has been minimized.
This comment has been minimized.
Flask-SQLAlchemy
: Use _typeshed.Self
instead of Generic
in model queryFlask-SQLAlchemy
: Make model query non-generic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR, and sorry for the delay on this! I realised there was a middle-ground where we could make it non-generic, but without throwing out all of the improvements @kkirsche added. I've gone ahead and applied my idea, and I'll merge when the CI is green :)
This comment has been minimized.
This comment has been minimized.
1 similar comment
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉 |
Thanks for your help with this, Alex. I hope to be around more in the coming weeks. Thank you @mano7onam for your help with getting this fixed. Sorry for the error in the first place! |
No description provided.