diff --git a/examples/flaskr/README.rst b/examples/flaskr/README.rst index 26550f76..b872bc7b 100644 --- a/examples/flaskr/README.rst +++ b/examples/flaskr/README.rst @@ -12,7 +12,7 @@ Install **Be sure to use the same version of the code as the version of the docs you're reading.** You probably want the latest tagged version, but the -default Git version is the master branch. +default Git version is the main branch. .. code-block:: text @@ -42,7 +42,7 @@ Install Flaskr: $ pip install -e . -Or if you are using the master branch, install Flask-SQLAlchemy from +Or if you are using the main branch, install Flask-SQLAlchemy from source before installing Flaskr: .. code-block:: text @@ -79,7 +79,7 @@ Test .. code-block:: text $ pip install -e '.[test]' - $ pytest + $ python3 -m pytest Run with coverage report: diff --git a/examples/flaskr/flaskr/__init__.py b/examples/flaskr/flaskr/__init__.py index d4966239..5eb7e0ee 100644 --- a/examples/flaskr/flaskr/__init__.py +++ b/examples/flaskr/flaskr/__init__.py @@ -4,10 +4,16 @@ from flask import Flask from flask.cli import with_appcontext from flask_sqlalchemy import SQLAlchemy +from sqlalchemy.orm import DeclarativeBase -__version__ = (1, 0, 0, "dev") +__version__ = (1, 1, 0, "dev") -db = SQLAlchemy() + +class Base(DeclarativeBase): + pass + + +db = SQLAlchemy(model_class=Base) def create_app(test_config=None): diff --git a/examples/flaskr/flaskr/auth/models.py b/examples/flaskr/flaskr/auth/models.py index 4409c5c2..9a6834ad 100644 --- a/examples/flaskr/flaskr/auth/models.py +++ b/examples/flaskr/flaskr/auth/models.py @@ -1,22 +1,29 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from sqlalchemy.orm import Mapped, mapped_column, relationship from werkzeug.security import check_password_hash from werkzeug.security import generate_password_hash from flaskr import db +if TYPE_CHECKING: + from ..blog.models import Post -class User(db.Model): - id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String, unique=True, nullable=False) - password_hash = db.Column(db.String, nullable=False) - posts = db.relationship("Post", back_populates="author") +class User(db.Model): + id: Mapped[int] = mapped_column(primary_key=True) + username: Mapped[str] = mapped_column(unique=True) + password_hash: Mapped[str] + posts: Mapped[list[Post]] = relationship("Post", back_populates="author") - def set_password(self, value): + def set_password(self, value: str) -> None: """Store the password as a hash for security.""" self.password_hash = generate_password_hash(value) # allow password = "..." to set a password password = property(fset=set_password) - def check_password(self, value): + def check_password(self, value: str) -> bool: return check_password_hash(self.password_hash, value) diff --git a/examples/flaskr/flaskr/blog/models.py b/examples/flaskr/flaskr/blog/models.py index f3d486d2..0d436e2b 100644 --- a/examples/flaskr/flaskr/blog/models.py +++ b/examples/flaskr/flaskr/blog/models.py @@ -1,3 +1,5 @@ +from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy import ForeignKey from datetime import datetime from datetime import timezone @@ -7,25 +9,25 @@ from flaskr.auth.models import User -def now_utc(): +def now_utc() -> datetime: return datetime.now(timezone.utc) class Post(db.Model): - id = db.Column(db.Integer, primary_key=True) - author_id = db.Column(db.ForeignKey(User.id), nullable=False) - created = db.Column(db.DateTime, nullable=False, default=now_utc) - title = db.Column(db.String, nullable=False) - body = db.Column(db.String, nullable=False) + id: Mapped[int] = mapped_column(primary_key=True) + author_id: Mapped[int] = mapped_column(ForeignKey("user.id")) + created: Mapped[datetime] = mapped_column(default=now_utc) + title: Mapped[str] + body: Mapped[str] # User object backed by author_id # lazy="joined" means the user is returned with the post in one query - author = db.relationship(User, lazy="joined", back_populates="posts") + author: Mapped[User] = relationship(lazy="joined", back_populates="posts") @property - def update_url(self): + def update_url(self) -> str: return url_for("blog.update", id=self.id) @property - def delete_url(self): + def delete_url(self) -> str: return url_for("blog.delete", id=self.id) diff --git a/examples/flaskr/setup.cfg b/examples/flaskr/setup.cfg index 88fa20ac..976931f2 100644 --- a/examples/flaskr/setup.cfg +++ b/examples/flaskr/setup.cfg @@ -15,8 +15,7 @@ include_package_data = true python_requires = >= 3.8 install_requires = Flask>=2.2 - Flask-SQLAlchemy>=3 - SQLAlchemy>=1.4.18 + Flask-SQLAlchemy>=3.1.0 [options.extras_require] test = diff --git a/examples/todo/app.py b/examples/todo/app.py index 5874a795..42da287d 100644 --- a/examples/todo/app.py +++ b/examples/todo/app.py @@ -8,23 +8,32 @@ from flask import request from flask import url_for from flask_sqlalchemy import SQLAlchemy +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.orm import Mapped, mapped_column + app = Flask(__name__) app.secret_key = "Achee6phIexoh8dagiQuew0ephuga4Ih" app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///todo.sqlite" -db = SQLAlchemy(app) -def now_utc(): +class Base(DeclarativeBase): + pass + + +db = SQLAlchemy(app, model_class=Base) + + +def now_utc() -> datetime: return datetime.now(timezone.utc) class Todo(db.Model): - id = db.Column(db.Integer, primary_key=True) - title = db.Column(db.String, nullable=False) - text = db.Column(db.String, nullable=False) - done = db.Column(db.Boolean, nullable=False, default=False) - pub_date = db.Column(db.DateTime, nullable=False, default=now_utc) + id: Mapped[int] = mapped_column(primary_key=True) + title: Mapped[str] + text: Mapped[str] + done: Mapped[bool] = mapped_column(default=False) + pub_date: Mapped[datetime] = mapped_column(default=now_utc) with app.app_context():