[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#988970: unblock: flask-sqlalchemy/2.5.1-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package flask-sqlalchemy

I'd like to request the transition of flask-sqlalchemy from unstable
into testing/bullseye.
I know it's rather late in the freeze process, and the request I have is
for sure not the typical things at this time, I hope the RT can follow
my arguments and will accept my unblock request.
If not than world is not going down tomorrow. :)

[ Reason ]
The current version of flask-sqlalchemy in testing is basically five
years old and lagging long behind the upstream releases. The version
2.1-1 was uploaded 2016-02-23 and got only Debian related modification
until 2.1-4 in 2019-08-10, means the package is mostly outdated and not
really usable anymore in user projects.
On my day job we run into trouble due the outdated version (the work was
based on testing/bullseye due the planned release).
Unfortunately wo found this out just shortly after the hard freeze on
2021-03-12, working with an updated package version based on the latest
upstream release has made the trouble go away.
The issues we had were connection issue to our PostgreSQL cluster.

After the technical thing it took some time to think about how to
proceed and it turns out that the previous maintainer of
flask-sqlalchemy Thomas Bechtold was set MIA since spring of 2018 and
the Python team suggested to take over the maintainer ship for this
package.

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=892679
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=948698

The upload to unstable happend due this about 10 days ago.

The problem about the outdated version wasn't new and version 2.4.1 was
prepared by Emmanuel Arias in the past but did not find a sponsor.

Given the current version in testing and stable is technically from
2016, the lifetime for bullseye will also be 5 years it's really
desirable Debian would ship an actual version in bullseye.

[ Impact ]
The user would get an recent version to work with after a long time and
will be able to simply do the Debian way of "apt-get install" and all
will work afterwards.

[ Tests ]
This package has no autopkgtest (yet), it's five years old and no one
has added some autopkgtests until now.
As I said we use Flask and flask-sqlalchemy on my day job and do rely on
the requested package version, since the packaging of the new upstream
version weeks ago and the usage of this in our productive environment we
don't have seen any issues that let me think the package would not fit
to get released with bullseye.

[ Risks ]
I see no risks in updating this package, otherwise I wouldn't request the
unblock.
This package isn't a big nor a key package nor has other packages that
do depend on it (except build dependencies). But the popcorn indicator
isn't also that low, but it shows also the installations have lost about
30% since the freeze process for bullseye.
Updating this package will only have impact for users of
flask-sqlalchemy and will not affect users of flask which do not rely on
this package in ay way.

Of course there are a lot of changes within the source code since 2.1,
quite a lot are just results of reformatting Python doc strings,
extending code functionality and switched code positions.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
To decrease the debdiff attached below the output was is a bit cleaned
out about unrelated modifications like documentation, modified examples,
tests and the project graphic. The debdiff delta is still but not really
surprisingly huge.

I've also attached a full debdiff.

unblock flask-sqlalchemy/2.5.1-1

debdiff --exclude docs \
        --exclude examples \
        --exclude flask-sqlalchemy.svg \
        --exclude tests \
        --exclude test_* \
flask-sqlalchemy_2.1-4.dsc flask-sqlalchemy_2.5.1-1.dsc

diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/CHANGES flask-sqlalchemy-2.5.1/CHANGES
--- flask-sqlalchemy-2.1/CHANGES	2015-10-23 11:49:12.000000000 +0200
+++ flask-sqlalchemy-2.5.1/CHANGES	1970-01-01 01:00:00.000000000 +0100
@@ -1,112 +0,0 @@
-Changelog
-=========
-
-Here you can see the full list of changes between each Flask-SQLAlchemy
-release.
-
-Version 3.0
------------
-
-In development, codename Dubnium
-
-Version 2.1
------------
-
-Released on October 23rd 2015, codename Caesium
-
-- Table names are automatically generated in more cases, including
-  subclassing mixins and abstract models.
-- Allow using a custom MetaData object.
-- Add support for binds parameter to session.
-
-Version 2.0
------------
-
-Released on August 29th 2014, codename Bohrium
-
-- Changed how the builtin signals are subscribed to skip non Flask-SQLAlchemy
-  sessions.  This will also fix the attribute error about model changes
-  not existing.
-- Added a way to control how signals for model modifications are tracked.
-- Made the ``SignallingSession`` a public interface and added a hook
-  for customizing session creation.
-- If the ``bind`` parameter is given to the signalling session it will no
-  longer cause an error that a parameter is given twice.
-- Added working table reflection support.
-- Enabled autoflush by default.
-- Consider ``SQLALCHEMY_COMMIT_ON_TEARDOWN`` harmful and remove from docs.
-
-Version 1.0
------------
-
-Released on July 20th 2013, codename Aurum
-
-- Added Python 3.3 support.
-- Dropped 2.5 compatibility.
-- Various bugfixes
-- Changed versioning format to do major releases for each update now.
-
-Version 0.16
-------------
-
-- New distribution format (flask_sqlalchemy)
-- Added support for Flask 0.9 specifics.
-
-Version 0.15
-------------
-
-- Added session support for multiple databases
-
-Version 0.14
-------------
-
-- Make relative sqlite paths relative to the application root.
-
-Version 0.13
-------------
-
-- Fixed an issue with Flask-SQLAlchemy not selecting the correct binds.
-
-Version 0.12
-------------
-- Added support for multiple databases.
-- Expose Flask-SQLAlchemy's BaseQuery as `db.Query`.
-- Set default query_class for `db.relation`, `db.relationship`, and
-  `db.dynamic_loader` to Flask-SQLAlchemy's BaseQuery.
-- Improved compatibility with Flask 0.7.
-
-Version 0.11
-------------
-
-- Fixed a bug introduced in 0.10 with alternative table constructors.
-
-Version 0.10
-------------
-
-- Added support for signals.
-- Table names are now automatically set from the class name unless
-  overriden.
-- Model.query now always works for applications directly passed to
-  the SQLAlchemy constructor.  Furthermore the property now raises
-  an RuntimeError instead of being None.
-- added session options to constructor.
-- fixed a broken `__repr__`
-- `db.Table` is now a factor function that creates table objects.
-  This makes it possible to omit the metadata.
-
-Version 0.9
------------
-
-- applied changes to pass the Flask extension approval process.
-
-Version 0.8
------------
-
-- added a few configuration keys for creating connections.
-- automatically activate connection recycling for MySQL connections.
-- added support for the Flask testing mode.
-
-Version 0.7
------------
-
-- Initial public release
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/CHANGES.rst flask-sqlalchemy-2.5.1/CHANGES.rst
--- flask-sqlalchemy-2.1/CHANGES.rst	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/CHANGES.rst	2021-03-18 20:01:27.000000000 +0100
@@ -0,0 +1,263 @@
+Version 2.5.1
+-------------
+
+Released 2021-03-18
+
+-   Fix compatibility with Python 2.7.
+
+
+Version 2.5.0
+-------------
+
+Released 2021-03-18
+
+-   Update to support SQLAlchemy 1.4.
+-   SQLAlchemy ``URL`` objects are immutable. Some internal methods have
+    changed to return a new URL instead of ``None``. :issue:`885`
+
+
+Version 2.4.4
+-------------
+
+Released 2020-07-14
+
+-   Change base class of meta mixins to ``type``. This fixes an issue
+    caused by a regression in CPython 3.8.4. :issue:`852`
+
+
+Version 2.4.3
+-------------
+
+Released 2020-05-26
+
+-   Deprecate ``SQLALCHEMY_COMMIT_ON_TEARDOWN`` as it can cause various
+    design issues that are difficult to debug. Call
+    ``db.session.commit()`` directly instead. :issue:`216`
+
+
+Version 2.4.2
+-------------
+
+Released 2020-05-25
+
+-   Fix bad pagination when records are de-duped. :pr:`812`
+
+
+Version 2.4.1
+-------------
+
+Released 2019-09-24
+
+-   Fix ``AttributeError`` when using multiple binds with polymorphic
+    models. :pr:`651`
+
+
+Version 2.4.0
+-------------
+
+Released 2019-04-24
+
+-   Make engine configuration more flexible. (:pr:`684`)
+-   Address SQLAlchemy 1.3 deprecations. (:pr:`684`)
+-   ``get_or_404()`` and ``first_or_404()`` now accept a ``description``
+    parameter to control the 404 message. (:issue:`636`)
+-   Use ``time.perf_counter`` for Python 3 on Windows. (:issue:`638`)
+-   Drop support for Python 2.6 and 3.3. (:pr:`687`)
+-   Add an example of Flask's tutorial project, Flaskr, adapted for
+    Flask-SQLAlchemy. (:pr:`720`)
+
+
+Version 2.3.2
+-------------
+
+Released 2017-10-11
+
+-   Don't mask the parent table for single-table inheritance models.
+    (:pr:`561`)
+
+
+Version 2.3.1
+-------------
+
+Released 2017-10-05
+
+-   If a model has a table name that matches an existing table in the
+    metadata, use that table. Fixes a regression where reflected tables
+    were not picked up by models. (:issue:`551`)
+-   Raise the correct error when a model has a table name but no primary
+    key. (:pr:`556`)
+-   Fix ``repr`` on models that don't have an identity because they have
+    not been flushed yet. (:issue:`555`)
+-   Allow specifying a ``max_per_page`` limit for pagination, to avoid
+    users specifying high values in the request args. (:pr:`542`)
+-   For ``paginate`` with ``error_out=False``, the minimum value for
+    ``page`` is 1 and ``per_page`` is 0. (:issue:`558`)
+
+
+Version 2.3.0
+-------------
+
+Released 2017-09-28
+
+-   Multiple bugs with ``__tablename__`` generation are fixed. Names
+    will be generated for models that define a primary key, but not for
+    single-table inheritance subclasses. Names will not override a
+    ``declared_attr``. ``PrimaryKeyConstraint`` is detected.
+    (:pr:`541`)
+-   Passing an existing ``declarative_base()`` as ``model_class`` to
+    ``SQLAlchemy.__init__`` will use this as the base class instead of
+    creating one. This allows customizing the metaclass used to
+    construct the base. (:issue:`546`)
+-   The undocumented ``DeclarativeMeta`` internals that the extension
+    uses for binds and table name generation have been refactored to
+    work as mixins. Documentation is added about how to create a custom
+    metaclass that does not do table name generation. (:issue:`546`)
+-   Model and metaclass code has been moved to a new ``models`` module.
+    ``_BoundDeclarativeMeta`` is renamed to ``DefaultMeta``; the old
+    name will be removed in 3.0. (:issue:`546`)
+-   Models have a default ``repr`` that shows the model name and primary
+    key. (:pr:`530`)
+-   Fixed a bug where using ``init_app`` would cause connectors to
+    always use the ``current_app`` rather than the app they were created
+    for. This caused issues when multiple apps were registered with the
+    extension. (:pr:`547`)
+
+
+Version 2.2
+-----------
+
+Released 2017-02-27, codename Dubnium
+
+-   Minimum SQLAlchemy version is 0.8 due to use of
+    ``sqlalchemy.inspect``.
+-   Added support for custom ``query_class`` and ``model_class`` as args
+    to the ``SQLAlchemy`` constructor. (:pr:`328`)
+-   Allow listening to SQLAlchemy events on ``db.session``.
+    (:pr:`364`)
+-   Allow ``__bind_key__`` on abstract models. (:pr:`373`)
+-   Allow ``SQLALCHEMY_ECHO`` to be a string. (:issue:`409`)
+-   Warn when ``SQLALCHEMY_DATABASE_URI`` is not set. (:pr:`443`)
+-   Don't let pagination generate invalid page numbers. (:issue:`460`)
+-   Drop support of Flask < 0.10. This means the db session is always
+    tied to the app context and its teardown event. (:issue:`461`)
+-   Tablename generation logic no longer accesses class properties
+    unless they are ``declared_attr``. (:issue:`467`)
+
+
+Version 2.1
+-----------
+
+Released 2015-10-23, codename Caesium
+
+-   Table names are automatically generated in more cases, including
+    subclassing mixins and abstract models.
+-   Allow using a custom MetaData object.
+-   Add support for binds parameter to session.
+
+
+Version 2.0
+-----------
+
+Released 2014-08-29, codename Bohrium
+
+-   Changed how the builtin signals are subscribed to skip
+    non-Flask-SQLAlchemy sessions. This will also fix the attribute
+    error about model changes not existing.
+-   Added a way to control how signals for model modifications are
+    tracked.
+-   Made the ``SignallingSession`` a public interface and added a hook
+    for customizing session creation.
+-   If the ``bind`` parameter is given to the signalling session it will
+    no longer cause an error that a parameter is given twice.
+-   Added working table reflection support.
+-   Enabled autoflush by default.
+-   Consider ``SQLALCHEMY_COMMIT_ON_TEARDOWN`` harmful and remove from
+    docs.
+
+
+Version 1.0
+-----------
+
+Released 2013-07-20, codename Aurum
+
+-   Added Python 3.3 support.
+-   Dropped 2.5 compatibility.
+-   Various bugfixes
+-   Changed versioning format to do major releases for each update now.
+
+
+Version 0.16
+------------
+
+-   New distribution format (flask_sqlalchemy)
+-   Added support for Flask 0.9 specifics.
+
+
+Version 0.15
+------------
+
+-   Added session support for multiple databases.
+
+
+Version 0.14
+------------
+
+-   Make relative sqlite paths relative to the application root.
+
+
+Version 0.13
+------------
+
+-   Fixed an issue with Flask-SQLAlchemy not selecting the correct
+    binds.
+
+
+Version 0.12
+------------
+
+-   Added support for multiple databases.
+-   Expose ``BaseQuery`` as ``db.Query``.
+-   Set default ``query_class`` for ``db.relation``,
+    ``db.relationship``, and ``db.dynamic_loader`` to ``BaseQuery``.
+-   Improved compatibility with Flask 0.7.
+
+
+Version 0.11
+------------
+
+-   Fixed a bug introduced in 0.10 with alternative table constructors.
+
+
+Version 0.10
+------------
+
+-   Added support for signals.
+-   Table names are now automatically set from the class name unless
+    overridden.
+-   ``Model.query`` now always works for applications directly passed to
+    the ``SQLAlchemy`` constructor. Furthermore the property now raises
+    a ``RuntimeError`` instead of being ``None``.
+-   Added session options to constructor.
+-   Fixed a broken ``__repr__``.
+-   ``db.Table`` is now a factory function that creates table objects.
+    This makes it possible to omit the metadata.
+
+
+Version 0.9
+-----------
+
+-   Applied changes to pass the Flask extension approval process.
+
+
+Version 0.8
+-----------
+
+-   Added a few configuration keys for creating connections.
+-   Automatically activate connection recycling for MySQL connections.
+-   Added support for the Flask testing mode.
+
+
+Version 0.7
+-----------
+
+-   Initial public release
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/changelog flask-sqlalchemy-2.5.1/debian/changelog
--- flask-sqlalchemy-2.1/debian/changelog	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/changelog	2021-05-11 20:54:11.000000000 +0200
@@ -1,3 +1,52 @@
+flask-sqlalchemy (2.5.1-1) unstable; urgency=medium
+
+  [ Emmanuel Arias ]
+  * New upstream version 2.4.1
+    (Closes: #948698)
+  * d/docs use README.rst instead of README.
+  * d/gbp.conf: Add gbp configuration file.
+    - Set debian-branch on debian/master
+    - Set pristine-tar on True
+  * d/gitlab-ci.yml: enable Salsa CI.
+
+  [ Ondřej Nový ]
+  * Bump Standards-Version to 4.4.1.
+  * d/control: Update Maintainer field with new Debian Python Team
+    contact address.
+  * d/control: Update Vcs-* fields with new Debian Python Team Salsa
+    layout.
+
+  [ Debian Janitor ]
+  * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository,
+    Repository-Browse.
+  * Update standards version to 4.5.0, no changes needed.
+
+  [ Carsten Schoenert ]
+  * [434c411] d/gbp.conf: Add some more defaults
+    Add some more default behavior while working with git-buildpackage
+    like the upstream branch, compression type for orig tarball and length
+    of commit IDs.
+  * [6079ced] d/watch: Update to version 4
+  * [9691684] d/control: Bump debhelper version to 13
+  * [dbf29f6] d/control: Update Standards-Version to 4.5.1
+    No further changes needed.
+  * [d5f4edf] New upstream version 2.5.1
+  * [545c0c2] d/copyright: Update years of contributions
+  * [acfb558] d/control: Update Uploaders list
+    Adding Henning and myself to the list of uploaders and taking over
+    maintainership for this package.
+    Thanks go out to Thomas for maintaining this package in the past!
+    (Closes: #892679)
+  * [2ce791e] d/control: Update Homepage field to real project website
+    The PyPi project webspace isn't the source for the upstream data, the
+    real home is within the palletsprojects.com umbrella.
+  * [e31076d] d/control: Adding entry Rules-Requires-Root: no
+  * [b68a975] *.docs: Also installing the provided examples folder
+  * [1f17669] d/rules: Clean out .gitignore file within examples
+  * [f885eef] lintian-overides: Ignore some files in examples
+
+ -- Carsten Schoenert <c.schoenert@t-online.de>  Tue, 11 May 2021 20:54:11 +0200
+
 flask-sqlalchemy (2.1-4) unstable; urgency=medium
 
   [ Emmanuel Arias ]
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/control flask-sqlalchemy-2.5.1/debian/control
--- flask-sqlalchemy-2.1/debian/control	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/control	2021-05-11 20:29:21.000000000 +0200
@@ -1,19 +1,22 @@
 Source: flask-sqlalchemy
 Section: python
 Priority: optional
-Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
-Uploaders: Thomas Bechtold <thomasbechtold@jpberlin.de>
+Maintainer: Debian Python Team <team+python@tracker.debian.org>
+Uploaders:
+ Henning Sprang <henning.sprang@gmail.com>,
+ Carsten Schoenert <c.schoenert@t-online.de>
 Build-Depends:
- debhelper-compat (= 12),
+ debhelper-compat (= 13),
  dh-python,
  python3-all,
  python3-flask (>= 0.10),
  python3-setuptools,
  python3-sqlalchemy,
-Standards-Version: 4.4.0
-Homepage: https://pypi.python.org/pypi/Flask-SQLAlchemy
-Vcs-Git: https://salsa.debian.org/python-team/modules/flask-sqlalchemy.git
-Vcs-Browser: https://salsa.debian.org/python-team/modules/flask-sqlalchemy
+Rules-Requires-Root: no
+Standards-Version: 4.5.1
+Homepage: https://flask-sqlalchemy.palletsprojects.com
+Vcs-Git: https://salsa.debian.org/python-team/packages/flask-sqlalchemy.git
+Vcs-Browser: https://salsa.debian.org/python-team/packages/flask-sqlalchemy
 Testsuite: autopkgtest-pkg-python
 
 Package: python3-flask-sqlalchemy
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/copyright flask-sqlalchemy-2.5.1/debian/copyright
--- flask-sqlalchemy-2.1/debian/copyright	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/copyright	2021-04-29 19:25:09.000000000 +0200
@@ -3,7 +3,9 @@
 Source: https://pypi.python.org/pypi/Flask-SQLAlchemy/
 
 Files: *
-Copyright: 2010-2012 Armin Ronacher
+Copyright: 2010-2021 Armin Ronacher
+           2019-2021 David Lord <davidism@gmail.com>
+		   2019 Randy Syring <randy@thesyrings.us>
 License: BSD-3-Clause
 
 Files: flask_sqlalchemy/_compat.py
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/gbp.conf flask-sqlalchemy-2.5.1/debian/gbp.conf
--- flask-sqlalchemy-2.1/debian/gbp.conf	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/gbp.conf	2021-04-29 18:56:19.000000000 +0200
@@ -0,0 +1,14 @@
+# Configuration file for git-buildpackage and friends
+
+[DEFAULT]
+compression = gz
+debian-branch = debian/master
+upstream-branch = upstream
+pristine-tar = True
+
+[pq]
+patch-numbers = False
+
+[dch]
+id-length = 7
+debian-branch = debian/master
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/gitlab-ci.yml flask-sqlalchemy-2.5.1/debian/gitlab-ci.yml
--- flask-sqlalchemy-2.1/debian/gitlab-ci.yml	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/gitlab-ci.yml	2021-04-29 18:48:58.000000000 +0200
@@ -0,0 +1,5 @@
+---
+include:
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
+
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/python3-flask-sqlalchemy.docs flask-sqlalchemy-2.5.1/debian/python3-flask-sqlalchemy.docs
--- flask-sqlalchemy-2.1/debian/python3-flask-sqlalchemy.docs	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/python3-flask-sqlalchemy.docs	2021-05-11 20:54:08.000000000 +0200
@@ -0,0 +1,2 @@
+README.rst
+examples
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/python3-flask-sqlalchemy.lintian-overrides flask-sqlalchemy-2.5.1/debian/python3-flask-sqlalchemy.lintian-overrides
--- flask-sqlalchemy-2.1/debian/python3-flask-sqlalchemy.lintian-overrides	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/python3-flask-sqlalchemy.lintian-overrides	2021-05-11 20:54:08.000000000 +0200
@@ -0,0 +1,4 @@
+# This is expected here.
+python3-flask-sqlalchemy: duplicate-files usr/share/doc/python3-flask-sqlalchemy/examples/flaskr/flaskr/auth/__init__.py usr/share/doc/python3-flask-sqlalchemy/examples/flaskr/flaskr/blog/__init__.py
+# Also not critical, we have installed some examples.
+python3-flask-sqlalchemy: repeated-path-segment flaskr usr/share/doc/python3-flask-sqlalchemy/examples/flaskr/flaskr/
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/rules flask-sqlalchemy-2.5.1/debian/rules
--- flask-sqlalchemy-2.1/debian/rules	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/rules	2021-05-11 20:54:08.000000000 +0200
@@ -8,3 +8,7 @@
 
 %:
 	dh $@ --with python3 --buildsystem=pybuild
+
+override_dh_installdocs:
+	find examples -type f -name ".gitignore" -exec rm {} \;
+	dh_installdocs
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/upstream/metadata flask-sqlalchemy-2.5.1/debian/upstream/metadata
--- flask-sqlalchemy-2.1/debian/upstream/metadata	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/upstream/metadata	2021-04-29 18:48:58.000000000 +0200
@@ -0,0 +1,4 @@
+Bug-Database: https://github.com/pallets/flask-sqlalchemy/issues
+Bug-Submit: https://github.com/pallets/flask-sqlalchemy/issues/new
+Repository: https://github.com/pallets/flask-sqlalchemy.git
+Repository-Browse: https://github.com/pallets/flask-sqlalchemy
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/watch flask-sqlalchemy-2.5.1/debian/watch
--- flask-sqlalchemy-2.1/debian/watch	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/watch	2021-04-29 18:53:21.000000000 +0200
@@ -1,3 +1,6 @@
-version=3
+# uscan watch file for flask-sqlalchemy
+
+version=4
+
 https://pypi.debian.net/Flask-SQLAlchemy/Flask-SQLAlchemy-(.*)\.tar\.gz
 
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/flask_sqlalchemy/_compat.py flask-sqlalchemy-2.5.1/flask_sqlalchemy/_compat.py
--- flask-sqlalchemy-2.1/flask_sqlalchemy/_compat.py	2015-02-26 18:58:08.000000000 +0100
+++ flask-sqlalchemy-2.5.1/flask_sqlalchemy/_compat.py	2021-03-18 19:56:21.000000000 +0100
@@ -1,16 +1,5 @@
-# -*- coding: utf-8 -*-
-"""
-    flaskext.sqlalchemy._compat
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Internal Python 2.x/3.x compatibility layer.
-
-    :copyright: (c) 2013 by Daniel Neuhäuser
-    :license: BSD, see LICENSE for more details.
-"""
 import sys
 
-
 PY2 = sys.version_info[0] == 2
 
 
@@ -25,6 +14,15 @@
 
     string_types = (unicode, bytes)
 
+    def to_str(x, charset='utf8', errors='strict'):
+        if x is None or isinstance(x, str):
+            return x
+
+        if isinstance(x, unicode):
+            return x.encode(charset, errors)
+
+        return str(x)
+
 else:
     def iteritems(d):
         return iter(d.items())
@@ -34,4 +32,13 @@
 
     xrange = range
 
-    string_types = (str, )
+    string_types = (str,)
+
+    def to_str(x, charset='utf8', errors='strict'):
+        if x is None or isinstance(x, str):
+            return x
+
+        if isinstance(x, bytes):
+            return x.decode(charset, errors)
+
+        return str(x)
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/flask_sqlalchemy/__init__.py flask-sqlalchemy-2.5.1/flask_sqlalchemy/__init__.py
--- flask-sqlalchemy-2.1/flask_sqlalchemy/__init__.py	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/flask_sqlalchemy/__init__.py	2021-03-18 20:01:04.000000000 +0100
@@ -1,60 +1,81 @@
 # -*- coding: utf-8 -*-
-"""
-    flaskext.sqlalchemy
-    ~~~~~~~~~~~~~~~~~~~
-
-    Adds basic SQLAlchemy support to your application.
-
-    :copyright: (c) 2014 by Armin Ronacher, Daniel Neuhäuser.
-    :license: BSD, see LICENSE for more details.
-"""
-from __future__ import with_statement, absolute_import
+from __future__ import absolute_import
+
+import functools
 import os
-import re
 import sys
 import time
-import functools
 import warnings
-import sqlalchemy
 from math import ceil
-from functools import partial
-from flask import _request_ctx_stack, abort, has_request_context, request
-from flask.signals import Namespace
 from operator import itemgetter
 from threading import Lock
-from sqlalchemy import orm, event, inspect
+
+import sqlalchemy
+from flask import _app_ctx_stack, abort, current_app, request
+from flask.signals import Namespace
+from sqlalchemy import event, inspect, orm
+from sqlalchemy.engine.url import make_url
 from sqlalchemy.orm.exc import UnmappedClassError
 from sqlalchemy.orm.session import Session as SessionBase
-from sqlalchemy.engine.url import make_url
-from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
-from flask_sqlalchemy._compat import iteritems, itervalues, xrange, \
-     string_types
+
+from ._compat import itervalues, string_types, xrange
+from .model import DefaultMeta
+from .model import Model
+from . import utils
+
+try:
+    from sqlalchemy.orm import declarative_base
+    from sqlalchemy.orm import DeclarativeMeta
+except ImportError:
+    # SQLAlchemy <= 1.3
+    from sqlalchemy.ext.declarative import declarative_base
+    from sqlalchemy.ext.declarative import DeclarativeMeta
+
+# Scope the session to the current greenlet if greenlet is available,
+# otherwise fall back to the current thread.
+try:
+    from greenlet import getcurrent as _ident_func
+except ImportError:
+    try:
+        from threading import get_ident as _ident_func
+    except ImportError:
+        # Python 2.7
+        from thread import get_ident as _ident_func
+
+__version__ = "2.5.1"
 
 # the best timer function for the platform
 if sys.platform == 'win32':
-    _timer = time.clock
+    if sys.version_info >= (3, 3):
+        _timer = time.perf_counter
+    else:
+        _timer = time.clock
 else:
     _timer = time.time
 
-try:
-    from flask import _app_ctx_stack
-except ImportError:
-    _app_ctx_stack = None
+_signals = Namespace()
+models_committed = _signals.signal('models-committed')
+before_models_committed = _signals.signal('before-models-committed')
 
 
-__version__ = '2.1'
+def _sa_url_set(url, **kwargs):
+    try:
+        url = url.set(**kwargs)
+    except AttributeError:
+        # SQLAlchemy <= 1.3
+        for key, value in kwargs.items():
+            setattr(url, key, value)
 
+    return url
 
-# Which stack should we use?  _app_ctx_stack is new in 0.9
-connection_stack = _app_ctx_stack or _request_ctx_stack
 
+def _sa_url_query_setdefault(url, **kwargs):
+    query = dict(url.query)
 
-_camelcase_re = re.compile(r'([A-Z]+)(?=[a-z0-9])')
-_signals = Namespace()
+    for key, value in kwargs.items():
+        query.setdefault(key, value)
 
-
-models_committed = _signals.signal('models-committed')
-before_models_committed = _signals.signal('before-models-committed')
+    return _sa_url_set(url, query=query)
 
 
 def _make_table(db):
@@ -68,34 +89,34 @@
     return _make_table
 
 
-def _set_default_query_class(d):
+def _set_default_query_class(d, cls):
     if 'query_class' not in d:
-        d['query_class'] = BaseQuery
+        d['query_class'] = cls
 
 
-def _wrap_with_default_query_class(fn):
+def _wrap_with_default_query_class(fn, cls):
     @functools.wraps(fn)
     def newfn(*args, **kwargs):
-        _set_default_query_class(kwargs)
+        _set_default_query_class(kwargs, cls)
         if "backref" in kwargs:
             backref = kwargs['backref']
             if isinstance(backref, string_types):
                 backref = (backref, {})
-            _set_default_query_class(backref[1])
+            _set_default_query_class(backref[1], cls)
         return fn(*args, **kwargs)
     return newfn
 
 
-def _include_sqlalchemy(obj):
+def _include_sqlalchemy(obj, cls):
     for module in sqlalchemy, sqlalchemy.orm:
         for key in module.__all__:
             if not hasattr(obj, key):
                 setattr(obj, key, getattr(module, key))
     # Note: obj.Table does not attempt to be a SQLAlchemy Table class.
     obj.Table = _make_table(obj)
-    obj.relationship = _wrap_with_default_query_class(obj.relationship)
-    obj.relation = _wrap_with_default_query_class(obj.relation)
-    obj.dynamic_loader = _wrap_with_default_query_class(obj.dynamic_loader)
+    obj.relationship = _wrap_with_default_query_class(obj.relationship, cls)
+    obj.relation = _wrap_with_default_query_class(obj.relation, cls)
+    obj.dynamic_loader = _wrap_with_default_query_class(obj.dynamic_loader, cls)
     obj.event = event
 
 
@@ -148,12 +169,12 @@
         to an external transaction.
     """
 
-    def __init__(self, db, autocommit=False, autoflush=True, app=None, **options):
+    def __init__(self, db, autocommit=False, autoflush=True, **options):
         #: The application that this session belongs to.
         self.app = app = db.get_app()
         track_modifications = app.config['SQLALCHEMY_TRACK_MODIFICATIONS']
         bind = options.pop('bind', None) or db.engine
-        binds = options.pop('binds', None) or db.get_binds(app)
+        binds = options.pop('binds', db.get_binds(app))
 
         if track_modifications is None or track_modifications:
             _SessionSignalEvents.register(self)
@@ -164,9 +185,19 @@
         )
 
     def get_bind(self, mapper=None, clause=None):
+        """Return the engine or connection for a given model or
+        table, using the ``__bind_key__`` if it is set.
+        """
         # mapper is None if someone tries to just get a connection
         if mapper is not None:
-            info = getattr(mapper.mapped_table, 'info', {})
+            try:
+                # SA >= 1.3
+                persist_selectable = mapper.persist_selectable
+            except AttributeError:
+                # SA < 1.3
+                persist_selectable = mapper.mapped_table
+
+            info = getattr(persist_selectable, 'info', {})
             bind_key = info.get('bind_key')
             if bind_key is not None:
                 state = get_state(self.app)
@@ -242,32 +273,40 @@
 
 
 class _EngineDebuggingSignalEvents(object):
-    """Sets up handlers for two events that let us track the execution time of queries."""
+    """Sets up handlers for two events that let us track the execution time of
+    queries."""
 
     def __init__(self, engine, import_name):
         self.engine = engine
         self.app_package = import_name
 
     def register(self):
-        event.listen(self.engine, 'before_cursor_execute', self.before_cursor_execute)
-        event.listen(self.engine, 'after_cursor_execute', self.after_cursor_execute)
+        event.listen(
+            self.engine, 'before_cursor_execute', self.before_cursor_execute
+        )
+        event.listen(
+            self.engine, 'after_cursor_execute', self.after_cursor_execute
+        )
 
-    def before_cursor_execute(self, conn, cursor, statement,
-                              parameters, context, executemany):
-        if connection_stack.top is not None:
+    def before_cursor_execute(
+        self, conn, cursor, statement, parameters, context, executemany
+    ):
+        if current_app:
             context._query_start_time = _timer()
 
-    def after_cursor_execute(self, conn, cursor, statement,
-                             parameters, context, executemany):
-        ctx = connection_stack.top
-        if ctx is not None:
-            queries = getattr(ctx, 'sqlalchemy_queries', None)
-            if queries is None:
-                queries = []
-                setattr(ctx, 'sqlalchemy_queries', queries)
+    def after_cursor_execute(
+        self, conn, cursor, statement, parameters, context, executemany
+    ):
+        if current_app:
+            try:
+                queries = _app_ctx_stack.top.sqlalchemy_queries
+            except AttributeError:
+                queries = _app_ctx_stack.top.sqlalchemy_queries = []
+
             queries.append(_DebugQueryTuple((
                 statement, parameters, context._query_start_time, _timer(),
-                _calling_context(self.app_package))))
+                _calling_context(self.app_package)
+            )))
 
 
 def get_debug_queries():
@@ -302,7 +341,7 @@
         query was issued.  The exact format is undefined so don't try
         to reconstruct filename or function name.
     """
-    return getattr(connection_stack.top, 'sqlalchemy_queries', [])
+    return getattr(_app_ctx_stack.top, 'sqlalchemy_queries', [])
 
 
 class Pagination(object):
@@ -344,6 +383,8 @@
     @property
     def prev_num(self):
         """Number of the previous page."""
+        if not self.has_prev:
+            return None
         return self.page - 1
 
     @property
@@ -365,6 +406,8 @@
     @property
     def next_num(self):
         """Number of the next page"""
+        if not self.has_next:
+            return None
         return self.page + 1
 
     def iter_pages(self, left_edge=2, left_current=2,
@@ -395,7 +438,7 @@
         last = 0
         for num in xrange(1, self.pages + 1):
             if num <= left_edge or \
-               (num > self.page - left_current - 1 and \
+               (num > self.page - left_current - 1 and
                 num < self.page + right_current) or \
                num > self.pages - right_edge:
                 if last + 1 != num:
@@ -405,47 +448,50 @@
 
 
 class BaseQuery(orm.Query):
-    """The default query object used for models, and exposed as
-    :attr:`~SQLAlchemy.Query`. This can be subclassed and
-    replaced for individual models by setting the :attr:`~Model.query_class`
-    attribute.  This is a subclass of a standard SQLAlchemy
-    :class:`~sqlalchemy.orm.query.Query` class and has all the methods of a
-    standard query as well.
+    """SQLAlchemy :class:`~sqlalchemy.orm.query.Query` subclass with convenience methods for querying in a web application.
+
+    This is the default :attr:`~Model.query` object used for models, and exposed as :attr:`~SQLAlchemy.Query`.
+    Override the query class for an individual model by subclassing this and setting :attr:`~Model.query_class`.
     """
 
-    def get_or_404(self, ident):
-        """Like :meth:`get` but aborts with 404 if not found instead of
-        returning `None`.
-        """
+    def get_or_404(self, ident, description=None):
+        """Like :meth:`get` but aborts with 404 if not found instead of returning ``None``."""
+
         rv = self.get(ident)
         if rv is None:
-            abort(404)
+            abort(404, description=description)
         return rv
 
-    def first_or_404(self):
-        """Like :meth:`first` but aborts with 404 if not found instead of
-        returning `None`.
-        """
+    def first_or_404(self, description=None):
+        """Like :meth:`first` but aborts with 404 if not found instead of returning ``None``."""
+
         rv = self.first()
         if rv is None:
-            abort(404)
+            abort(404, description=description)
         return rv
 
-    def paginate(self, page=None, per_page=None, error_out=True):
-        """Returns `per_page` items from page `page`.  By default it will
-        abort with 404 if no items were found and the page was larger than
-        1.  This behavor can be disabled by setting `error_out` to `False`.
+    def paginate(self, page=None, per_page=None, error_out=True, max_per_page=None):
+        """Returns ``per_page`` items from page ``page``.
+
+        If ``page`` or ``per_page`` are ``None``, they will be retrieved from
+        the request query. If ``max_per_page`` is specified, ``per_page`` will
+        be limited to that value. If there is no request or they aren't in the
+        query, they default to 1 and 20 respectively.
+
+        When ``error_out`` is ``True`` (default), the following rules will
+        cause a 404 response:
 
-        If page or per_page are None, they will be retrieved from the
-        request query.  If the values are not ints and ``error_out`` is
-        true, it will abort with 404.  If there is no request or they
-        aren't in the query, they default to page 1 and 20
-        respectively.
+        * No items are found and ``page`` is not 1.
+        * ``page`` is less than 1, or ``per_page`` is negative.
+        * ``page`` or ``per_page`` are not ints.
 
-        Returns an :class:`Pagination` object.
+        When ``error_out`` is ``False``, ``page`` and ``per_page`` default to
+        1 and 20 respectively.
+
+        Returns a :class:`Pagination` object.
         """
 
-        if has_request_context():
+        if request:
             if page is None:
                 try:
                     page = int(request.args.get('page', 1))
@@ -470,26 +516,32 @@
             if per_page is None:
                 per_page = 20
 
-        if error_out and page < 1:
-            abort(404)
+        if max_per_page is not None:
+            per_page = min(per_page, max_per_page)
+
+        if page < 1:
+            if error_out:
+                abort(404)
+            else:
+                page = 1
+
+        if per_page < 0:
+            if error_out:
+                abort(404)
+            else:
+                per_page = 20
 
         items = self.limit(per_page).offset((page - 1) * per_page).all()
 
         if not items and page != 1 and error_out:
             abort(404)
 
-        # No need to count if we're on the first page and there are fewer
-        # items than we expected.
-        if page == 1 and len(items) < per_page:
-            total = len(items)
-        else:
-            total = self.order_by(None).count()
+        total = self.order_by(None).count()
 
         return Pagination(self, page, per_page, total, items)
 
 
 class _QueryProperty(object):
-
     def __init__(self, sa):
         self.sa = sa
 
@@ -536,79 +588,36 @@
             echo = self._app.config['SQLALCHEMY_ECHO']
             if (uri, echo) == self._connected_for:
                 return self._engine
-            info = make_url(uri)
-            options = {'convert_unicode': True}
-            self._sa.apply_pool_defaults(self._app, options)
-            self._sa.apply_driver_hacks(self._app, info, options)
-            if echo:
-                options['echo'] = True
-            self._engine = rv = sqlalchemy.create_engine(info, **options)
+
+            sa_url = make_url(uri)
+            sa_url, options = self.get_options(sa_url, echo)
+            self._engine = rv = self._sa.create_engine(sa_url, options)
+
             if _record_queries(self._app):
                 _EngineDebuggingSignalEvents(self._engine,
                                              self._app.import_name).register()
-            self._connected_for = (uri, echo)
-            return rv
-
 
-def _should_set_tablename(bases, d):
-    """Check what values are set by a class and its bases to determine if a
-    tablename should be automatically generated.
+            self._connected_for = (uri, echo)
 
-    The class and its bases are checked in order of precedence: the class
-    itself then each base in the order they were given at class definition.
+            return rv
 
-    Abstract classes do not generate a tablename, although they may have set
-    or inherited a tablename elsewhere.
+    def get_options(self, sa_url, echo):
+        options = {}
 
-    If a class defines a tablename or table, a new one will not be generated.
-    Otherwise, if the class defines a primary key, a new name will be generated.
+        options = self._sa.apply_pool_defaults(self._app, options)
+        sa_url, options = self._sa.apply_driver_hacks(self._app, sa_url, options)
 
-    This supports:
+        if echo:
+            options['echo'] = echo
 
-    * Joined table inheritance without explicitly naming sub-models.
-    * Single table inheritance.
-    * Inheriting from mixins or abstract models.
+        # Give the config options set by a developer explicitly priority
+        # over decisions FSA makes.
+        options.update(self._app.config['SQLALCHEMY_ENGINE_OPTIONS'])
 
-    :param bases: base classes of new class
-    :param d: new class dict
-    :return: True if tablename should be set
-    """
+        # Give options set in SQLAlchemy.__init__() ultimate priority
+        options.update(self._sa._engine_options)
 
-    if '__tablename__' in d or '__table__' in d or '__abstract__' in d:
-        return False
-
-    if any(v.primary_key for v in itervalues(d) if isinstance(v, sqlalchemy.Column)):
-        return True
-
-    for base in bases:
-        if hasattr(base, '__tablename__') or hasattr(base, '__table__'):
-            return False
-
-        for name in dir(base):
-            attr = getattr(base, name)
-
-            if isinstance(attr, sqlalchemy.Column) and attr.primary_key:
-                return True
-
-
-class _BoundDeclarativeMeta(DeclarativeMeta):
-
-    def __new__(cls, name, bases, d):
-        if _should_set_tablename(bases, d):
-            def _join(match):
-                word = match.group()
-                if len(word) > 1:
-                    return ('_%s_%s' % (word[:-1], word[-1])).lower()
-                return '_' + word.lower()
-            d['__tablename__'] = _camelcase_re.sub(_join, name).lstrip('_')
-
-        return DeclarativeMeta.__new__(cls, name, bases, d)
-
-    def __init__(self, name, bases, d):
-        bind_key = d.pop('__bind_key__', None)
-        DeclarativeMeta.__init__(self, name, bases, d)
-        if bind_key is not None:
-            self.__table__.info['bind_key'] = bind_key
+        return sa_url, options
 
 
 def get_state(app):
@@ -622,24 +631,11 @@
 class _SQLAlchemyState(object):
     """Remembers configuration for the (db, app) tuple."""
 
-    def __init__(self, db, app):
+    def __init__(self, db):
         self.db = db
-        self.app = app
         self.connectors = {}
 
 
-class Model(object):
-    """Baseclass for custom user models."""
-
-    #: the query class used.  The :attr:`query` attribute is an instance
-    #: of this class.  By default a :class:`BaseQuery` is used.
-    query_class = BaseQuery
-
-    #: an instance of :attr:`query_class`.  Can be used to query the
-    #: database for instances of this model.
-    query = None
-
-
 class SQLAlchemy(object):
     """This class is used to control the SQLAlchemy integration to one
     or more Flask applications.  Depending on how you initialize the
@@ -667,13 +663,18 @@
     the second case a :meth:`flask.Flask.app_context` has to exist.
 
     By default Flask-SQLAlchemy will apply some backend-specific settings
-    to improve your experience with them.  As of SQLAlchemy 0.6 SQLAlchemy
+    to improve your experience with them.
+
+    As of SQLAlchemy 0.6 SQLAlchemy
     will probe the library for native unicode support.  If it detects
     unicode it will let the library handle that, otherwise do that itself.
     Sometimes this detection can fail in which case you might want to set
-    `use_native_unicode` (or the ``SQLALCHEMY_NATIVE_UNICODE`` configuration
-    key) to `False`.  Note that the configuration key overrides the
-    value you pass to the constructor.
+    ``use_native_unicode`` (or the ``SQLALCHEMY_NATIVE_UNICODE`` configuration
+    key) to ``False``.  Note that the configuration key overrides the
+    value you pass to the constructor.  Direct support for ``use_native_unicode``
+    and SQLALCHEMY_NATIVE_UNICODE are deprecated as of v2.4 and will be removed
+    in v3.0.  ``engine_options`` and ``SQLALCHEMY_ENGINE_OPTIONS`` may be used
+    instead.
 
     This class also provides access to all the SQLAlchemy functions and classes
     from the :mod:`sqlalchemy` and :mod:`sqlalchemy.orm` modules.  So you can
@@ -697,26 +698,15 @@
        emulates `Table` behavior but is not a class. `db.Table` exposes the
        `Table` interface, but is a function which allows omission of metadata.
 
-    You may also define your own SessionExtension instances as well when
-    defining your SQLAlchemy class instance. You may pass your custom instances
-    to the `session_extensions` keyword. This can be either a single
-    SessionExtension instance, or a list of SessionExtension instances. In the
-    following use case we use the VersionedListener from the SQLAlchemy
-    versioning examples.::
-
-        from history_meta import VersionedMeta, VersionedListener
-
-        app = Flask(__name__)
-        db = SQLAlchemy(app, session_extensions=[VersionedListener()])
-
-        class User(db.Model):
-            __metaclass__ = VersionedMeta
-            username = db.Column(db.String(80), unique=True)
-            pw_hash = db.Column(db.String(80))
-
-    The `session_options` parameter can be used to override session
-    options.  If provided it's a dict of parameters passed to the
-    session's constructor.
+    The ``session_options`` parameter, if provided, is a dict of parameters
+    to be passed to the session constructor.  See :class:`~sqlalchemy.orm.session.Session`
+    for the standard options.
+
+    The ``engine_options`` parameter, if provided, is a dict of parameters
+    to be passed to create engine.  See :func:`~sqlalchemy.create_engine`
+    for the standard options.  The values given here will be merged with and
+    override anything set in the ``'SQLALCHEMY_ENGINE_OPTIONS'`` config
+    variable or othewise set by this library.
 
     .. versionadded:: 0.10
        The `session_options` parameter was added.
@@ -728,55 +718,125 @@
     .. versionadded:: 2.1
        The `metadata` parameter was added. This allows for setting custom
        naming conventions among other, non-trivial things.
-    """
 
-    def __init__(self, app=None, use_native_unicode=True, session_options=None, metadata=None):
+       The `query_class` parameter was added, to allow customisation
+       of the query class, in place of the default of :class:`BaseQuery`.
+
+       The `model_class` parameter was added, which allows a custom model
+       class to be used in place of :class:`Model`.
 
-        if session_options is None:
-            session_options = {}
+    .. versionchanged:: 2.1
+       Utilise the same query class across `session`, `Model.query` and `Query`.
+
+    .. versionadded:: 2.4
+       The `engine_options` parameter was added.
+
+    .. versionchanged:: 2.4
+       The `use_native_unicode` parameter was deprecated.
+
+    .. versionchanged:: 2.4.3
+        ``COMMIT_ON_TEARDOWN`` is deprecated and will be removed in
+        version 3.1. Call ``db.session.commit()`` directly instead.
+    """
+
+    #: Default query class used by :attr:`Model.query` and other queries.
+    #: Customize this by passing ``query_class`` to :func:`SQLAlchemy`.
+    #: Defaults to :class:`BaseQuery`.
+    Query = None
+
+    def __init__(self, app=None, use_native_unicode=True, session_options=None,
+                 metadata=None, query_class=BaseQuery, model_class=Model,
+                 engine_options=None):
 
-        session_options.setdefault('scopefunc', connection_stack.__ident_func__)
         self.use_native_unicode = use_native_unicode
+        self.Query = query_class
         self.session = self.create_scoped_session(session_options)
-        self.Model = self.make_declarative_base(metadata)
-        self.Query = BaseQuery
+        self.Model = self.make_declarative_base(model_class, metadata)
         self._engine_lock = Lock()
         self.app = app
-        _include_sqlalchemy(self)
+        self._engine_options = engine_options or {}
+        _include_sqlalchemy(self, query_class)
 
         if app is not None:
             self.init_app(app)
 
     @property
     def metadata(self):
-        """Returns the metadata"""
+        """The metadata associated with ``db.Model``."""
+
         return self.Model.metadata
 
     def create_scoped_session(self, options=None):
-        """Helper factory method that creates a scoped session.  It
-        internally calls :meth:`create_session`.
+        """Create a :class:`~sqlalchemy.orm.scoping.scoped_session`
+        on the factory from :meth:`create_session`.
+
+        An extra key ``'scopefunc'`` can be set on the ``options`` dict to
+        specify a custom scope function.  If it's not provided, Flask's app
+        context stack identity is used. This will ensure that sessions are
+        created and removed with the request/response cycle, and should be fine
+        in most cases.
+
+        :param options: dict of keyword arguments passed to session class  in
+            ``create_session``
         """
+
         if options is None:
             options = {}
-        scopefunc = options.pop('scopefunc', None)
-        return orm.scoped_session(partial(self.create_session, options),
-                                  scopefunc=scopefunc)
+
+        scopefunc = options.pop('scopefunc', _ident_func)
+        options.setdefault('query_cls', self.Query)
+        return orm.scoped_session(
+            self.create_session(options), scopefunc=scopefunc
+        )
 
     def create_session(self, options):
-        """Creates the session.  The default implementation returns a
-        :class:`SignallingSession`.
+        """Create the session factory used by :meth:`create_scoped_session`.
+
+        The factory **must** return an object that SQLAlchemy recognizes as a session,
+        or registering session events may raise an exception.
+
+        Valid factories include a :class:`~sqlalchemy.orm.session.Session`
+        class or a :class:`~sqlalchemy.orm.session.sessionmaker`.
 
-        .. versionadded:: 2.0
+        The default implementation creates a ``sessionmaker`` for :class:`SignallingSession`.
+
+        :param options: dict of keyword arguments passed to session class
         """
-        return SignallingSession(self, **options)
 
-    def make_declarative_base(self, metadata=None):
-        """Creates the declarative base."""
-        base = declarative_base(cls=Model, name='Model',
-                                metadata=metadata,
-                                metaclass=_BoundDeclarativeMeta)
-        base.query = _QueryProperty(self)
-        return base
+        return orm.sessionmaker(class_=SignallingSession, db=self, **options)
+
+    def make_declarative_base(self, model, metadata=None):
+        """Creates the declarative base that all models will inherit from.
+
+        :param model: base model class (or a tuple of base classes) to pass
+            to :func:`~sqlalchemy.ext.declarative.declarative_base`. Or a class
+            returned from ``declarative_base``, in which case a new base class
+            is not created.
+        :param metadata: :class:`~sqlalchemy.MetaData` instance to use, or
+            none to use SQLAlchemy's default.
+
+        .. versionchanged 2.3.0::
+            ``model`` can be an existing declarative base in order to support
+            complex customization such as changing the metaclass.
+        """
+        if not isinstance(model, DeclarativeMeta):
+            model = declarative_base(
+                cls=model,
+                name='Model',
+                metadata=metadata,
+                metaclass=DefaultMeta
+            )
+
+        # if user passed in a declarative base and a metaclass for some reason,
+        # make sure the base uses the metaclass
+        if metadata is not None and model.metadata is not metadata:
+            model.metadata = metadata
+
+        if not getattr(model, 'query_class', None):
+            model.query_class = self.Query
+
+        model.query = _QueryProperty(self)
+        return model
 
     def init_app(self, app):
         """This callback can be used to initialize an application for the
@@ -784,7 +844,16 @@
         of an application not initialized that way or connections will
         leak.
         """
-        app.config.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite://')
+        if (
+            'SQLALCHEMY_DATABASE_URI' not in app.config and
+            'SQLALCHEMY_BINDS' not in app.config
+        ):
+            warnings.warn(
+                'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
+                'Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".'
+            )
+
+        app.config.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite:///:memory:')
         app.config.setdefault('SQLALCHEMY_BINDS', None)
         app.config.setdefault('SQLALCHEMY_NATIVE_UNICODE', None)
         app.config.setdefault('SQLALCHEMY_ECHO', False)
@@ -794,36 +863,48 @@
         app.config.setdefault('SQLALCHEMY_POOL_RECYCLE', None)
         app.config.setdefault('SQLALCHEMY_MAX_OVERFLOW', None)
         app.config.setdefault('SQLALCHEMY_COMMIT_ON_TEARDOWN', False)
-        track_modifications = app.config.setdefault('SQLALCHEMY_TRACK_MODIFICATIONS', None)
+        track_modifications = app.config.setdefault(
+            'SQLALCHEMY_TRACK_MODIFICATIONS', None
+        )
+        app.config.setdefault('SQLALCHEMY_ENGINE_OPTIONS', {})
 
         if track_modifications is None:
-            warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.')
+            warnings.warn(FSADeprecationWarning(
+                'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
+                'will be disabled by default in the future.  Set it to True '
+                'or False to suppress this warning.'
+            ))
+
+        # Deprecation warnings for config keys that should be replaced by SQLALCHEMY_ENGINE_OPTIONS.
+        utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_SIZE', 'pool_size')
+        utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_TIMEOUT', 'pool_timeout')
+        utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_RECYCLE', 'pool_recycle')
+        utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_MAX_OVERFLOW', 'max_overflow')
 
-        if not hasattr(app, 'extensions'):
-            app.extensions = {}
-        app.extensions['sqlalchemy'] = _SQLAlchemyState(self, app)
-
-        # 0.9 and later
-        if hasattr(app, 'teardown_appcontext'):
-            teardown = app.teardown_appcontext
-        # 0.7 to 0.8
-        elif hasattr(app, 'teardown_request'):
-            teardown = app.teardown_request
-        # Older Flask versions
-        else:
-            if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:
-                raise RuntimeError("Commit on teardown requires Flask >= 0.7")
-            teardown = app.after_request
+        app.extensions['sqlalchemy'] = _SQLAlchemyState(self)
 
-        @teardown
+        @app.teardown_appcontext
         def shutdown_session(response_or_exc):
             if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:
+                warnings.warn(
+                    "'COMMIT_ON_TEARDOWN' is deprecated and will be"
+                    " removed in version 3.1. Call"
+                    " 'db.session.commit()'` directly instead.",
+                    DeprecationWarning,
+                )
+
                 if response_or_exc is None:
                     self.session.commit()
+
             self.session.remove()
             return response_or_exc
 
     def apply_pool_defaults(self, app, options):
+        """
+        .. versionchanged:: 2.5
+            Returns the ``options`` dict, for consistency with
+            :meth:`apply_driver_hacks`.
+        """
         def _setdefault(optionkey, configkey):
             value = app.config[configkey]
             if value is not None:
@@ -832,8 +913,9 @@
         _setdefault('pool_timeout', 'SQLALCHEMY_POOL_TIMEOUT')
         _setdefault('pool_recycle', 'SQLALCHEMY_POOL_RECYCLE')
         _setdefault('max_overflow', 'SQLALCHEMY_MAX_OVERFLOW')
+        return options
 
-    def apply_driver_hacks(self, app, info, options):
+    def apply_driver_hacks(self, app, sa_url, options):
         """This method is called before engine creation and used to inject
         driver specific hacks into the options.  The `options` parameter is
         a dictionary of keyword arguments that will then be used to call
@@ -842,18 +924,22 @@
         The default implementation provides some saner defaults for things
         like pool sizes for MySQL and sqlite.  Also it injects the setting of
         `SQLALCHEMY_NATIVE_UNICODE`.
+
+        .. versionchanged:: 2.5
+            Returns ``(sa_url, options)``. SQLAlchemy 1.4 made the URL
+            immutable, so any changes to it must now be passed back up
+            to the original caller.
         """
-        if info.drivername.startswith('mysql'):
-            info.query.setdefault('charset', 'utf8')
-            if info.drivername != 'mysql+gaerdbms':
+        if sa_url.drivername.startswith('mysql'):
+            sa_url = _sa_url_query_setdefault(sa_url, charset="utf8")
+
+            if sa_url.drivername != 'mysql+gaerdbms':
                 options.setdefault('pool_size', 10)
                 options.setdefault('pool_recycle', 7200)
-        elif info.drivername == 'sqlite':
+        elif sa_url.drivername == 'sqlite':
             pool_size = options.get('pool_size')
             detected_in_memory = False
-            # we go to memory and the pool size was explicitly set to 0
-            # which is fail.  Let the user know that
-            if info.database in (None, '', ':memory:'):
+            if sa_url.database in (None, '', ':memory:'):
                 detected_in_memory = True
                 from sqlalchemy.pool import StaticPool
                 options['poolclass'] = StaticPool
@@ -861,6 +947,8 @@
                     options['connect_args'] = {}
                 options['connect_args']['check_same_thread'] = False
 
+                # we go to memory and the pool size was explicitly set
+                # to 0 which is fail.  Let the user know that
                 if pool_size == 0:
                     raise RuntimeError('SQLite in memory database with an '
                                        'empty queue not possible due to data '
@@ -874,7 +962,9 @@
 
             # if it's not an in memory database we make the path absolute.
             if not detected_in_memory:
-                info.database = os.path.join(app.root_path, info.database)
+                sa_url = _sa_url_set(
+                    sa_url, database=os.path.join(app.root_path, sa_url.database)
+                )
 
         unu = app.config['SQLALCHEMY_NATIVE_UNICODE']
         if unu is None:
@@ -882,6 +972,21 @@
         if not unu:
             options['use_native_unicode'] = False
 
+        if app.config['SQLALCHEMY_NATIVE_UNICODE'] is not None:
+            warnings.warn(
+                "The 'SQLALCHEMY_NATIVE_UNICODE' config option is deprecated and will be removed in"
+                " v3.0.  Use 'SQLALCHEMY_ENGINE_OPTIONS' instead.",
+                DeprecationWarning
+            )
+        if not self.use_native_unicode:
+            warnings.warn(
+                "'use_native_unicode' is deprecated and will be removed in v3.0."
+                "  Use the 'engine_options' parameter instead.",
+                DeprecationWarning
+            )
+
+        return sa_url, options
+
     @property
     def engine(self):
         """Gives access to the engine.  If the database configuration is bound
@@ -890,38 +995,55 @@
         is used this might raise a :exc:`RuntimeError` if no application is
         active at the moment.
         """
-        return self.get_engine(self.get_app())
+        return self.get_engine()
 
-    def make_connector(self, app, bind=None):
+    def make_connector(self, app=None, bind=None):
         """Creates the connector for a given state and bind."""
-        return _EngineConnector(self, app, bind)
+        return _EngineConnector(self, self.get_app(app), bind)
 
-    def get_engine(self, app, bind=None):
-        """Returns a specific engine.
+    def get_engine(self, app=None, bind=None):
+        """Returns a specific engine."""
+
+        app = self.get_app(app)
+        state = get_state(app)
 
-        .. versionadded:: 0.12
-        """
         with self._engine_lock:
-            state = get_state(app)
             connector = state.connectors.get(bind)
+
             if connector is None:
                 connector = self.make_connector(app, bind)
                 state.connectors[bind] = connector
+
             return connector.get_engine()
 
-    def get_app(self, reference_app=None):
-        """Helper method that implements the logic to look up an application.
+    def create_engine(self, sa_url, engine_opts):
         """
+            Override this method to have final say over how the SQLAlchemy engine
+            is created.
+
+            In most cases, you will want to use ``'SQLALCHEMY_ENGINE_OPTIONS'``
+            config variable or set ``engine_options`` for :func:`SQLAlchemy`.
+        """
+        return sqlalchemy.create_engine(sa_url, **engine_opts)
+
+    def get_app(self, reference_app=None):
+        """Helper method that implements the logic to look up an
+        application."""
+
         if reference_app is not None:
             return reference_app
+
+        if current_app:
+            return current_app._get_current_object()
+
         if self.app is not None:
             return self.app
-        ctx = connection_stack.top
-        if ctx is not None:
-            return ctx.app
-        raise RuntimeError('application not registered on db '
-                           'instance and no application bound '
-                           'to current context')
+
+        raise RuntimeError(
+            'No application found. Either work inside a view function or push'
+            ' an application context. See'
+            ' http://flask-sqlalchemy.pocoo.org/contexts/.'
+        )
 
     def get_tables_for_bind(self, bind=None):
         """Returns a list of all tables relevant for a bind."""
@@ -988,14 +1110,23 @@
         self._execute_for_all_tables(app, bind, 'reflect', skip_tables=True)
 
     def __repr__(self):
-        app = None
-        if self.app is not None:
-            app = self.app
-        else:
-            ctx = connection_stack.top
-            if ctx is not None:
-                app = ctx.app
         return '<%s engine=%r>' % (
             self.__class__.__name__,
-            app and app.config['SQLALCHEMY_DATABASE_URI'] or None
+            self.engine.url if self.app or current_app else None
         )
+
+
+class _BoundDeclarativeMeta(DefaultMeta):
+    def __init__(cls, name, bases, d):
+        warnings.warn(FSADeprecationWarning(
+            '"_BoundDeclarativeMeta" has been renamed to "DefaultMeta". The'
+            ' old name will be removed in 3.0.'
+        ), stacklevel=3)
+        super(_BoundDeclarativeMeta, cls).__init__(name, bases, d)
+
+
+class FSADeprecationWarning(DeprecationWarning):
+    pass
+
+
+warnings.simplefilter('always', FSADeprecationWarning)
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/flask_sqlalchemy/model.py flask-sqlalchemy-2.5.1/flask_sqlalchemy/model.py
--- flask-sqlalchemy-2.1/flask_sqlalchemy/model.py	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/flask_sqlalchemy/model.py	2021-03-18 19:56:21.000000000 +0100
@@ -0,0 +1,154 @@
+import re
+
+import sqlalchemy as sa
+from sqlalchemy import inspect
+from sqlalchemy.ext.declarative import DeclarativeMeta, declared_attr
+from sqlalchemy.schema import _get_table_key
+
+from ._compat import to_str
+
+
+def should_set_tablename(cls):
+    """Determine whether ``__tablename__`` should be automatically generated
+    for a model.
+
+    * If no class in the MRO sets a name, one should be generated.
+    * If a declared attr is found, it should be used instead.
+    * If a name is found, it should be used if the class is a mixin, otherwise
+      one should be generated.
+    * Abstract models should not have one generated.
+
+    Later, :meth:`._BoundDeclarativeMeta.__table_cls__` will determine if the
+    model looks like single or joined-table inheritance. If no primary key is
+    found, the name will be unset.
+    """
+    if (
+        cls.__dict__.get('__abstract__', False)
+        or not any(isinstance(b, DeclarativeMeta) for b in cls.__mro__[1:])
+    ):
+        return False
+
+    for base in cls.__mro__:
+        if '__tablename__' not in base.__dict__:
+            continue
+
+        if isinstance(base.__dict__['__tablename__'], declared_attr):
+            return False
+
+        return not (
+            base is cls
+            or base.__dict__.get('__abstract__', False)
+            or not isinstance(base, DeclarativeMeta)
+        )
+
+    return True
+
+
+camelcase_re = re.compile(r'([A-Z]+)(?=[a-z0-9])')
+
+
+def camel_to_snake_case(name):
+    def _join(match):
+        word = match.group()
+
+        if len(word) > 1:
+            return ('_%s_%s' % (word[:-1], word[-1])).lower()
+
+        return '_' + word.lower()
+
+    return camelcase_re.sub(_join, name).lstrip('_')
+
+
+class NameMetaMixin(type):
+    def __init__(cls, name, bases, d):
+        if should_set_tablename(cls):
+            cls.__tablename__ = camel_to_snake_case(cls.__name__)
+
+        super(NameMetaMixin, cls).__init__(name, bases, d)
+
+        # __table_cls__ has run at this point
+        # if no table was created, use the parent table
+        if (
+            '__tablename__' not in cls.__dict__
+            and '__table__' in cls.__dict__
+            and cls.__dict__['__table__'] is None
+        ):
+            del cls.__table__
+
+    def __table_cls__(cls, *args, **kwargs):
+        """This is called by SQLAlchemy during mapper setup. It determines the
+        final table object that the model will use.
+
+        If no primary key is found, that indicates single-table inheritance,
+        so no table will be created and ``__tablename__`` will be unset.
+        """
+        # check if a table with this name already exists
+        # allows reflected tables to be applied to model by name
+        key = _get_table_key(args[0], kwargs.get('schema'))
+
+        if key in cls.metadata.tables:
+            return sa.Table(*args, **kwargs)
+
+        # if a primary key or constraint is found, create a table for
+        # joined-table inheritance
+        for arg in args:
+            if (
+                (isinstance(arg, sa.Column) and arg.primary_key)
+                or isinstance(arg, sa.PrimaryKeyConstraint)
+            ):
+                return sa.Table(*args, **kwargs)
+
+        # if no base classes define a table, return one
+        # ensures the correct error shows up when missing a primary key
+        for base in cls.__mro__[1:-1]:
+            if '__table__' in base.__dict__:
+                break
+        else:
+            return sa.Table(*args, **kwargs)
+
+        # single-table inheritance, use the parent tablename
+        if '__tablename__' in cls.__dict__:
+            del cls.__tablename__
+
+
+class BindMetaMixin(type):
+    def __init__(cls, name, bases, d):
+        bind_key = (
+            d.pop('__bind_key__', None)
+            or getattr(cls, '__bind_key__', None)
+        )
+
+        super(BindMetaMixin, cls).__init__(name, bases, d)
+
+        if bind_key is not None and getattr(cls, '__table__', None) is not None:
+            cls.__table__.info['bind_key'] = bind_key
+
+
+class DefaultMeta(NameMetaMixin, BindMetaMixin, DeclarativeMeta):
+    pass
+
+
+class Model(object):
+    """Base class for SQLAlchemy declarative base model.
+
+    To define models, subclass :attr:`db.Model <SQLAlchemy.Model>`, not this
+    class. To customize ``db.Model``, subclass this and pass it as
+    ``model_class`` to :class:`SQLAlchemy`.
+    """
+
+    #: Query class used by :attr:`query`. Defaults to
+    # :class:`SQLAlchemy.Query`, which defaults to :class:`BaseQuery`.
+    query_class = None
+
+    #: Convenience property to query the database for instances of this model
+    # using the current session. Equivalent to ``db.session.query(Model)``
+    # unless :attr:`query_class` has been changed.
+    query = None
+
+    def __repr__(self):
+        identity = inspect(self).identity
+        if identity is None:
+            pk = "(transient {0})".format(id(self))
+        else:
+            pk = ', '.join(to_str(value) for value in identity)
+        return '<{0} {1}>'.format(type(self).__name__, pk)
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/flask_sqlalchemy/utils.py flask-sqlalchemy-2.5.1/flask_sqlalchemy/utils.py
--- flask-sqlalchemy-2.1/flask_sqlalchemy/utils.py	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/flask_sqlalchemy/utils.py	2021-03-18 19:56:21.000000000 +0100
@@ -0,0 +1,45 @@
+import warnings
+
+import sqlalchemy
+
+
+def parse_version(v):
+    """
+        Take a string version and conver it to a tuple (for easier comparison), e.g.:
+
+            "1.2.3" --> (1, 2, 3)
+            "1.2" --> (1, 2, 0)
+            "1" --> (1, 0, 0)
+    """
+    parts = v.split(".")
+    # Pad the list to make sure there is three elements so that we get major, minor, point
+    # comparisons that default to "0" if not given.  I.e. "1.2" --> (1, 2, 0)
+    parts = (parts + 3 * ['0'])[:3]
+    return tuple(int(x) for x in parts)
+
+
+def sqlalchemy_version(op, val):
+    sa_ver = parse_version(sqlalchemy.__version__)
+    target_ver = parse_version(val)
+
+    assert op in ('<', '>', '<=', '>=', '=='), 'op {} not supported'.format(op)
+
+    if op == '<':
+        return sa_ver < target_ver
+    if op == '>':
+        return sa_ver > target_ver
+    if op == '<=':
+        return sa_ver <= target_ver
+    if op == '>=':
+        return sa_ver >= target_ver
+    return sa_ver == target_ver
+
+
+def engine_config_warning(config, version, deprecated_config_key, engine_option):
+    if config[deprecated_config_key] is not None:
+        warnings.warn(
+            'The `{}` config option is deprecated and will be removed in'
+            ' v{}.  Use `SQLALCHEMY_ENGINE_OPTIONS[\'{}\']` instead.'
+            .format(deprecated_config_key, version, engine_option),
+            DeprecationWarning
+        )
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/not-zip-safe flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/not-zip-safe
--- flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/not-zip-safe	2015-03-26 17:15:10.000000000 +0100
+++ flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/not-zip-safe	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/PKG-INFO flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/PKG-INFO
--- flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/PKG-INFO	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/PKG-INFO	2021-03-18 20:02:18.000000000 +0100
@@ -1,36 +1,90 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: Flask-SQLAlchemy
-Version: 2.1
-Summary: Adds SQLAlchemy support to your Flask application
-Home-page: http://github.com/mitsuhiko/flask-sqlalchemy
-Author: Phil Howell
-Author-email: phil@quae.co.uk
-License: BSD
-Description: 
-        Flask-SQLAlchemy
+Version: 2.5.1
+Summary: Adds SQLAlchemy support to your Flask application.
+Home-page: https://github.com/pallets/flask-sqlalchemy
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+Maintainer: Pallets
+Maintainer-email: contact@palletsprojects.com
+License: BSD-3-Clause
+Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com/
+Project-URL: Code, https://github.com/pallets/flask-sqlalchemy
+Project-URL: Issue tracker, https://github.com/pallets/flask-sqlalchemy/issues
+Description: Flask-SQLAlchemy
+        ================
+        
+        Flask-SQLAlchemy is an extension for `Flask`_ that adds support for
+        `SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy
+        with Flask by providing useful defaults and extra helpers that make it
+        easier to accomplish common tasks.
+        
+        
+        Installing
+        ----------
+        
+        Install and update using `pip`_:
+        
+        .. code-block:: text
+        
+          $ pip install -U Flask-SQLAlchemy
+        
+        
+        A Simple Example
         ----------------
         
-        Adds SQLAlchemy support to your Flask application.
+        .. code-block:: python
+        
+            from flask import Flask
+            from flask_sqlalchemy import SQLAlchemy
+        
+            app = Flask(__name__)
+            app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite"
+            db = SQLAlchemy(app)
+        
+        
+            class User(db.Model):
+                id = db.Column(db.Integer, primary_key=True)
+                username = db.Column(db.String, unique=True, nullable=False)
+                email = db.Column(db.String, unique=True, nullable=False)
+        
+        
+            db.session.add(User(name="Flask", email="example@example.com"))
+            db.session.commit()
+        
+            users = User.query.all()
+        
         
         Links
-        `````
+        -----
         
-        * `documentation <http://flask-sqlalchemy.pocoo.org>`_
-        * `development version
-          <http://github.com/mitsuhiko/flask-sqlalchemy/zipball/master#egg=Flask-SQLAlchemy-dev>`_
+        -   Documentation: https://flask-sqlalchemy.palletsprojects.com/
+        -   Releases: https://pypi.org/project/Flask-SQLAlchemy/
+        -   Code: https://github.com/pallets/flask-sqlalchemy
+        -   Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues
+        -   Test status: https://travis-ci.org/pallets/flask-sqlalchemy
+        -   Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy
         
+        .. _Flask: https://palletsprojects.com/p/flask/
+        .. _SQLAlchemy: https://www.sqlalchemy.org
+        .. _pip: https://pip.pypa.io/en/stable/quickstart/
         
-Platform: any
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
-Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/requires.txt flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/requires.txt
--- flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/requires.txt	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/requires.txt	2021-03-18 20:02:18.000000000 +0100
@@ -1,2 +1,2 @@
 Flask>=0.10
-SQLAlchemy>=0.7
\ Kein Zeilenumbruch am Dateiende.
+SQLAlchemy>=0.8.0
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/SOURCES.txt flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/SOURCES.txt
--- flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/SOURCES.txt	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/SOURCES.txt	2021-03-18 20:02:18.000000000 +0100
@@ -1,35 +1,80 @@
-CHANGES
-LICENSE
+CHANGES.rst
+LICENSE.rst
 MANIFEST.in
-README
+README.rst
 setup.cfg
 setup.py
-test_sqlalchemy.py
+tox.ini
 Flask_SQLAlchemy.egg-info/PKG-INFO
 Flask_SQLAlchemy.egg-info/SOURCES.txt
 Flask_SQLAlchemy.egg-info/dependency_links.txt
-Flask_SQLAlchemy.egg-info/not-zip-safe
 Flask_SQLAlchemy.egg-info/requires.txt
 Flask_SQLAlchemy.egg-info/top_level.txt
+artwork/flask-sqlalchemy.svg
 docs/Makefile
 docs/api.rst
 docs/binds.rst
 docs/changelog.rst
 docs/conf.py
 docs/config.rst
-docs/contents.rst.inc
 docs/contexts.rst
-docs/flaskstyle.sty
+docs/customizing.rst
 docs/index.rst
-docs/logo.pdf
+docs/license.rst
 docs/make.bat
 docs/models.rst
 docs/queries.rst
 docs/quickstart.rst
+docs/requirements.txt
 docs/signals.rst
-docs/_static/flask-sqlalchemy-small.png
-docs/_static/flask-sqlalchemy.png
-docs/_templates/sidebarintro.html
-docs/_templates/sidebarlogo.html
+docs/_static/flask-sqlalchemy-logo.png
+docs/_static/flask-sqlalchemy-title.png
+examples/flaskr/.gitignore
+examples/flaskr/LICENSE.rst
+examples/flaskr/MANIFEST.in
+examples/flaskr/README.rst
+examples/flaskr/setup.cfg
+examples/flaskr/setup.py
+examples/flaskr/flaskr/__init__.py
+examples/flaskr/flaskr/auth/__init__.py
+examples/flaskr/flaskr/auth/models.py
+examples/flaskr/flaskr/auth/views.py
+examples/flaskr/flaskr/blog/__init__.py
+examples/flaskr/flaskr/blog/models.py
+examples/flaskr/flaskr/blog/views.py
+examples/flaskr/flaskr/static/style.css
+examples/flaskr/flaskr/templates/base.html
+examples/flaskr/flaskr/templates/auth/login.html
+examples/flaskr/flaskr/templates/auth/register.html
+examples/flaskr/flaskr/templates/blog/create.html
+examples/flaskr/flaskr/templates/blog/index.html
+examples/flaskr/flaskr/templates/blog/update.html
+examples/flaskr/tests/conftest.py
+examples/flaskr/tests/test_auth.py
+examples/flaskr/tests/test_blog.py
+examples/flaskr/tests/test_init.py
+examples/hello/hello.cfg
+examples/hello/hello.py
+examples/hello/templates/layout.html
+examples/hello/templates/new.html
+examples/hello/templates/show_all.html
 flask_sqlalchemy/__init__.py
-flask_sqlalchemy/_compat.py
\ Kein Zeilenumbruch am Dateiende.
+flask_sqlalchemy/_compat.py
+flask_sqlalchemy/model.py
+flask_sqlalchemy/utils.py
+tests/conftest.py
+tests/test_basic_app.py
+tests/test_binds.py
+tests/test_commit_on_teardown.py
+tests/test_config.py
+tests/test_meta_data.py
+tests/test_model_class.py
+tests/test_pagination.py
+tests/test_query_class.py
+tests/test_query_property.py
+tests/test_regressions.py
+tests/test_sessions.py
+tests/test_signals.py
+tests/test_sqlalchemy_includes.py
+tests/test_table_name.py
+tests/test_utils.py
\ Kein Zeilenumbruch am Dateiende.
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/LICENSE flask-sqlalchemy-2.5.1/LICENSE
--- flask-sqlalchemy-2.1/LICENSE	2015-02-26 18:58:08.000000000 +0100
+++ flask-sqlalchemy-2.5.1/LICENSE	1970-01-01 01:00:00.000000000 +0100
@@ -1,31 +0,0 @@
-Copyright (c) 2014 by Armin Ronacher.
-
-Some rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-* Redistributions of source code must retain the above copyright
-  notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the following
-  disclaimer in the documentation and/or other materials provided
-  with the distribution.
-
-* The names of the contributors may not be used to endorse or
-  promote products derived from this software without specific
-  prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/LICENSE.rst flask-sqlalchemy-2.5.1/LICENSE.rst
--- flask-sqlalchemy-2.1/LICENSE.rst	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/LICENSE.rst	2020-05-27 16:51:59.000000000 +0200
@@ -0,0 +1,28 @@
+Copyright 2010 Pallets
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1.  Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+2.  Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+3.  Neither the name of the copyright holder nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/MANIFEST.in flask-sqlalchemy-2.5.1/MANIFEST.in
--- flask-sqlalchemy-2.1/MANIFEST.in	2015-02-26 18:58:08.000000000 +0100
+++ flask-sqlalchemy-2.5.1/MANIFEST.in	2021-03-18 19:56:21.000000000 +0100
@@ -1,6 +1,8 @@
-include LICENSE CHANGES *.py
-recursive-include docs *
-recursive-exclude docs *.pyc
-recursive-exclude docs *.pyo
+include CHANGES.rst
+include tox.ini
+graft artwork
+graft docs
 prune docs/_build
-prune docs/_themes/.git
+graft examples
+graft tests
+global-exclude *.pyc
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/PKG-INFO flask-sqlalchemy-2.5.1/PKG-INFO
--- flask-sqlalchemy-2.1/PKG-INFO	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/PKG-INFO	2021-03-18 20:02:18.032466000 +0100
@@ -1,36 +1,90 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: Flask-SQLAlchemy
-Version: 2.1
-Summary: Adds SQLAlchemy support to your Flask application
-Home-page: http://github.com/mitsuhiko/flask-sqlalchemy
-Author: Phil Howell
-Author-email: phil@quae.co.uk
-License: BSD
-Description: 
-        Flask-SQLAlchemy
+Version: 2.5.1
+Summary: Adds SQLAlchemy support to your Flask application.
+Home-page: https://github.com/pallets/flask-sqlalchemy
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+Maintainer: Pallets
+Maintainer-email: contact@palletsprojects.com
+License: BSD-3-Clause
+Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com/
+Project-URL: Code, https://github.com/pallets/flask-sqlalchemy
+Project-URL: Issue tracker, https://github.com/pallets/flask-sqlalchemy/issues
+Description: Flask-SQLAlchemy
+        ================
+        
+        Flask-SQLAlchemy is an extension for `Flask`_ that adds support for
+        `SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy
+        with Flask by providing useful defaults and extra helpers that make it
+        easier to accomplish common tasks.
+        
+        
+        Installing
+        ----------
+        
+        Install and update using `pip`_:
+        
+        .. code-block:: text
+        
+          $ pip install -U Flask-SQLAlchemy
+        
+        
+        A Simple Example
         ----------------
         
-        Adds SQLAlchemy support to your Flask application.
+        .. code-block:: python
+        
+            from flask import Flask
+            from flask_sqlalchemy import SQLAlchemy
+        
+            app = Flask(__name__)
+            app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite"
+            db = SQLAlchemy(app)
+        
+        
+            class User(db.Model):
+                id = db.Column(db.Integer, primary_key=True)
+                username = db.Column(db.String, unique=True, nullable=False)
+                email = db.Column(db.String, unique=True, nullable=False)
+        
+        
+            db.session.add(User(name="Flask", email="example@example.com"))
+            db.session.commit()
+        
+            users = User.query.all()
+        
         
         Links
-        `````
+        -----
         
-        * `documentation <http://flask-sqlalchemy.pocoo.org>`_
-        * `development version
-          <http://github.com/mitsuhiko/flask-sqlalchemy/zipball/master#egg=Flask-SQLAlchemy-dev>`_
+        -   Documentation: https://flask-sqlalchemy.palletsprojects.com/
+        -   Releases: https://pypi.org/project/Flask-SQLAlchemy/
+        -   Code: https://github.com/pallets/flask-sqlalchemy
+        -   Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues
+        -   Test status: https://travis-ci.org/pallets/flask-sqlalchemy
+        -   Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy
         
+        .. _Flask: https://palletsprojects.com/p/flask/
+        .. _SQLAlchemy: https://www.sqlalchemy.org
+        .. _pip: https://pip.pypa.io/en/stable/quickstart/
         
-Platform: any
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
-Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/README flask-sqlalchemy-2.5.1/README
--- flask-sqlalchemy-2.1/README	2015-07-28 21:15:32.000000000 +0200
+++ flask-sqlalchemy-2.5.1/README	1970-01-01 01:00:00.000000000 +0100
@@ -1,47 +0,0 @@
-
-                          // Flask-SQLAlchemy //
-
-
-    ~ What is Flask-SQLAlchemy?
-
-      Flask-SQLAlchemy is a Flask microframework extension which adds
-      support for the SQLAlchemy SQL toolkit/ORM.
-
-    ~ What's the latest version?
-
-      2.0 is the most recent stable version. 2.1 is slated for release in early February. 
-      
-    ~ What do I need?
-
-      SQLAlchemy, and Flask 0.10 or later. `pip` or `easy_install` will
-      install them for you if you do `pip install Flask-SQLAlchemy`.
-      We encourage you to use a virtualenv. Check the docs for complete
-      installation and usage instructions.
-
-    ~ Where are the docs?
-
-      Go to http://flask-sqlalchemy.pocoo.org/ for a prebuilt version
-      of the current documentation.  Otherwise build them yourself
-      from the sphinx sources in the docs folder.
-
-    ~ Where are the tests?
-
-      Good that you're asking. To run the tests use the
-      `test_sqlalchemy.py` file:
-
-        $ python test_sqlalchemy.py
-
-      If you just want one particular testcase to run you can provide
-      it on the command line:
-
-        $ python test_sqlalchemy.py PaginationTestCase
-        
-      In case you have `tox` installed, you can also run that to have
-      virtualenvs created automatically and tests run inside of them
-      at your convenience. Running just `tox` is enough:
-      
-        $ tox
-
-    ~ Where can I get help?
-
-      Join us on the #pocoo IRC channel on irc.freenode.net.
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/README.rst flask-sqlalchemy-2.5.1/README.rst
--- flask-sqlalchemy-2.1/README.rst	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/README.rst	2021-03-18 19:56:21.000000000 +0100
@@ -0,0 +1,57 @@
+Flask-SQLAlchemy
+================
+
+Flask-SQLAlchemy is an extension for `Flask`_ that adds support for
+`SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy
+with Flask by providing useful defaults and extra helpers that make it
+easier to accomplish common tasks.
+
+
+Installing
+----------
+
+Install and update using `pip`_:
+
+.. code-block:: text
+
+  $ pip install -U Flask-SQLAlchemy
+
+
+A Simple Example
+----------------
+
+.. code-block:: python
+
+    from flask import Flask
+    from flask_sqlalchemy import SQLAlchemy
+
+    app = Flask(__name__)
+    app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite"
+    db = SQLAlchemy(app)
+
+
+    class User(db.Model):
+        id = db.Column(db.Integer, primary_key=True)
+        username = db.Column(db.String, unique=True, nullable=False)
+        email = db.Column(db.String, unique=True, nullable=False)
+
+
+    db.session.add(User(name="Flask", email="example@example.com"))
+    db.session.commit()
+
+    users = User.query.all()
+
+
+Links
+-----
+
+-   Documentation: https://flask-sqlalchemy.palletsprojects.com/
+-   Releases: https://pypi.org/project/Flask-SQLAlchemy/
+-   Code: https://github.com/pallets/flask-sqlalchemy
+-   Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues
+-   Test status: https://travis-ci.org/pallets/flask-sqlalchemy
+-   Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy
+
+.. _Flask: https://palletsprojects.com/p/flask/
+.. _SQLAlchemy: https://www.sqlalchemy.org
+.. _pip: https://pip.pypa.io/en/stable/quickstart/
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/setup.cfg flask-sqlalchemy-2.5.1/setup.cfg
--- flask-sqlalchemy-2.1/setup.cfg	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/setup.cfg	2021-03-18 20:02:18.032466000 +0100
@@ -1,11 +1,25 @@
-[egg_info]
-tag_date = 0
-tag_build = 
-tag_svn_revision = 0
+[metadata]
+license_file = LICENSE.rst
+
+[bdist_wheel]
+universal = true
+
+[tool:pytest]
+testpaths = tests
 
-[aliases]
-release = egg_info -RDb ''
+[coverage:run]
+branch = True
+source = 
+	flask_sqlalchemy
+	tests
 
-[pytest]
-norecursedirs = .* _* scripts {args}
+[coverage:paths]
+source = 
+	flask_sqlalchemy
+	.tox/*/lib/python*/site-packages/flask_sqlalchemy
+	.tox/*/site-packages/flask_sqlalchemy
+
+[egg_info]
+tag_build = 
+tag_date = 0
 
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/setup.py flask-sqlalchemy-2.5.1/setup.py
--- flask-sqlalchemy-2.1/setup.py	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/setup.py	2021-03-18 19:56:21.000000000 +0100
@@ -1,52 +1,51 @@
-"""
-Flask-SQLAlchemy
-----------------
+import io
+import re
 
-Adds SQLAlchemy support to your Flask application.
-
-Links
-`````
-
-* `documentation <http://flask-sqlalchemy.pocoo.org>`_
-* `development version
-  <http://github.com/mitsuhiko/flask-sqlalchemy/zipball/master#egg=Flask-SQLAlchemy-dev>`_
-
-"""
 from setuptools import setup
 
+with io.open("README.rst", "rt", encoding="utf8") as f:
+    readme = f.read()
+
+with io.open("flask_sqlalchemy/__init__.py", "rt", encoding="utf8") as f:
+    version = re.search(r'__version__ = "(.*?)"', f.read(), re.M).group(1)
 
 setup(
-    name='Flask-SQLAlchemy',
-    version='2.1',
-    url='http://github.com/mitsuhiko/flask-sqlalchemy',
-    license='BSD',
-    author='Armin Ronacher',
-    author_email='armin.ronacher@active-4.com',
-    maintainer='Phil Howell',
-    maintainer_email='phil@quae.co.uk',
-    description='Adds SQLAlchemy support to your Flask application',
-    long_description=__doc__,
-    packages=['flask_sqlalchemy'],
-    zip_safe=False,
-    platforms='any',
-    install_requires=[
-        'Flask>=0.10',
-        'SQLAlchemy>=0.7'
-    ],
-    test_suite='test_sqlalchemy.suite',
+    name="el project website
+    The PyPi project webspace isn't the source for the upstream data, the
+    real home is within the palletsprojects.com umbrella.
+  * [e31076d] d/control: Adding entry Rules-Requires-Root: no
+  * [b68a975] *.docs: Also installing the provided examples folder
+  * [1f17669] d/rules: Clean out .gitignore file within examples
+  * [f885eef] lintian-overides: Ignore some files in examples
+
+ -- Carsten Schoenert <c.schoenert@t-online.de>  Tue, 11 May 2021 20:54:11 +0200
+
 flask-sqlalchemy (2.1-4) unstable; urgency=medium
 
   [ Emmanuel Arias ]
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/control flask-sqlalchemy-2.5.1/debian/control
--- flask-sqlalchemy-2.1/debian/control	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/control	2021-05-11 20:29:21.000000000 +0200
@@ -1,19 +1,22 @@
 Source: flask-sqlalchemy
 Section: python
 Priority: optional
-Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
-Uploaders: Thomas Bechtold <thomasbechtold@jpberlin.de>
+Maintainer: Debian Python Team <team+python@tracker.debian.org>
+Uploaders:
+ Henning Sprang <henning.sprang@gmail.com>,
+ Carsten Schoenert <c.schoenert@t-online.de>
 Build-Depends:
- debhelper-compat (= 12),
+ debhelper-compat (= 13),
  dh-python,
  python3-all,
  python3-flask (>= 0.10),
  python3-setuptools,
  python3-sqlalchemy,
-Standards-Version: 4.4.0
-Homepage: https://pypi.python.org/pypi/Flask-SQLAlchemy
-Vcs-Git: https://salsa.debian.org/python-team/modules/flask-sqlalchemy.git
-Vcs-Browser: https://salsa.debian.org/python-team/modules/flask-sqlalchemy
+Rules-Requires-Root: no
+Standards-Version: 4.5.1
+Homepage: https://flask-sqlalchemy.palletsprojects.com
+Vcs-Git: https://salsa.debian.org/python-team/packages/flask-sqlalchemy.git
+Vcs-Browser: https://salsa.debian.org/python-team/packages/flask-sqlalchemy
 Testsuite: autopkgtest-pkg-python
 
 Package: python3-flask-sqlalchemy
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/copyright flask-sqlalchemy-2.5.1/debian/copyright
--- flask-sqlalchemy-2.1/debian/copyright	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/copyright	2021-04-29 19:25:09.000000000 +0200
@@ -3,7 +3,9 @@
 Source: https://pypi.python.org/pypi/Flask-SQLAlchemy/
 
 Files: *
-Copyright: 2010-2012 Armin Ronacher
+Copyright: 2010-2021 Armin Ronacher
+           2019-2021 David Lord <davidism@gmail.com>
+		   2019 Randy Syring <randy@thesyrings.us>
 License: BSD-3-Clause
 
 Files: flask_sqlalchemy/_compat.py
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/gbp.conf flask-sqlalchemy-2.5.1/debian/gbp.conf
--- flask-sqlalchemy-2.1/debian/gbp.conf	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/gbp.conf	2021-04-29 18:56:19.000000000 +0200
@@ -0,0 +1,14 @@
+# Configuration file for git-buildpackage and friends
+
+[DEFAULT]
+compression = gz
+debian-branch = debian/master
+upstream-branch = upstream
+pristine-tar = True
+
+[pq]
+patch-numbers = False
+
+[dch]
+id-length = 7
+debian-branch = debian/master
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/gitlab-ci.yml flask-sqlalchemy-2.5.1/debian/gitlab-ci.yml
--- flask-sqlalchemy-2.1/debian/gitlab-ci.yml	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/gitlab-ci.yml	2021-04-29 18:48:58.000000000 +0200
@@ -0,0 +1,5 @@
+---
+include:
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
+
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/python3-flask-sqlalchemy.docs flask-sqlalchemy-2.5.1/debian/python3-flask-sqlalchemy.docs
--- flask-sqlalchemy-2.1/debian/python3-flask-sqlalchemy.docs	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/python3-flask-sqlalchemy.docs	2021-05-11 20:54:08.000000000 +0200
@@ -0,0 +1,2 @@
+README.rst
+examples
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/python3-flask-sqlalchemy.lintian-overrides flask-sqlalchemy-2.5.1/debian/python3-flask-sqlalchemy.lintian-overrides
--- flask-sqlalchemy-2.1/debian/python3-flask-sqlalchemy.lintian-overrides	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/python3-flask-sqlalchemy.lintian-overrides	2021-05-11 20:54:08.000000000 +0200
@@ -0,0 +1,4 @@
+# This is expected here.
+python3-flask-sqlalchemy: duplicate-files usr/share/doc/python3-flask-sqlalchemy/examples/flaskr/flaskr/auth/__init__.py usr/share/doc/python3-flask-sqlalchemy/examples/flaskr/flaskr/blog/__init__.py
+# Also not critical, we have installed some examples.
+python3-flask-sqlalchemy: repeated-path-segment flaskr usr/share/doc/python3-flask-sqlalchemy/examples/flaskr/flaskr/
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/rules flask-sqlalchemy-2.5.1/debian/rules
--- flask-sqlalchemy-2.1/debian/rules	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/rules	2021-05-11 20:54:08.000000000 +0200
@@ -8,3 +8,7 @@
 
 %:
 	dh $@ --with python3 --buildsystem=pybuild
+
+override_dh_installdocs:
+	find examples -type f -name ".gitignore" -exec rm {} \;
+	dh_installdocs
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/upstream/metadata flask-sqlalchemy-2.5.1/debian/upstream/metadata
--- flask-sqlalchemy-2.1/debian/upstream/metadata	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/debian/upstream/metadata	2021-04-29 18:48:58.000000000 +0200
@@ -0,0 +1,4 @@
+Bug-Database: https://github.com/pallets/flask-sqlalchemy/issues
+Bug-Submit: https://github.com/pallets/flask-sqlalchemy/issues/new
+Repository: https://github.com/pallets/flask-sqlalchemy.git
+Repository-Browse: https://github.com/pallets/flask-sqlalchemy
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/debian/watch flask-sqlalchemy-2.5.1/debian/watch
--- flask-sqlalchemy-2.1/debian/watch	2019-08-02 00:18:42.000000000 +0200
+++ flask-sqlalchemy-2.5.1/debian/watch	2021-04-29 18:53:21.000000000 +0200
@@ -1,3 +1,6 @@
-version=3
+# uscan watch file for flask-sqlalchemy
+
+version=4
+
 https://pypi.debian.net/Flask-SQLAlchemy/Flask-SQLAlchemy-(.*)\.tar\.gz
 
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/flask_sqlalchemy/_compat.py flask-sqlalchemy-2.5.1/flask_sqlalchemy/_compat.py
--- flask-sqlalchemy-2.1/flask_sqlalchemy/_compat.py	2015-02-26 18:58:08.000000000 +0100
+++ flask-sqlalchemy-2.5.1/flask_sqlalchemy/_compat.py	2021-03-18 19:56:21.000000000 +0100
@@ -1,16 +1,5 @@
-# -*- coding: utf-8 -*-
-"""
-    flaskext.sqlalchemy._compat
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Internal Python 2.x/3.x compatibility layer.
-
-    :copyright: (c) 2013 by Daniel Neuhäuser
-    :license: BSD, see LICENSE for more details.
-"""
 import sys
 
-
 PY2 = sys.version_info[0] == 2
 
 
@@ -25,6 +14,15 @@
 
     string_types = (unicode, bytes)
 
+    def to_str(x, charset='utf8', errors='strict'):
+        if x is None or isinstance(x, str):
+            return x
+
+        if isinstance(x, unicode):
+            return x.encode(charset, errors)
+
+        return str(x)
+
 else:
     def iteritems(d):
         return iter(d.items())
@@ -34,4 +32,13 @@
 
     xrange = range
 
-    string_types = (str, )
+    string_types = (str,)
+
+    def to_str(x, charset='utf8', errors='strict'):
+        if x is None or isinstance(x, str):
+            return x
+
+        if isinstance(x, bytes):
+            return x.decode(charset, errors)
+
+        return str(x)
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/flask_sqlalchemy/__init__.py flask-sqlalchemy-2.5.1/flask_sqlalchemy/__init__.py
--- flask-sqlalchemy-2.1/flask_sqlalchemy/__init__.py	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/flask_sqlalchemy/__init__.py	2021-03-18 20:01:04.000000000 +0100
@@ -1,60 +1,81 @@
 # -*- coding: utf-8 -*-
-"""
-    flaskext.sqlalchemy
-    ~~~~~~~~~~~~~~~~~~~
-
-    Adds basic SQLAlchemy support to your application.
-
-    :copyright: (c) 2014 by Armin Ronacher, Daniel Neuhäuser.
-    :license: BSD, see LICENSE for more details.
-"""
-from __future__ import with_statement, absolute_import
+from __future__ import absolute_import
+
+import functools
 import os
-import re
 import sys
 import time
-import functools
 import warnings
-import sqlalchemy
 from math import ceil
-from functools import partial
-from flask import _request_ctx_stack, abort, has_request_context, request
-from flask.signals import Namespace
 from operator import itemgetter
 from threading import Lock
-from sqlalchemy import orm, event, inspect
+
+import sqlalchemy
+from flask import _app_ctx_stack, abort, current_app, request
+from flask.signals import Namespace
+from sqlalchemy import event, inspect, orm
+from sqlalchemy.engine.url import make_url
 from sqlalchemy.orm.exc import UnmappedClassError
 from sqlalchemy.orm.session import Session as SessionBase
-from sqlalchemy.engine.url import make_url
-from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
-from flask_sqlalchemy._compat import iteritems, itervalues, xrange, \
-     string_types
+
+from ._compat import itervalues, string_types, xrange
+from .model import DefaultMeta
+from .model import Model
+from . import utils
+
+try:
+    from sqlalchemy.orm import declarative_base
+    from sqlalchemy.orm import DeclarativeMeta
+except ImportError:
+    # SQLAlchemy <= 1.3
+    from sqlalchemy.ext.declarative import declarative_base
+    from sqlalchemy.ext.declarative import DeclarativeMeta
+
+# Scope the session to the current greenlet if greenlet is available,
+# otherwise fall back to the current thread.
+try:
+    from greenlet import getcurrent as _ident_func
+except ImportError:
+    try:
+        from threading import get_ident as _ident_func
+    except ImportError:
+        # Python 2.7
+        from thread import get_ident as _ident_func
+
+__version__ = "2.5.1"
 
 # the best timer function for the platform
 if sys.platform == 'win32':
-    _timer = time.clock
+    if sys.version_info >= (3, 3):
+        _timer = time.perf_counter
+    else:
+        _timer = time.clock
 else:
     _timer = time.time
 
-try:
-    from flask import _app_ctx_stack
-except ImportError:
-    _app_ctx_stack = None
+_signals = Namespace()
+models_committed = _signals.signal('models-committed')
+before_models_committed = _signals.signal('before-models-committed')
 
 
-__version__ = '2.1'
+def _sa_url_set(url, **kwargs):
+    try:
+        url = url.set(**kwargs)
+    except AttributeError:
+        # SQLAlchemy <= 1.3
+        for key, value in kwargs.items():
+            setattr(url, key, value)
 
+    return url
 
-# Which stack should we use?  _app_ctx_stack is new in 0.9
-connection_stack = _app_ctx_stack or _request_ctx_stack
 
+def _sa_url_query_setdefault(url, **kwargs):
+    query = dict(url.query)
 
-_camelcase_re = re.compile(r'([A-Z]+)(?=[a-z0-9])')
-_signals = Namespace()
+    for key, value in kwargs.items():
+        query.setdefault(key, value)
 
-
-models_committed = _signals.signal('models-committed')
-before_models_committed = _signals.signal('before-models-committed')
+    return _sa_url_set(url, query=query)
 
 
 def _make_table(db):
@@ -68,34 +89,34 @@
     return _make_table
 
 
-def _set_default_query_class(d):
+def _set_default_query_class(d, cls):
     if 'query_class' not in d:
-        d['query_class'] = BaseQuery
+        d['query_class'] = cls
 
 
-def _wrap_with_default_query_class(fn):
+def _wrap_with_default_query_class(fn, cls):
     @functools.wraps(fn)
     def newfn(*args, **kwargs):
-        _set_default_query_class(kwargs)
+        _set_default_query_class(kwargs, cls)
         if "backref" in kwargs:
             backref = kwargs['backref']
             if isinstance(backref, string_types):
                 backref = (backref, {})
-            _set_default_query_class(backref[1])
+            _set_default_query_class(backref[1], cls)
         return fn(*args, **kwargs)
     return newfn
 
 
-def _include_sqlalchemy(obj):
+def _include_sqlalchemy(obj, cls):
     for module in sqlalchemy, sqlalchemy.orm:
         for key in module.__all__:
             if not hasattr(obj, key):
                 setattr(obj, key, getattr(module, key))
     # Note: obj.Table does not attempt to be a SQLAlchemy Table class.
     obj.Table = _make_table(obj)
-    obj.relationship = _wrap_with_default_query_class(obj.relationship)
-    obj.relation = _wrap_with_default_query_class(obj.relation)
-    obj.dynamic_loader = _wrap_with_default_query_class(obj.dynamic_loader)
+    obj.relationship = _wrap_with_default_query_class(obj.relationship, cls)
+    obj.relation = _wrap_with_default_query_class(obj.relation, cls)
+    obj.dynamic_loader = _wrap_with_default_query_class(obj.dynamic_loader, cls)
     obj.event = event
 
 
@@ -148,12 +169,12 @@
         to an external transaction.
     """
 
-    def __init__(self, db, autocommit=False, autoflush=True, app=None, **options):
+    def __init__(self, db, autocommit=False, autoflush=True, **options):
         #: The application that this session belongs to.
         self.app = app = db.get_app()
         track_modifications = app.config['SQLALCHEMY_TRACK_MODIFICATIONS']
         bind = options.pop('bind', None) or db.engine
-        binds = options.pop('binds', None) or db.get_binds(app)
+        binds = options.pop('binds', db.get_binds(app))
 
         if track_modifications is None or track_modifications:
             _SessionSignalEvents.register(self)
@@ -164,9 +185,19 @@
         )
 
     def get_bind(self, mapper=None, clause=None):
+        """Return the engine or connection for a given model or
+        table, using the ``__bind_key__`` if it is set.
+        """
         # mapper is None if someone tries to just get a connection
         if mapper is not None:
-            info = getattr(mapper.mapped_table, 'info', {})
+            try:
+                # SA >= 1.3
+                persist_selectable = mapper.persist_selectable
+            except AttributeError:
+                # SA < 1.3
+                persist_selectable = mapper.mapped_table
+
+            info = getattr(persist_selectable, 'info', {})
             bind_key = info.get('bind_key')
             if bind_key is not None:
                 state = get_state(self.app)
@@ -242,32 +273,40 @@
 
 
 class _EngineDebuggingSignalEvents(object):
-    """Sets up handlers for two events that let us track the execution time of queries."""
+    """Sets up handlers for two events that let us track the execution time of
+    queries."""
 
     def __init__(self, engine, import_name):
         self.engine = engine
         self.app_package = import_name
 
     def register(self):
-        event.listen(self.engine, 'before_cursor_execute', self.before_cursor_execute)
-        event.listen(self.engine, 'after_cursor_execute', self.after_cursor_execute)
+        event.listen(
+            self.engine, 'before_cursor_execute', self.before_cursor_execute
+        )
+        event.listen(
+            self.engine, 'after_cursor_execute', self.after_cursor_execute
+        )
 
-    def before_cursor_execute(self, conn, cursor, statement,
-                              parameters, context, executemany):
-        if connection_stack.top is not None:
+    def before_cursor_execute(
+        self, conn, cursor, statement, parameters, context, executemany
+    ):
+        if current_app:
             context._query_start_time = _timer()
 
-    def after_cursor_execute(self, conn, cursor, statement,
-                             parameters, context, executemany):
-        ctx = connection_stack.top
-        if ctx is not None:
-            queries = getattr(ctx, 'sqlalchemy_queries', None)
-            if queries is None:
-                queries = []
-                setattr(ctx, 'sqlalchemy_queries', queries)
+    def after_cursor_execute(
+        self, conn, cursor, statement, parameters, context, executemany
+    ):
+        if current_app:
+            try:
+                queries = _app_ctx_stack.top.sqlalchemy_queries
+            except AttributeError:
+                queries = _app_ctx_stack.top.sqlalchemy_queries = []
+
             queries.append(_DebugQueryTuple((
                 statement, parameters, context._query_start_time, _timer(),
-                _calling_context(self.app_package))))
+                _calling_context(self.app_package)
+            )))
 
 
 def get_debug_queries():
@@ -302,7 +341,7 @@
         query was issued.  The exact format is undefined so don't try
         to reconstruct filename or function name.
     """
-    return getattr(connection_stack.top, 'sqlalchemy_queries', [])
+    return getattr(_app_ctx_stack.top, 'sqlalchemy_queries', [])
 
 
 class Pagination(object):
@@ -344,6 +383,8 @@
     @property
     def prev_num(self):
         """Number of the previous page."""
+        if not self.has_prev:
+            return None
         return self.page - 1
 
     @property
@@ -365,6 +406,8 @@
     @property
     def next_num(self):
         """Number of the next page"""
+        if not self.has_next:
+            return None
         return self.page + 1
 
     def iter_pages(self, left_edge=2, left_current=2,
@@ -395,7 +438,7 @@
         last = 0
         for num in xrange(1, self.pages + 1):
             if num <= left_edge or \
-               (num > self.page - left_current - 1 and \
+               (num > self.page - left_current - 1 and
                 num < self.page + right_current) or \
                num > self.pages - right_edge:
                 if last + 1 != num:
@@ -405,47 +448,50 @@
 
 
 class BaseQuery(orm.Query):
-    """The default query object used for models, and exposed as
-    :attr:`~SQLAlchemy.Query`. This can be subclassed and
-    replaced for individual models by setting the :attr:`~Model.query_class`
-    attribute.  This is a subclass of a standard SQLAlchemy
-    :class:`~sqlalchemy.orm.query.Query` class and has all the methods of a
-    standard query as well.
+    """SQLAlchemy :class:`~sqlalchemy.orm.query.Query` subclass with convenience methods for querying in a web application.
+
+    This is the default :attr:`~Model.query` object used for models, and exposed as :attr:`~SQLAlchemy.Query`.
+    Override the query class for an individual model by subclassing this and setting :attr:`~Model.query_class`.
     """
 
-    def get_or_404(self, ident):
-        """Like :meth:`get` but aborts with 404 if not found instead of
-        returning `None`.
-        """
+    def get_or_404(self, ident, description=None):
+        """Like :meth:`get` but aborts with 404 if not found instead of returning ``None``."""
+
         rv = self.get(ident)
         if rv is None:
-            abort(404)
+            abort(404, description=description)
         return rv
 
-    def first_or_404(self):
-        """Like :meth:`first` but aborts with 404 if not found instead of
-        returning `None`.
-        """
+    def first_or_404(self, description=None):
+        """Like :meth:`first` but aborts with 404 if not found instead of returning ``None``."""
+
         rv = self.first()
         if rv is None:
-            abort(404)
+            abort(404, description=description)
         return rv
 
-    def paginate(self, page=None, per_page=None, error_out=True):
-        """Returns `per_page` items from page `page`.  By default it will
-        abort with 404 if no items were found and the page was larger than
-        1.  This behavor can be disabled by setting `error_out` to `False`.
+    def paginate(self, page=None, per_page=None, error_out=True, max_per_page=None):
+        """Returns ``per_page`` items from page ``page``.
+
+        If ``page`` or ``per_page`` are ``None``, they will be retrieved from
+        the request query. If ``max_per_page`` is specified, ``per_page`` will
+        be limited to that value. If there is no request or they aren't in the
+        query, they default to 1 and 20 respectively.
+
+        When ``error_out`` is ``True`` (default), the following rules will
+        cause a 404 response:
 
-        If page or per_page are None, they will be retrieved from the
-        request query.  If the values are not ints and ``error_out`` is
-        true, it will abort with 404.  If there is no request or they
-        aren't in the query, they default to page 1 and 20
-        respectively.
+        * No items are found and ``page`` is not 1.
+        * ``page`` is less than 1, or ``per_page`` is negative.
+        * ``page`` or ``per_page`` are not ints.
 
-        Returns an :class:`Pagination` object.
+        When ``error_out`` is ``False``, ``page`` and ``per_page`` default to
+        1 and 20 respectively.
+
+        Returns a :class:`Pagination` object.
         """
 
-        if has_request_context():
+        if request:
             if page is None:
                 try:
                     page = int(request.args.get('page', 1))
@@ -470,26 +516,32 @@
             if per_page is None:
                 per_page = 20
 
-        if error_out and page < 1:
-            abort(404)
+        if max_per_page is not None:
+            per_page = min(per_page, max_per_page)
+
+        if page < 1:
+            if error_out:
+                abort(404)
+            else:
+                page = 1
+
+        if per_page < 0:
+            if error_out:
+                abort(404)
+            else:
+                per_page = 20
 
         items = self.limit(per_page).offset((page - 1) * per_page).all()
 
         if not items and page != 1 and error_out:
             abort(404)
 
-        # No need to count if we're on the first page and there are fewer
-        # items than we expected.
-        if page == 1 and len(items) < per_page:
-            total = len(items)
-        else:
-            total = self.order_by(None).count()
+        total = self.order_by(None).count()
 
         return Pagination(self, page, per_page, total, items)
 
 
 class _QueryProperty(object):
-
     def __init__(self, sa):
         self.sa = sa
 
@@ -536,79 +588,36 @@
             echo = self._app.config['SQLALCHEMY_ECHO']
             if (uri, echo) == self._connected_for:
                 return self._engine
-            info = make_url(uri)
-            options = {'convert_unicode': True}
-            self._sa.apply_pool_defaults(self._app, options)
-            self._sa.apply_driver_hacks(self._app, info, options)
-            if echo:
-                options['echo'] = True
-            self._engine = rv = sqlalchemy.create_engine(info, **options)
+
+            sa_url = make_url(uri)
+            sa_url, options = self.get_options(sa_url, echo)
+            self._engine = rv = self._sa.create_engine(sa_url, options)
+
             if _record_queries(self._app):
                 _EngineDebuggingSignalEvents(self._engine,
                                              self._app.import_name).register()
-            self._connected_for = (uri, echo)
-            return rv
-
 
-def _should_set_tablename(bases, d):
-    """Check what values are set by a class and its bases to determine if a
-    tablename should be automatically generated.
+            self._connected_for = (uri, echo)
 
-    The class and its bases are checked in order of precedence: the class
-    itself then each base in the order they were given at class definition.
+            return rv
 
-    Abstract classes do not generate a tablename, although they may have set
-    or inherited a tablename elsewhere.
+    def get_options(self, sa_url, echo):
+        options = {}
 
-    If a class defines a tablename or table, a new one will not be generated.
-    Otherwise, if the class defines a primary key, a new name will be generated.
+        options = self._sa.apply_pool_defaults(self._app, options)
+        sa_url, options = self._sa.apply_driver_hacks(self._app, sa_url, options)
 
-    This supports:
+        if echo:
+            options['echo'] = echo
 
-    * Joined table inheritance without explicitly naming sub-models.
-    * Single table inheritance.
-    * Inheriting from mixins or abstract models.
+        # Give the config options set by a developer explicitly priority
+        # over decisions FSA makes.
+        options.update(self._app.config['SQLALCHEMY_ENGINE_OPTIONS'])
 
-    :param bases: base classes of new class
-    :param d: new class dict
-    :return: True if tablename should be set
-    """
+        # Give options set in SQLAlchemy.__init__() ultimate priority
+        options.update(self._sa._engine_options)
 
-    if '__tablename__' in d or '__table__' in d or '__abstract__' in d:
-        return False
-
-    if any(v.primary_key for v in itervalues(d) if isinstance(v, sqlalchemy.Column)):
-        return True
-
-    for base in bases:
-        if hasattr(base, '__tablename__') or hasattr(base, '__table__'):
-            return False
-
-        for name in dir(base):
-            attr = getattr(base, name)
-
-            if isinstance(attr, sqlalchemy.Column) and attr.primary_key:
-                return True
-
-
-class _BoundDeclarativeMeta(DeclarativeMeta):
-
-    def __new__(cls, name, bases, d):
-        if _should_set_tablename(bases, d):
-            def _join(match):
-                word = match.group()
-                if len(word) > 1:
-                    return ('_%s_%s' % (word[:-1], word[-1])).lower()
-                return '_' + word.lower()
-            d['__tablename__'] = _camelcase_re.sub(_join, name).lstrip('_')
-
-        return DeclarativeMeta.__new__(cls, name, bases, d)
-
-    def __init__(self, name, bases, d):
-        bind_key = d.pop('__bind_key__', None)
-        DeclarativeMeta.__init__(self, name, bases, d)
-        if bind_key is not None:
-            self.__table__.info['bind_key'] = bind_key
+        return sa_url, options
 
 
 def get_state(app):
@@ -622,24 +631,11 @@
 class _SQLAlchemyState(object):
     """Remembers configuration for the (db, app) tuple."""
 
-    def __init__(self, db, app):
+    def __init__(self, db):
         self.db = db
-        self.app = app
         self.connectors = {}
 
 
-class Model(object):
-    """Baseclass for custom user models."""
-
-    #: the query class used.  The :attr:`query` attribute is an instance
-    #: of this class.  By default a :class:`BaseQuery` is used.
-    query_class = BaseQuery
-
-    #: an instance of :attr:`query_class`.  Can be used to query the
-    #: database for instances of this model.
-    query = None
-
-
 class SQLAlchemy(object):
     """This class is used to control the SQLAlchemy integration to one
     or more Flask applications.  Depending on how you initialize the
@@ -667,13 +663,18 @@
     the second case a :meth:`flask.Flask.app_context` has to exist.
 
     By default Flask-SQLAlchemy will apply some backend-specific settings
-    to improve your experience with them.  As of SQLAlchemy 0.6 SQLAlchemy
+    to improve your experience with them.
+
+    As of SQLAlchemy 0.6 SQLAlchemy
     will probe the library for native unicode support.  If it detects
     unicode it will let the library handle that, otherwise do that itself.
     Sometimes this detection can fail in which case you might want to set
-    `use_native_unicode` (or the ``SQLALCHEMY_NATIVE_UNICODE`` configuration
-    key) to `False`.  Note that the configuration key overrides the
-    value you pass to the constructor.
+    ``use_native_unicode`` (or the ``SQLALCHEMY_NATIVE_UNICODE`` configuration
+    key) to ``False``.  Note that the configuration key overrides the
+    value you pass to the constructor.  Direct support for ``use_native_unicode``
+    and SQLALCHEMY_NATIVE_UNICODE are deprecated as of v2.4 and will be removed
+    in v3.0.  ``engine_options`` and ``SQLALCHEMY_ENGINE_OPTIONS`` may be used
+    instead.
 
     This class also provides access to all the SQLAlchemy functions and classes
     from the :mod:`sqlalchemy` and :mod:`sqlalchemy.orm` modules.  So you can
@@ -697,26 +698,15 @@
        emulates `Table` behavior but is not a class. `db.Table` exposes the
        `Table` interface, but is a function which allows omission of metadata.
 
-    You may also define your own SessionExtension instances as well when
-    defining your SQLAlchemy class instance. You may pass your custom instances
-    to the `session_extensions` keyword. This can be either a single
-    SessionExtension instance, or a list of SessionExtension instances. In the
-    following use case we use the VersionedListener from the SQLAlchemy
-    versioning examples.::
-
-        from history_meta import VersionedMeta, VersionedListener
-
-        app = Flask(__name__)
-        db = SQLAlchemy(app, session_extensions=[VersionedListener()])
-
-        class User(db.Model):
-            __metaclass__ = VersionedMeta
-            username = db.Column(db.String(80), unique=True)
-            pw_hash = db.Column(db.String(80))
-
-    The `session_options` parameter can be used to override session
-    options.  If provided it's a dict of parameters passed to the
-    session's constructor.
+    The ``session_options`` parameter, if provided, is a dict of parameters
+    to be passed to the session constructor.  See :class:`~sqlalchemy.orm.session.Session`
+    for the standard options.
+
+    The ``engine_options`` parameter, if provided, is a dict of parameters
+    to be passed to create engine.  See :func:`~sqlalchemy.create_engine`
+    for the standard options.  The values given here will be merged with and
+    override anything set in the ``'SQLALCHEMY_ENGINE_OPTIONS'`` config
+    variable or othewise set by this library.
 
     .. versionadded:: 0.10
        The `session_options` parameter was added.
@@ -728,55 +718,125 @@
     .. versionadded:: 2.1
        The `metadata` parameter was added. This allows for setting custom
        naming conventions among other, non-trivial things.
-    """
 
-    def __init__(self, app=None, use_native_unicode=True, session_options=None, metadata=None):
+       The `query_class` parameter was added, to allow customisation
+       of the query class, in place of the default of :class:`BaseQuery`.
+
+       The `model_class` parameter was added, which allows a custom model
+       class to be used in place of :class:`Model`.
 
-        if session_options is None:
-            session_options = {}
+    .. versionchanged:: 2.1
+       Utilise the same query class across `session`, `Model.query` and `Query`.
+
+    .. versionadded:: 2.4
+       The `engine_options` parameter was added.
+
+    .. versionchanged:: 2.4
+       The `use_native_unicode` parameter was deprecated.
+
+    .. versionchanged:: 2.4.3
+        ``COMMIT_ON_TEARDOWN`` is deprecated and will be removed in
+        version 3.1. Call ``db.session.commit()`` directly instead.
+    """
+
+    #: Default query class used by :attr:`Model.query` and other queries.
+    #: Customize this by passing ``query_class`` to :func:`SQLAlchemy`.
+    #: Defaults to :class:`BaseQuery`.
+    Query = None
+
+    def __init__(self, app=None, use_native_unicode=True, session_options=None,
+                 metadata=None, query_class=BaseQuery, model_class=Model,
+                 engine_options=None):
 
-        session_options.setdefault('scopefunc', connection_stack.__ident_func__)
         self.use_native_unicode = use_native_unicode
+        self.Query = query_class
         self.session = self.create_scoped_session(session_options)
-        self.Model = self.make_declarative_base(metadata)
-        self.Query = BaseQuery
+        self.Model = self.make_declarative_base(model_class, metadata)
         self._engine_lock = Lock()
         self.app = app
-        _include_sqlalchemy(self)
+        self._engine_options = engine_options or {}
+        _include_sqlalchemy(self, query_class)
 
         if app is not None:
             self.init_app(app)
 
     @property
     def metadata(self):
-        """Returns the metadata"""
+        """The metadata associated with ``db.Model``."""
+
         return self.Model.metadata
 
     def create_scoped_session(self, options=None):
-        """Helper factory method that creates a scoped session.  It
-        internally calls :meth:`create_session`.
+        """Create a :class:`~sqlalchemy.orm.scoping.scoped_session`
+        on the factory from :meth:`create_session`.
+
+        An extra key ``'scopefunc'`` can be set on the ``options`` dict to
+        specify a custom scope function.  If it's not provided, Flask's app
+        context stack identity is used. This will ensure that sessions are
+        created and removed with the request/response cycle, and should be fine
+        in most cases.
+
+        :param options: dict of keyword arguments passed to session class  in
+            ``create_session``
         """
+
         if options is None:
             options = {}
-        scopefunc = options.pop('scopefunc', None)
-        return orm.scoped_session(partial(self.create_session, options),
-                                  scopefunc=scopefunc)
+
+        scopefunc = options.pop('scopefunc', _ident_func)
+        options.setdefault('query_cls', self.Query)
+        return orm.scoped_session(
+            self.create_session(options), scopefunc=scopefunc
+        )
 
     def create_session(self, options):
-        """Creates the session.  The default implementation returns a
-        :class:`SignallingSession`.
+        """Create the session factory used by :meth:`create_scoped_session`.
+
+        The factory **must** return an object that SQLAlchemy recognizes as a session,
+        or registering session events may raise an exception.
+
+        Valid factories include a :class:`~sqlalchemy.orm.session.Session`
+        class or a :class:`~sqlalchemy.orm.session.sessionmaker`.
 
-        .. versionadded:: 2.0
+        The default implementation creates a ``sessionmaker`` for :class:`SignallingSession`.
+
+        :param options: dict of keyword arguments passed to session class
         """
-        return SignallingSession(self, **options)
 
-    def make_declarative_base(self, metadata=None):
-        """Creates the declarative base."""
-        base = declarative_base(cls=Model, name='Model',
-                                metadata=metadata,
-                                metaclass=_BoundDeclarativeMeta)
-        base.query = _QueryProperty(self)
-        return base
+        return orm.sessionmaker(class_=SignallingSession, db=self, **options)
+
+    def make_declarative_base(self, model, metadata=None):
+        """Creates the declarative base that all models will inherit from.
+
+        :param model: base model class (or a tuple of base classes) to pass
+            to :func:`~sqlalchemy.ext.declarative.declarative_base`. Or a class
+            returned from ``declarative_base``, in which case a new base class
+            is not created.
+        :param metadata: :class:`~sqlalchemy.MetaData` instance to use, or
+            none to use SQLAlchemy's default.
+
+        .. versionchanged 2.3.0::
+            ``model`` can be an existing declarative base in order to support
+            complex customization such as changing the metaclass.
+        """
+        if not isinstance(model, DeclarativeMeta):
+            model = declarative_base(
+                cls=model,
+                name='Model',
+                metadata=metadata,
+                metaclass=DefaultMeta
+            )
+
+        # if user passed in a declarative base and a metaclass for some reason,
+        # make sure the base uses the metaclass
+        if metadata is not None and model.metadata is not metadata:
+            model.metadata = metadata
+
+        if not getattr(model, 'query_class', None):
+            model.query_class = self.Query
+
+        model.query = _QueryProperty(self)
+        return model
 
     def init_app(self, app):
         """This callback can be used to initialize an application for the
@@ -784,7 +844,16 @@
         of an application not initialized that way or connections will
         leak.
         """
-        app.config.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite://')
+        if (
+            'SQLALCHEMY_DATABASE_URI' not in app.config and
+            'SQLALCHEMY_BINDS' not in app.config
+        ):
+            warnings.warn(
+                'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
+                'Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".'
+            )
+
+        app.config.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite:///:memory:')
         app.config.setdefault('SQLALCHEMY_BINDS', None)
         app.config.setdefault('SQLALCHEMY_NATIVE_UNICODE', None)
         app.config.setdefault('SQLALCHEMY_ECHO', False)
@@ -794,36 +863,48 @@
         app.config.setdefault('SQLALCHEMY_POOL_RECYCLE', None)
         app.config.setdefault('SQLALCHEMY_MAX_OVERFLOW', None)
         app.config.setdefault('SQLALCHEMY_COMMIT_ON_TEARDOWN', False)
-        track_modifications = app.config.setdefault('SQLALCHEMY_TRACK_MODIFICATIONS', None)
+        track_modifications = app.config.setdefault(
+            'SQLALCHEMY_TRACK_MODIFICATIONS', None
+        )
+        app.config.setdefault('SQLALCHEMY_ENGINE_OPTIONS', {})
 
         if track_modifications is None:
-            warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.')
+            warnings.warn(FSADeprecationWarning(
+                'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
+                'will be disabled by default in the future.  Set it to True '
+                'or False to suppress this warning.'
+            ))
+
+        # Deprecation warnings for config keys that should be replaced by SQLALCHEMY_ENGINE_OPTIONS.
+        utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_SIZE', 'pool_size')
+        utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_TIMEOUT', 'pool_timeout')
+        utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_RECYCLE', 'pool_recycle')
+        utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_MAX_OVERFLOW', 'max_overflow')
 
-        if not hasattr(app, 'extensions'):
-            app.extensions = {}
-        app.extensions['sqlalchemy'] = _SQLAlchemyState(self, app)
-
-        # 0.9 and later
-        if hasattr(app, 'teardown_appcontext'):
-            teardown = app.teardown_appcontext
-        # 0.7 to 0.8
-        elif hasattr(app, 'teardown_request'):
-            teardown = app.teardown_request
-        # Older Flask versions
-        else:
-            if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:
-                raise RuntimeError("Commit on teardown requires Flask >= 0.7")
-            teardown = app.after_request
+        app.extensions['sqlalchemy'] = _SQLAlchemyState(self)
 
-        @teardown
+        @app.teardown_appcontext
         def shutdown_session(response_or_exc):
             if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:
+                warnings.warn(
+                    "'COMMIT_ON_TEARDOWN' is deprecated and will be"
+                    " removed in version 3.1. Call"
+                    " 'db.session.commit()'` directly instead.",
+                    DeprecationWarning,
+                )
+
                 if response_or_exc is None:
                     self.session.commit()
+
             self.session.remove()
             return response_or_exc
 
     def apply_pool_defaults(self, app, options):
+        """
+        .. versionchanged:: 2.5
+            Returns the ``options`` dict, for consistency with
+            :meth:`apply_driver_hacks`.
+        """
         def _setdefault(optionkey, configkey):
             value = app.config[configkey]
             if value is not None:
@@ -832,8 +913,9 @@
         _setdefault('pool_timeout', 'SQLALCHEMY_POOL_TIMEOUT')
         _setdefault('pool_recycle', 'SQLALCHEMY_POOL_RECYCLE')
         _setdefault('max_overflow', 'SQLALCHEMY_MAX_OVERFLOW')
+        return options
 
-    def apply_driver_hacks(self, app, info, options):
+    def apply_driver_hacks(self, app, sa_url, options):
         """This method is called before engine creation and used to inject
         driver specific hacks into the options.  The `options` parameter is
         a dictionary of keyword arguments that will then be used to call
@@ -842,18 +924,22 @@
         The default implementation provides some saner defaults for things
         like pool sizes for MySQL and sqlite.  Also it injects the setting of
         `SQLALCHEMY_NATIVE_UNICODE`.
+
+        .. versionchanged:: 2.5
+            Returns ``(sa_url, options)``. SQLAlchemy 1.4 made the URL
+            immutable, so any changes to it must now be passed back up
+            to the original caller.
         """
-        if info.drivername.startswith('mysql'):
-            info.query.setdefault('charset', 'utf8')
-            if info.drivername != 'mysql+gaerdbms':
+        if sa_url.drivername.startswith('mysql'):
+            sa_url = _sa_url_query_setdefault(sa_url, charset="utf8")
+
+            if sa_url.drivername != 'mysql+gaerdbms':
                 options.setdefault('pool_size', 10)
                 options.setdefault('pool_recycle', 7200)
-        elif info.drivername == 'sqlite':
+        elif sa_url.drivername == 'sqlite':
             pool_size = options.get('pool_size')
             detected_in_memory = False
-            # we go to memory and the pool size was explicitly set to 0
-            # which is fail.  Let the user know that
-            if info.database in (None, '', ':memory:'):
+            if sa_url.database in (None, '', ':memory:'):
                 detected_in_memory = True
                 from sqlalchemy.pool import StaticPool
                 options['poolclass'] = StaticPool
@@ -861,6 +947,8 @@
                     options['connect_args'] = {}
                 options['connect_args']['check_same_thread'] = False
 
+                # we go to memory and the pool size was explicitly set
+                # to 0 which is fail.  Let the user know that
                 if pool_size == 0:
                     raise RuntimeError('SQLite in memory database with an '
                                        'empty queue not possible due to data '
@@ -874,7 +962,9 @@
 
             # if it's not an in memory database we make the path absolute.
             if not detected_in_memory:
-                info.database = os.path.join(app.root_path, info.database)
+                sa_url = _sa_url_set(
+                    sa_url, database=os.path.join(app.root_path, sa_url.database)
+                )
 
         unu = app.config['SQLALCHEMY_NATIVE_UNICODE']
         if unu is None:
@@ -882,6 +972,21 @@
         if not unu:
             options['use_native_unicode'] = False
 
+        if app.config['SQLALCHEMY_NATIVE_UNICODE'] is not None:
+            warnings.warn(
+                "The 'SQLALCHEMY_NATIVE_UNICODE' config option is deprecated and will be removed in"
+                " v3.0.  Use 'SQLALCHEMY_ENGINE_OPTIONS' instead.",
+                DeprecationWarning
+            )
+        if not self.use_native_unicode:
+            warnings.warn(
+                "'use_native_unicode' is deprecated and will be removed in v3.0."
+                "  Use the 'engine_options' parameter instead.",
+                DeprecationWarning
+            )
+
+        return sa_url, options
+
     @property
     def engine(self):
         """Gives access to the engine.  If the database configuration is bound
@@ -890,38 +995,55 @@
         is used this might raise a :exc:`RuntimeError` if no application is
         active at the moment.
         """
-        return self.get_engine(self.get_app())
+        return self.get_engine()
 
-    def make_connector(self, app, bind=None):
+    def make_connector(self, app=None, bind=None):
         """Creates the connector for a given state and bind."""
-        return _EngineConnector(self, app, bind)
+        return _EngineConnector(self, self.get_app(app), bind)
 
-    def get_engine(self, app, bind=None):
-        """Returns a specific engine.
+    def get_engine(self, app=None, bind=None):
+        """Returns a specific engine."""
+
+        app = self.get_app(app)
+        state = get_state(app)
 
-        .. versionadded:: 0.12
-        """
         with self._engine_lock:
-            state = get_state(app)
             connector = state.connectors.get(bind)
+
             if connector is None:
                 connector = self.make_connector(app, bind)
                 state.connectors[bind] = connector
+
             return connector.get_engine()
 
-    def get_app(self, reference_app=None):
-        """Helper method that implements the logic to look up an application.
+    def create_engine(self, sa_url, engine_opts):
         """
+            Override this method to have final say over how the SQLAlchemy engine
+            is created.
+
+            In most cases, you will want to use ``'SQLALCHEMY_ENGINE_OPTIONS'``
+            config variable or set ``engine_options`` for :func:`SQLAlchemy`.
+        """
+        return sqlalchemy.create_engine(sa_url, **engine_opts)
+
+    def get_app(self, reference_app=None):
+        """Helper method that implements the logic to look up an
+        application."""
+
         if reference_app is not None:
             return reference_app
+
+        if current_app:
+            return current_app._get_current_object()
+
         if self.app is not None:
             return self.app
-        ctx = connection_stack.top
-        if ctx is not None:
-            return ctx.app
-        raise RuntimeError('application not registered on db '
-                           'instance and no application bound '
-                           'to current context')
+
+        raise RuntimeError(
+            'No application found. Either work inside a view function or push'
+            ' an application context. See'
+            ' http://flask-sqlalchemy.pocoo.org/contexts/.'
+        )
 
     def get_tables_for_bind(self, bind=None):
         """Returns a list of all tables relevant for a bind."""
@@ -988,14 +1110,23 @@
         self._execute_for_all_tables(app, bind, 'reflect', skip_tables=True)
 
     def __repr__(self):
-        app = None
-        if self.app is not None:
-            app = self.app
-        else:
-            ctx = connection_stack.top
-            if ctx is not None:
-                app = ctx.app
         return '<%s engine=%r>' % (
             self.__class__.__name__,
-            app and app.config['SQLALCHEMY_DATABASE_URI'] or None
+            self.engine.url if self.app or current_app else None
         )
+
+
+class _BoundDeclarativeMeta(DefaultMeta):
+    def __init__(cls, name, bases, d):
+        warnings.warn(FSADeprecationWarning(
+            '"_BoundDeclarativeMeta" has been renamed to "DefaultMeta". The'
+            ' old name will be removed in 3.0.'
+        ), stacklevel=3)
+        super(_BoundDeclarativeMeta, cls).__init__(name, bases, d)
+
+
+class FSADeprecationWarning(DeprecationWarning):
+    pass
+
+
+warnings.simplefilter('always', FSADeprecationWarning)
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/flask_sqlalchemy/model.py flask-sqlalchemy-2.5.1/flask_sqlalchemy/model.py
--- flask-sqlalchemy-2.1/flask_sqlalchemy/model.py	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/flask_sqlalchemy/model.py	2021-03-18 19:56:21.000000000 +0100
@@ -0,0 +1,154 @@
+import re
+
+import sqlalchemy as sa
+from sqlalchemy import inspect
+from sqlalchemy.ext.declarative import DeclarativeMeta, declared_attr
+from sqlalchemy.schema import _get_table_key
+
+from ._compat import to_str
+
+
+def should_set_tablename(cls):
+    """Determine whether ``__tablename__`` should be automatically generated
+    for a model.
+
+    * If no class in the MRO sets a name, one should be generated.
+    * If a declared attr is found, it should be used instead.
+    * If a name is found, it should be used if the class is a mixin, otherwise
+      one should be generated.
+    * Abstract models should not have one generated.
+
+    Later, :meth:`._BoundDeclarativeMeta.__table_cls__` will determine if the
+    model looks like single or joined-table inheritance. If no primary key is
+    found, the name will be unset.
+    """
+    if (
+        cls.__dict__.get('__abstract__', False)
+        or not any(isinstance(b, DeclarativeMeta) for b in cls.__mro__[1:])
+    ):
+        return False
+
+    for base in cls.__mro__:
+        if '__tablename__' not in base.__dict__:
+            continue
+
+        if isinstance(base.__dict__['__tablename__'], declared_attr):
+            return False
+
+        return not (
+            base is cls
+            or base.__dict__.get('__abstract__', False)
+            or not isinstance(base, DeclarativeMeta)
+        )
+
+    return True
+
+
+camelcase_re = re.compile(r'([A-Z]+)(?=[a-z0-9])')
+
+
+def camel_to_snake_case(name):
+    def _join(match):
+        word = match.group()
+
+        if len(word) > 1:
+            return ('_%s_%s' % (word[:-1], word[-1])).lower()
+
+        return '_' + word.lower()
+
+    return camelcase_re.sub(_join, name).lstrip('_')
+
+
+class NameMetaMixin(type):
+    def __init__(cls, name, bases, d):
+        if should_set_tablename(cls):
+            cls.__tablename__ = camel_to_snake_case(cls.__name__)
+
+        super(NameMetaMixin, cls).__init__(name, bases, d)
+
+        # __table_cls__ has run at this point
+        # if no table was created, use the parent table
+        if (
+            '__tablename__' not in cls.__dict__
+            and '__table__' in cls.__dict__
+            and cls.__dict__['__table__'] is None
+        ):
+            del cls.__table__
+
+    def __table_cls__(cls, *args, **kwargs):
+        """This is called by SQLAlchemy during mapper setup. It determines the
+        final table object that the model will use.
+
+        If no primary key is found, that indicates single-table inheritance,
+        so no table will be created and ``__tablename__`` will be unset.
+        """
+        # check if a table with this name already exists
+        # allows reflected tables to be applied to model by name
+        key = _get_table_key(args[0], kwargs.get('schema'))
+
+        if key in cls.metadata.tables:
+            return sa.Table(*args, **kwargs)
+
+        # if a primary key or constraint is found, create a table for
+        # joined-table inheritance
+        for arg in args:
+            if (
+                (isinstance(arg, sa.Column) and arg.primary_key)
+                or isinstance(arg, sa.PrimaryKeyConstraint)
+            ):
+                return sa.Table(*args, **kwargs)
+
+        # if no base classes define a table, return one
+        # ensures the correct error shows up when missing a primary key
+        for base in cls.__mro__[1:-1]:
+            if '__table__' in base.__dict__:
+                break
+        else:
+            return sa.Table(*args, **kwargs)
+
+        # single-table inheritance, use the parent tablename
+        if '__tablename__' in cls.__dict__:
+            del cls.__tablename__
+
+
+class BindMetaMixin(type):
+    def __init__(cls, name, bases, d):
+        bind_key = (
+            d.pop('__bind_key__', None)
+            or getattr(cls, '__bind_key__', None)
+        )
+
+        super(BindMetaMixin, cls).__init__(name, bases, d)
+
+        if bind_key is not None and getattr(cls, '__table__', None) is not None:
+            cls.__table__.info['bind_key'] = bind_key
+
+
+class DefaultMeta(NameMetaMixin, BindMetaMixin, DeclarativeMeta):
+    pass
+
+
+class Model(object):
+    """Base class for SQLAlchemy declarative base model.
+
+    To define models, subclass :attr:`db.Model <SQLAlchemy.Model>`, not this
+    class. To customize ``db.Model``, subclass this and pass it as
+    ``model_class`` to :class:`SQLAlchemy`.
+    """
+
+    #: Query class used by :attr:`query`. Defaults to
+    # :class:`SQLAlchemy.Query`, which defaults to :class:`BaseQuery`.
+    query_class = None
+
+    #: Convenience property to query the database for instances of this model
+    # using the current session. Equivalent to ``db.session.query(Model)``
+    # unless :attr:`query_class` has been changed.
+    query = None
+
+    def __repr__(self):
+        identity = inspect(self).identity
+        if identity is None:
+            pk = "(transient {0})".format(id(self))
+        else:
+            pk = ', '.join(to_str(value) for value in identity)
+        return '<{0} {1}>'.format(type(self).__name__, pk)
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/flask_sqlalchemy/utils.py flask-sqlalchemy-2.5.1/flask_sqlalchemy/utils.py
--- flask-sqlalchemy-2.1/flask_sqlalchemy/utils.py	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/flask_sqlalchemy/utils.py	2021-03-18 19:56:21.000000000 +0100
@@ -0,0 +1,45 @@
+import warnings
+
+import sqlalchemy
+
+
+def parse_version(v):
+    """
+        Take a string version and conver it to a tuple (for easier comparison), e.g.:
+
+            "1.2.3" --> (1, 2, 3)
+            "1.2" --> (1, 2, 0)
+            "1" --> (1, 0, 0)
+    """
+    parts = v.split(".")
+    # Pad the list to make sure there is three elements so that we get major, minor, point
+    # comparisons that default to "0" if not given.  I.e. "1.2" --> (1, 2, 0)
+    parts = (parts + 3 * ['0'])[:3]
+    return tuple(int(x) for x in parts)
+
+
+def sqlalchemy_version(op, val):
+    sa_ver = parse_version(sqlalchemy.__version__)
+    target_ver = parse_version(val)
+
+    assert op in ('<', '>', '<=', '>=', '=='), 'op {} not supported'.format(op)
+
+    if op == '<':
+        return sa_ver < target_ver
+    if op == '>':
+        return sa_ver > target_ver
+    if op == '<=':
+        return sa_ver <= target_ver
+    if op == '>=':
+        return sa_ver >= target_ver
+    return sa_ver == target_ver
+
+
+def engine_config_warning(config, version, deprecated_config_key, engine_option):
+    if config[deprecated_config_key] is not None:
+        warnings.warn(
+            'The `{}` config option is deprecated and will be removed in'
+            ' v{}.  Use `SQLALCHEMY_ENGINE_OPTIONS[\'{}\']` instead.'
+            .format(deprecated_config_key, version, engine_option),
+            DeprecationWarning
+        )
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/not-zip-safe flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/not-zip-safe
--- flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/not-zip-safe	2015-03-26 17:15:10.000000000 +0100
+++ flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/not-zip-safe	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/PKG-INFO flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/PKG-INFO
--- flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/PKG-INFO	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/PKG-INFO	2021-03-18 20:02:18.000000000 +0100
@@ -1,36 +1,90 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: Flask-SQLAlchemy
-Version: 2.1
-Summary: Adds SQLAlchemy support to your Flask application
-Home-page: http://github.com/mitsuhiko/flask-sqlalchemy
-Author: Phil Howell
-Author-email: phil@quae.co.uk
-License: BSD
-Description: 
-        Flask-SQLAlchemy
+Version: 2.5.1
+Summary: Adds SQLAlchemy support to your Flask application.
+Home-page: https://github.com/pallets/flask-sqlalchemy
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+Maintainer: Pallets
+Maintainer-email: contact@palletsprojects.com
+License: BSD-3-Clause
+Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com/
+Project-URL: Code, https://github.com/pallets/flask-sqlalchemy
+Project-URL: Issue tracker, https://github.com/pallets/flask-sqlalchemy/issues
+Description: Flask-SQLAlchemy
+        ================
+        
+        Flask-SQLAlchemy is an extension for `Flask`_ that adds support for
+        `SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy
+        with Flask by providing useful defaults and extra helpers that make it
+        easier to accomplish common tasks.
+        
+        
+        Installing
+        ----------
+        
+        Install and update using `pip`_:
+        
+        .. code-block:: text
+        
+          $ pip install -U Flask-SQLAlchemy
+        
+        
+        A Simple Example
         ----------------
         
-        Adds SQLAlchemy support to your Flask application.
+        .. code-block:: python
+        
+            from flask import Flask
+            from flask_sqlalchemy import SQLAlchemy
+        
+            app = Flask(__name__)
+            app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite"
+            db = SQLAlchemy(app)
+        
+        
+            class User(db.Model):
+                id = db.Column(db.Integer, primary_key=True)
+                username = db.Column(db.String, unique=True, nullable=False)
+                email = db.Column(db.String, unique=True, nullable=False)
+        
+        
+            db.session.add(User(name="Flask", email="example@example.com"))
+            db.session.commit()
+        
+            users = User.query.all()
+        
         
         Links
-        `````
+        -----
         
-        * `documentation <http://flask-sqlalchemy.pocoo.org>`_
-        * `development version
-          <http://github.com/mitsuhiko/flask-sqlalchemy/zipball/master#egg=Flask-SQLAlchemy-dev>`_
+        -   Documentation: https://flask-sqlalchemy.palletsprojects.com/
+        -   Releases: https://pypi.org/project/Flask-SQLAlchemy/
+        -   Code: https://github.com/pallets/flask-sqlalchemy
+        -   Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues
+        -   Test status: https://travis-ci.org/pallets/flask-sqlalchemy
+        -   Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy
         
+        .. _Flask: https://palletsprojects.com/p/flask/
+        .. _SQLAlchemy: https://www.sqlalchemy.org
+        .. _pip: https://pip.pypa.io/en/stable/quickstart/
         
-Platform: any
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
-Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/requires.txt flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/requires.txt
--- flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/requires.txt	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/requires.txt	2021-03-18 20:02:18.000000000 +0100
@@ -1,2 +1,2 @@
 Flask>=0.10
-SQLAlchemy>=0.7
\ Kein Zeilenumbruch am Dateiende.
+SQLAlchemy>=0.8.0
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/SOURCES.txt flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/SOURCES.txt
--- flask-sqlalchemy-2.1/Flask_SQLAlchemy.egg-info/SOURCES.txt	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/Flask_SQLAlchemy.egg-info/SOURCES.txt	2021-03-18 20:02:18.000000000 +0100
@@ -1,35 +1,80 @@
-CHANGES
-LICENSE
+CHANGES.rst
+LICENSE.rst
 MANIFEST.in
-README
+README.rst
 setup.cfg
 setup.py
-test_sqlalchemy.py
+tox.ini
 Flask_SQLAlchemy.egg-info/PKG-INFO
 Flask_SQLAlchemy.egg-info/SOURCES.txt
 Flask_SQLAlchemy.egg-info/dependency_links.txt
-Flask_SQLAlchemy.egg-info/not-zip-safe
 Flask_SQLAlchemy.egg-info/requires.txt
 Flask_SQLAlchemy.egg-info/top_level.txt
+artwork/flask-sqlalchemy.svg
 docs/Makefile
 docs/api.rst
 docs/binds.rst
 docs/changelog.rst
 docs/conf.py
 docs/config.rst
-docs/contents.rst.inc
 docs/contexts.rst
-docs/flaskstyle.sty
+docs/customizing.rst
 docs/index.rst
-docs/logo.pdf
+docs/license.rst
 docs/make.bat
 docs/models.rst
 docs/queries.rst
 docs/quickstart.rst
+docs/requirements.txt
 docs/signals.rst
-docs/_static/flask-sqlalchemy-small.png
-docs/_static/flask-sqlalchemy.png
-docs/_templates/sidebarintro.html
-docs/_templates/sidebarlogo.html
+docs/_static/flask-sqlalchemy-logo.png
+docs/_static/flask-sqlalchemy-title.png
+examples/flaskr/.gitignore
+examples/flaskr/LICENSE.rst
+examples/flaskr/MANIFEST.in
+examples/flaskr/README.rst
+examples/flaskr/setup.cfg
+examples/flaskr/setup.py
+examples/flaskr/flaskr/__init__.py
+examples/flaskr/flaskr/auth/__init__.py
+examples/flaskr/flaskr/auth/models.py
+examples/flaskr/flaskr/auth/views.py
+examples/flaskr/flaskr/blog/__init__.py
+examples/flaskr/flaskr/blog/models.py
+examples/flaskr/flaskr/blog/views.py
+examples/flaskr/flaskr/static/style.css
+examples/flaskr/flaskr/templates/base.html
+examples/flaskr/flaskr/templates/auth/login.html
+examples/flaskr/flaskr/templates/auth/register.html
+examples/flaskr/flaskr/templates/blog/create.html
+examples/flaskr/flaskr/templates/blog/index.html
+examples/flaskr/flaskr/templates/blog/update.html
+examples/flaskr/tests/conftest.py
+examples/flaskr/tests/test_auth.py
+examples/flaskr/tests/test_blog.py
+examples/flaskr/tests/test_init.py
+examples/hello/hello.cfg
+examples/hello/hello.py
+examples/hello/templates/layout.html
+examples/hello/templates/new.html
+examples/hello/templates/show_all.html
 flask_sqlalchemy/__init__.py
-flask_sqlalchemy/_compat.py
\ Kein Zeilenumbruch am Dateiende.
+flask_sqlalchemy/_compat.py
+flask_sqlalchemy/model.py
+flask_sqlalchemy/utils.py
+tests/conftest.py
+tests/test_basic_app.py
+tests/test_binds.py
+tests/test_commit_on_teardown.py
+tests/test_config.py
+tests/test_meta_data.py
+tests/test_model_class.py
+tests/test_pagination.py
+tests/test_query_class.py
+tests/test_query_property.py
+tests/test_regressions.py
+tests/test_sessions.py
+tests/test_signals.py
+tests/test_sqlalchemy_includes.py
+tests/test_table_name.py
+tests/test_utils.py
\ Kein Zeilenumbruch am Dateiende.
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/LICENSE flask-sqlalchemy-2.5.1/LICENSE
--- flask-sqlalchemy-2.1/LICENSE	2015-02-26 18:58:08.000000000 +0100
+++ flask-sqlalchemy-2.5.1/LICENSE	1970-01-01 01:00:00.000000000 +0100
@@ -1,31 +0,0 @@
-Copyright (c) 2014 by Armin Ronacher.
-
-Some rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-* Redistributions of source code must retain the above copyright
-  notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the following
-  disclaimer in the documentation and/or other materials provided
-  with the distribution.
-
-* The names of the contributors may not be used to endorse or
-  promote products derived from this software without specific
-  prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/LICENSE.rst flask-sqlalchemy-2.5.1/LICENSE.rst
--- flask-sqlalchemy-2.1/LICENSE.rst	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/LICENSE.rst	2020-05-27 16:51:59.000000000 +0200
@@ -0,0 +1,28 @@
+Copyright 2010 Pallets
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1.  Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+2.  Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+3.  Neither the name of the copyright holder nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/MANIFEST.in flask-sqlalchemy-2.5.1/MANIFEST.in
--- flask-sqlalchemy-2.1/MANIFEST.in	2015-02-26 18:58:08.000000000 +0100
+++ flask-sqlalchemy-2.5.1/MANIFEST.in	2021-03-18 19:56:21.000000000 +0100
@@ -1,6 +1,8 @@
-include LICENSE CHANGES *.py
-recursive-include docs *
-recursive-exclude docs *.pyc
-recursive-exclude docs *.pyo
+include CHANGES.rst
+include tox.ini
+graft artwork
+graft docs
 prune docs/_build
-prune docs/_themes/.git
+graft examples
+graft tests
+global-exclude *.pyc
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/PKG-INFO flask-sqlalchemy-2.5.1/PKG-INFO
--- flask-sqlalchemy-2.1/PKG-INFO	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/PKG-INFO	2021-03-18 20:02:18.032466000 +0100
@@ -1,36 +1,90 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: Flask-SQLAlchemy
-Version: 2.1
-Summary: Adds SQLAlchemy support to your Flask application
-Home-page: http://github.com/mitsuhiko/flask-sqlalchemy
-Author: Phil Howell
-Author-email: phil@quae.co.uk
-License: BSD
-Description: 
-        Flask-SQLAlchemy
+Version: 2.5.1
+Summary: Adds SQLAlchemy support to your Flask application.
+Home-page: https://github.com/pallets/flask-sqlalchemy
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+Maintainer: Pallets
+Maintainer-email: contact@palletsprojects.com
+License: BSD-3-Clause
+Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com/
+Project-URL: Code, https://github.com/pallets/flask-sqlalchemy
+Project-URL: Issue tracker, https://github.com/pallets/flask-sqlalchemy/issues
+Description: Flask-SQLAlchemy
+        ================
+        
+        Flask-SQLAlchemy is an extension for `Flask`_ that adds support for
+        `SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy
+        with Flask by providing useful defaults and extra helpers that make it
+        easier to accomplish common tasks.
+        
+        
+        Installing
+        ----------
+        
+        Install and update using `pip`_:
+        
+        .. code-block:: text
+        
+          $ pip install -U Flask-SQLAlchemy
+        
+        
+        A Simple Example
         ----------------
         
-        Adds SQLAlchemy support to your Flask application.
+        .. code-block:: python
+        
+            from flask import Flask
+            from flask_sqlalchemy import SQLAlchemy
+        
+            app = Flask(__name__)
+            app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite"
+            db = SQLAlchemy(app)
+        
+        
+            class User(db.Model):
+                id = db.Column(db.Integer, primary_key=True)
+                username = db.Column(db.String, unique=True, nullable=False)
+                email = db.Column(db.String, unique=True, nullable=False)
+        
+        
+            db.session.add(User(name="Flask", email="example@example.com"))
+            db.session.commit()
+        
+            users = User.query.all()
+        
         
         Links
-        `````
+        -----
         
-        * `documentation <http://flask-sqlalchemy.pocoo.org>`_
-        * `development version
-          <http://github.com/mitsuhiko/flask-sqlalchemy/zipball/master#egg=Flask-SQLAlchemy-dev>`_
+        -   Documentation: https://flask-sqlalchemy.palletsprojects.com/
+        -   Releases: https://pypi.org/project/Flask-SQLAlchemy/
+        -   Code: https://github.com/pallets/flask-sqlalchemy
+        -   Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues
+        -   Test status: https://travis-ci.org/pallets/flask-sqlalchemy
+        -   Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy
         
+        .. _Flask: https://palletsprojects.com/p/flask/
+        .. _SQLAlchemy: https://www.sqlalchemy.org
+        .. _pip: https://pip.pypa.io/en/stable/quickstart/
         
-Platform: any
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
-Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/README flask-sqlalchemy-2.5.1/README
--- flask-sqlalchemy-2.1/README	2015-07-28 21:15:32.000000000 +0200
+++ flask-sqlalchemy-2.5.1/README	1970-01-01 01:00:00.000000000 +0100
@@ -1,47 +0,0 @@
-
-                          // Flask-SQLAlchemy //
-
-
-    ~ What is Flask-SQLAlchemy?
-
-      Flask-SQLAlchemy is a Flask microframework extension which adds
-      support for the SQLAlchemy SQL toolkit/ORM.
-
-    ~ What's the latest version?
-
-      2.0 is the most recent stable version. 2.1 is slated for release in early February. 
-      
-    ~ What do I need?
-
-      SQLAlchemy, and Flask 0.10 or later. `pip` or `easy_install` will
-      install them for you if you do `pip install Flask-SQLAlchemy`.
-      We encourage you to use a virtualenv. Check the docs for complete
-      installation and usage instructions.
-
-    ~ Where are the docs?
-
-      Go to http://flask-sqlalchemy.pocoo.org/ for a prebuilt version
-      of the current documentation.  Otherwise build them yourself
-      from the sphinx sources in the docs folder.
-
-    ~ Where are the tests?
-
-      Good that you're asking. To run the tests use the
-      `test_sqlalchemy.py` file:
-
-        $ python test_sqlalchemy.py
-
-      If you just want one particular testcase to run you can provide
-      it on the command line:
-
-        $ python test_sqlalchemy.py PaginationTestCase
-        
-      In case you have `tox` installed, you can also run that to have
-      virtualenvs created automatically and tests run inside of them
-      at your convenience. Running just `tox` is enough:
-      
-        $ tox
-
-    ~ Where can I get help?
-
-      Join us on the #pocoo IRC channel on irc.freenode.net.
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/README.rst flask-sqlalchemy-2.5.1/README.rst
--- flask-sqlalchemy-2.1/README.rst	1970-01-01 01:00:00.000000000 +0100
+++ flask-sqlalchemy-2.5.1/README.rst	2021-03-18 19:56:21.000000000 +0100
@@ -0,0 +1,57 @@
+Flask-SQLAlchemy
+================
+
+Flask-SQLAlchemy is an extension for `Flask`_ that adds support for
+`SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy
+with Flask by providing useful defaults and extra helpers that make it
+easier to accomplish common tasks.
+
+
+Installing
+----------
+
+Install and update using `pip`_:
+
+.. code-block:: text
+
+  $ pip install -U Flask-SQLAlchemy
+
+
+A Simple Example
+----------------
+
+.. code-block:: python
+
+    from flask import Flask
+    from flask_sqlalchemy import SQLAlchemy
+
+    app = Flask(__name__)
+    app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite"
+    db = SQLAlchemy(app)
+
+
+    class User(db.Model):
+        id = db.Column(db.Integer, primary_key=True)
+        username = db.Column(db.String, unique=True, nullable=False)
+        email = db.Column(db.String, unique=True, nullable=False)
+
+
+    db.session.add(User(name="Flask", email="example@example.com"))
+    db.session.commit()
+
+    users = User.query.all()
+
+
+Links
+-----
+
+-   Documentation: https://flask-sqlalchemy.palletsprojects.com/
+-   Releases: https://pypi.org/project/Flask-SQLAlchemy/
+-   Code: https://github.com/pallets/flask-sqlalchemy
+-   Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues
+-   Test status: https://travis-ci.org/pallets/flask-sqlalchemy
+-   Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy
+
+.. _Flask: https://palletsprojects.com/p/flask/
+.. _SQLAlchemy: https://www.sqlalchemy.org
+.. _pip: https://pip.pypa.io/en/stable/quickstart/
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/setup.cfg flask-sqlalchemy-2.5.1/setup.cfg
--- flask-sqlalchemy-2.1/setup.cfg	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/setup.cfg	2021-03-18 20:02:18.032466000 +0100
@@ -1,11 +1,25 @@
-[egg_info]
-tag_date = 0
-tag_build = 
-tag_svn_revision = 0
+[metadata]
+license_file = LICENSE.rst
+
+[bdist_wheel]
+universal = true
+
+[tool:pytest]
+testpaths = tests
 
-[aliases]
-release = egg_info -RDb ''
+[coverage:run]
+branch = True
+source = 
+	flask_sqlalchemy
+	tests
 
-[pytest]
-norecursedirs = .* _* scripts {args}
+[coverage:paths]
+source = 
+	flask_sqlalchemy
+	.tox/*/lib/python*/site-packages/flask_sqlalchemy
+	.tox/*/site-packages/flask_sqlalchemy
+
+[egg_info]
+tag_build = 
+tag_date = 0
 
diff -Nru --exclude docs --exclude examples --exclude flask-sqlalchemy.svg --exclude tests --exclude 'test_*' flask-sqlalchemy-2.1/setup.py flask-sqlalchemy-2.5.1/setup.py
--- flask-sqlalchemy-2.1/setup.py	2015-10-23 11:49:49.000000000 +0200
+++ flask-sqlalchemy-2.5.1/setup.py	2021-03-18 19:56:21.000000000 +0100
@@ -1,52 +1,51 @@
-"""
-Flask-SQLAlchemy
-----------------
+import io
+import re
 
-Adds SQLAlchemy support to your Flask application.
-
-Links
-`````
-
-* `documentation <http://flask-sqlalchemy.pocoo.org>`_
-* `development version
-  <http://github.com/mitsuhiko/flask-sqlalchemy/zipball/master#egg=Flask-SQLAlchemy-dev>`_
-
-"""
 from setuptools import setup
 
+with io.open("README.rst", "rt", encoding="utf8") as f:
+    readme = f.read()
+
+with io.open("flask_sqlalchemy/__init__.py", "rt", encoding="utf8") as f:
+    version = re.search(r'__version__ = "(.*?)"', f.read(), re.M).group(1)
 
 setup(
-    name='Flask-SQLAlchemy',
-    version='2.1',
-    url='http://github.com/mitsuhiko/flask-sqlalchemy',
-    license='BSD',
-    author='Armin Ronacher',
-    author_email='armin.ronacher@active-4.com',
-    maintainer='Phil Howell',
-    maintainer_email='phil@quae.co.uk',
-    description='Adds SQLAlchemy support to your Flask application',
-    long_description=__doc__,
-    packages=['flask_sqlalchemy'],
-    zip_safe=False,
-    platforms='any',
-    install_requires=[
-        'Flask>=0.10',
-        'SQLAlchemy>=0.7'
-    ],
-    test_suite='test_sqlalchemy.suite',
+    name="Flask-SQLAlchemy",
+    version=version,
+    url="https://github.com/pallets/flask-sqlalchemy";,
+    project_urls={
+        "Documentation": "https://flask-sqlalchemy.palletsprojects.com/";,
+        "Code": "https://github.com/pallets/flask-sqlalchemy";,
+        "Issue tracker": "https://github.com/pallets/flask-sqlalchemy/issues";,
+    },
+    license="BSD-3-Clause",
+    author="Armin Ronacher",
+    author_email="armin.ronacher@active-4.com",
+    maintainer="Pallets",
+    maintainer_email="conta

Attachment: flask-sqlalchemy_2.1-4_2.5.1-1-debdiff-full.txt.gz
Description: application/gzip


Reply to: