diff --git a/.github/workflows/lock.yaml b/.github/workflows/lock.yaml index e962fd04..d51b85cb 100644 --- a/.github/workflows/lock.yaml +++ b/.github/workflows/lock.yaml @@ -19,7 +19,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@be8aa5be94131386884a6da4189effda9b14aa21 + - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 with: issue-inactive-days: 14 pr-inactive-days: 14 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 4b244216..d0a8925e 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -9,8 +9,8 @@ jobs: outputs: hash: ${{ steps.hash.outputs.hash }} steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 with: python-version: '3.x' cache: 'pip' @@ -23,7 +23,7 @@ jobs: - name: generate hash id: hash run: cd dist && echo "hash=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 with: path: ./dist provenance: @@ -63,10 +63,10 @@ jobs: steps: - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # Try uploading to Test PyPI first, in case something fails. - - uses: pypa/gh-action-pypi-publish@b7f401de30cb6434a1e19f805ff006643653240e + - uses: pypa/gh-action-pypi-publish@2f6f737ca5f74c637829c0f5c3acd0e29ea5e8bf with: repository-url: https://test.pypi.org/legacy/ packages-dir: artifact/ - - uses: pypa/gh-action-pypi-publish@b7f401de30cb6434a1e19f805ff006643653240e + - uses: pypa/gh-action-pypi-publish@2f6f737ca5f74c637829c0f5c3acd0e29ea5e8bf with: packages-dir: artifact/ diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 48c9a23d..f5df7e93 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -31,14 +31,14 @@ jobs: - {name: '3.8', python: '3.8', tox: py38} - {name: 'Typing', python: '3.11', tox: typing} steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 with: python-version: ${{ matrix.python }} cache: 'pip' cache-dependency-path: 'requirements/*.txt' - name: cache mypy - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 with: path: ./.mypy_cache key: mypy|${{ matrix.python }}|${{ hashFiles('requirements/mypy.txt') }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 027a97a3..b0f8e732 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,18 +2,18 @@ ci: autoupdate_schedule: monthly repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.15.0 hooks: - id: pyupgrade args: ["--py38-plus"] - repo: https://github.com/asottile/reorder-python-imports - rev: v3.10.0 + rev: v3.12.0 hooks: - id: reorder-python-imports files: "^(?!examples/)" args: ["--py38-plus", "--application-directories", "src"] - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.12.1 hooks: - id: black args: ["--target-version", "py38"] @@ -25,7 +25,7 @@ repos: - flake8-bugbear - flake8-implicit-str-concat - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: fix-byte-order-marker - id: trailing-whitespace diff --git a/CHANGES.rst b/CHANGES.rst index 996fe3f3..7ac59691 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,10 +1,16 @@ +Version 3.1.2 +------------- + +- Fix issue with calling ``repr()`` on ``SQLAlchemy`` instance with no default engine. :issue:`1295` + + Version 3.1.1 ------------- Released 2023-09-11 - Deprecate the ``__version__`` attribute. Use feature detection, or - ``importlib.metadata.version("flask-sqlalchemy")``, instead. :issue:`5230` + ``importlib.metadata.version("flask-sqlalchemy")``, instead. :pr:`1256` Version 3.1.0 diff --git a/README.rst b/README.rst index 92e3b29e..fb4b4a15 100644 --- a/README.rst +++ b/README.rst @@ -40,8 +40,8 @@ A Simple Example db = SQLAlchemy(app, model_class=Base) class User(db.Model): - id: Mapped[int] = mapped_column(db.Integer, primary_key=True) - username: Mapped[str] = mapped_column(db.String, unique=True, nullable=False) + id: Mapped[int] = mapped_column(primary_key=True) + username: Mapped[str] = mapped_column(unique=True) with app.app_context(): db.create_all() diff --git a/docs/customizing.rst b/docs/customizing.rst index 05ab71db..8c8c5abb 100644 --- a/docs/customizing.rst +++ b/docs/customizing.rst @@ -37,10 +37,10 @@ joined-table inheritance. db = SQLAlchemy(app, model_class=Base) class User(db.Model): - name: Mapped[str] = mapped_column(String) + name: Mapped[str] class Employee(User): - title: Mapped[str] = mapped_column(String) + title: Mapped[str] Abstract Models and Mixins @@ -52,34 +52,33 @@ they are created or updated. .. code-block:: python - from datetime import datetime - from sqlalchemy import DateTime, Integer, String - from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr + from datetime import datetime, timezone + from sqlalchemy.orm import Mapped, mapped_column class TimestampModel(db.Model): __abstract__ = True - created: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow) - updated: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + created: Mapped[datetime] = mapped_column(default=lambda: datetime.now(timezone.utc)) + updated: Mapped[datetime] = mapped_column(default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) class Author(db.Model): - id: Mapped[int] = mapped_column(Integer, primary_key=True) - username: Mapped[str] = mapped_column(String, unique=True, nullable=False) + id: Mapped[int] = mapped_column(primary_key=True) + username: Mapped[str] = mapped_column(unique=True) class Post(TimestampModel): - id: Mapped[int] = mapped_column(Integer, primary_key=True) - title: Mapped[str] = mapped_column(String, nullable=False) + id: Mapped[int] = mapped_column(primary_key=True) + title: Mapped[str] This can also be done with a mixin class, inheriting from ``db.Model`` separately. .. code-block:: python class TimestampMixin: - created: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow) - updated: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + created: Mapped[datetime] = mapped_column(default=lambda: datetime.now(timezone.utc)) + updated: Mapped[datetime] = mapped_column(default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) class Post(TimestampMixin, db.Model): - id: Mapped[int] = mapped_column(Integer, primary_key=True) - title: Mapped[str] = mapped_column(String, nullable=False) + id: Mapped[int] = mapped_column(primary_key=True) + title: Mapped[str] Disabling Table Name Generation diff --git a/docs/models.rst b/docs/models.rst index f2c6a159..f9597643 100644 --- a/docs/models.rst +++ b/docs/models.rst @@ -86,9 +86,9 @@ is not set and a primary key column is defined. from sqlalchemy.orm import Mapped, mapped_column class User(db.Model): - id: Mapped[int] = mapped_column(db.Integer, primary_key=True) - username: Mapped[str] = mapped_column(db.String, unique=True, nullable=False) - email: Mapped[str] = mapped_column(db.String) + id: Mapped[int] = mapped_column(primary_key=True) + username: Mapped[str] = mapped_column(unique=True) + email: Mapped[str] Defining a model does not create it in the database. Use :meth:`~.SQLAlchemy.create_all` @@ -139,14 +139,19 @@ defining the full model. Call the :meth:`~.SQLAlchemy.reflect` method on the extension. It will reflect all the tables for each bind key. Each metadata's ``tables`` attribute will contain the detected -table objects. +table objects. See :doc:`binds` for more details on bind keys. .. code-block:: python with app.app_context(): db.reflect() - class User: + # From the default bind key + class Book(db.Model): + __table__ = db.metadata.tables["book"] + + # From an "auth" bind key + class User(db.Model): __table__ = db.metadatas["auth"].tables["user"] In most cases, it will be more maintainable to define the model classes yourself. You diff --git a/docs/pagination.rst b/docs/pagination.rst index 8db2f22c..8289fdf5 100644 --- a/docs/pagination.rst +++ b/docs/pagination.rst @@ -58,7 +58,7 @@ The following Jinja macro renders a simple pagination widget. .. code-block:: jinja {% macro render_pagination(pagination, endpoint) %} -
+
{{ pagination.first }} - {{ pagination.last }} of {{ pagination.total }}