-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
520 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
@import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400;1,700&family=Source+Code+Pro:ital,wght@0,400;0,700;1,400;1,700&display=swap'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# API | ||
|
||
Anything documented here is part of the public API that Flask-SQLAlchemy | ||
provides, unless otherwise indicated. Anything not documented here is considered | ||
internal or private and may change at any time. | ||
|
||
```{eval-rst} | ||
.. currentmodule:: flask_sqlalchemy_lite | ||
.. autoclass:: SQLAlchemy | ||
:members: | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Changes | ||
|
||
```{include} ../CHANGES.md | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import importlib.metadata | ||
|
||
# Project -------------------------------------------------------------- | ||
|
||
project = "Flask-SQLAlchemy-Lite" | ||
version = release = importlib.metadata.version("flask-sqlalchemy-lite").partition( | ||
".dev" | ||
)[0] | ||
|
||
# General -------------------------------------------------------------- | ||
|
||
default_role = "code" | ||
extensions = [ | ||
"sphinx.ext.autodoc", | ||
"sphinx.ext.extlinks", | ||
"sphinx.ext.intersphinx", | ||
"myst_parser", | ||
] | ||
autodoc_member_order = "bysource" | ||
autodoc_typehints = "description" | ||
autodoc_preserve_defaults = True | ||
extlinks = { | ||
"issue": ("https://github.com/davidism/flask-sqlalchemy-lite/issues/%s", "#%s"), | ||
"pr": ("https://github.com/davidism/flask-sqlalchemy-lite/pull/%s", "#%s"), | ||
} | ||
intersphinx_mapping = { | ||
"python": ("https://docs.python.org/3/", None), | ||
"flask": ("https://flask.palletsprojects.com", None), | ||
"sqlalchemy": ("https://docs.sqlalchemy.org", None), | ||
} | ||
myst_enable_extensions = [ | ||
"fieldlist", | ||
] | ||
myst_heading_anchors = 2 | ||
|
||
# HTML ----------------------------------------------------------------- | ||
|
||
html_theme = "furo" | ||
html_static_path = ["_static"] | ||
html_css_files = ["theme.css"] | ||
html_copy_source = False | ||
html_theme_options = { | ||
"source_repository": "https://github.com/davidism/flask-sqlalchemy-lite/", | ||
"source_branch": "main", | ||
"source_directory": "docs/", | ||
"light_css_variables": { | ||
"font-stack": "'Atkinson Hyperlegible', sans-serif", | ||
"font-stack--monospace": "'Source Code Pro', monospace", | ||
}, | ||
} | ||
pygments_style = "default" | ||
pygments_style_dark = "github-dark" | ||
html_show_copyright = False | ||
html_use_index = False | ||
html_domain_indices = False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# Engines | ||
|
||
One or more SQLAlchemy {class}`engines <sqlalchemy.engine.Engine>` can be | ||
configured through Flask's {attr}`app.config <flask.Flask.config>`. The engines | ||
are created when {meth}`.SQLAlchemy.init_app` is called, changing config after | ||
that will have no effect. Both sync and async engines can be configured. | ||
|
||
|
||
## Flask Config | ||
|
||
```{currentmodule} flask_sqlalchemy_lite | ||
``` | ||
|
||
```{data} SQLALCHEMY_ENGINES | ||
A dictionary defining sync engine configurations. Each key is a name for an | ||
engine, used to refer to them later. Each value is the engine configuration. | ||
If the value is a dict, it consists of keyword arguments to be passed to | ||
{func}`sqlalchemy.create_engine`. The `'url'` key is required; it can be a | ||
connection string (`dialect://user:pass@host:port/name?args`), a | ||
{class}`sqlalchemy.engine.URL` instance, or a dict representing keyword | ||
arguments to pass to {meth}`sqlalchemy.engine.URL.create`. | ||
As a shortcut, if you only need to specify the URL and no other arguments, the | ||
value can be a connection string or `URL` instance. | ||
``` | ||
|
||
```{data} SQLALCHEMY_ASYNC_ENGINES | ||
The same as {data}`SQLALCHEMY_ENGINES`, but for async engine configurations. | ||
``` | ||
|
||
### URL Examples | ||
|
||
The following configurations are all equivalent. | ||
|
||
```python | ||
SQLALCHEMY_ENGINES = { | ||
"default": "sqlite:///default.sqlite" | ||
} | ||
``` | ||
|
||
```python | ||
from sqlalchemy import URL | ||
SQLALCHEMY_ENGINES = { | ||
"default": URL.create("sqlite", database="default.sqlite") | ||
} | ||
``` | ||
|
||
```python | ||
SQLALCHEMY_ENGINES = { | ||
"default": {"url": "sqlite:///default.sqlite"} | ||
} | ||
``` | ||
|
||
```python | ||
from sqlalchemy import URL | ||
SQLALCHEMY_ENGINES = { | ||
"default": {"url": URL.create("sqlite", database="default.sqlite")} | ||
} | ||
``` | ||
|
||
```python | ||
SQLALCHEMY_ENGINES = { | ||
"default": {"url": {"drivername": "sqlite", "database": "default.sqlite"}} | ||
} | ||
``` | ||
|
||
|
||
## Default Options | ||
|
||
Default engine options can be passed as the `engine_options` parameter when | ||
creating the {class}`.SQLAlchemy` instance. The config for each engine will be | ||
merged with these default options, overriding any shared keys. This applies to | ||
both sync and async engines. You can use specific config if you need different | ||
options for each. | ||
|
||
|
||
### SQLite Defaults | ||
|
||
A relative database path will be relative to the app's | ||
{attr}`~flask.Flask.instance_path` instead of the current directory. The | ||
instance folder will be created if it does not exist. | ||
|
||
When using a memory database (no path, or `:memory:`), a static pool will be | ||
used, and `check_same_thread=False` will be passed. This allows multiple workers | ||
to share the database. | ||
|
||
|
||
### MySQL Defaults | ||
|
||
When using a queue pool (default), `pool_recycle` is set to 7200 seconds | ||
(2 hours), forcing SQLAlchemy to reconnect before MySQL would discard the idle | ||
connection. | ||
|
||
The connection charset is set to `utf8mb4`. | ||
|
||
|
||
## The Default Engine and Bind | ||
|
||
The `"default"` key is special, and will be used for {attr}`.SQLAlchemy.engine` | ||
and as the default bind for {attr}`.SQLAlchemy.sessionmaker`. By default, it is | ||
an error not to configure it for one of sync or async engines. | ||
|
||
|
||
## Custom Engines | ||
|
||
You can ignore the Flask config altogether and create engines yourself. In that | ||
case, you pass `require_default_engine=False` when creating the extension to | ||
ignore the check for default config. Adding custom engines to the | ||
{attr}`.SQLAlchemy.engines` map will make them accessible through the extension, | ||
but that's not required either. You will want to call | ||
`db.sessionmaker.configure(bind=..., binds=...)` to set up these custom engines | ||
if you plan to use the provided session management though. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Flask-SQLAlchemy-Lite | ||
|
||
This [Flask]/[Quart] extension manages [SQLAlchemy] engines and sessions as part | ||
of your web application. Engines can be configured through Flask config, and | ||
sessions are manages and cleaned up as part of the app/request context. | ||
SQLAlchemy's async capabilities are supported as well, and both sync and async | ||
can be configured and used at the same time. | ||
|
||
[Flask]: https://flask.palletsprojects.com | ||
[Quart]: https://quart.palletsprojects.com | ||
[SQLAlchemy]: https://www.sqlalchemy.org | ||
|
||
Install it from PyPI using an installer such as pip: | ||
|
||
``` | ||
$ pip install Flask-SQLAlchemy-Lite | ||
``` | ||
|
||
This is intended to be a replacement for the [Flask-SQLAlchemy] extension. It | ||
provides the same `db.engine` and `db.session` interface. However, this | ||
extension avoids pretty much every other thing the former extension managed. It | ||
does not create the base model, table class, or metadata itself. It does not | ||
implement a custom bind system. It does not provide automatic table naming for | ||
models. It does not provide query recording, pagination, query methods, etc. | ||
|
||
[Flask-SQLAlchemy]: https://flask-sqlalchemy.palletsprojects.com | ||
|
||
This extension tries to do as little as possible and as close to plain | ||
SQLAlchemy as possible. You define your base model using whatever SQLAlchemy | ||
pattern you want, old or modern. You use SQLAlchemy's `session.binds` API for | ||
mapping different models to different engines. You import all names from | ||
SQLAlchemy directly, rather than using `db.Mapped`, `db.select`, etc. Sessions | ||
are tied directly to request lifetime, but can also be created and managed | ||
directly, and do not use the `scoped_session` interface. | ||
|
||
These docs cover how the extension works, _not_ how to use SQLAlchemy. Read the | ||
[SQLAlchemy docs], which include a comprehensive tutorial, to learn how to use | ||
SQLAlchemy. | ||
|
||
[SQLAlchemy docs]: https://docs.sqlalchemy.org | ||
|
||
```{toctree} | ||
:hidden: | ||
start | ||
engine | ||
session | ||
api | ||
changes | ||
license | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# MIT License | ||
|
||
```{literalinclude} ../LICENSE.txt | ||
:language: text | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Sessions | ||
|
||
A SQLAlchemy {class}`~sqlalchemy.orm.sessionmaker` is created when | ||
{meth}`.SQLAlchemy.init_app` is called. Both sync and async sessionmakers | ||
are created regardless of if any sync or async engines are defined. | ||
|
||
|
||
## Default Options | ||
|
||
Default session options can be passed as the `session_options` parameter when | ||
creating the {class}`.SQLAlchemy` instance. This applies to both sync and async | ||
sessions. You can call each sessionmaker's `configure` method if you need | ||
different options for each. | ||
|
||
|
||
## Session Management | ||
|
||
Most use cases will use one session, and tie it to the lifetime of each request. | ||
Use {attr}`db.session <SQLAlchemy.session>` for this. It will return the same | ||
session throughout a request, then close it when the request ends. SQLAlchemy | ||
will rollback any uncomitted state in the session when it is closed. | ||
|
||
You can also create other sessions besides the default. Calling | ||
{meth}`db.get_session(name)` will create separate sessions that are also closed | ||
at the end of the request. | ||
|
||
The sessions are closed when the application context is torn down. This happens | ||
for each request, but also at the end of CLI commands, and for manual | ||
`with app.app_context()` blocks. | ||
|
||
|
||
### Manual Sessions | ||
|
||
You can also use {attr}`db.sessionmaker <SQLAlchemy.sessionmaker>` directly to | ||
create sessions. These will not be closed automatically at the end of requests, | ||
so you'll need to manage them manually. An easy way to do that is using a `with` | ||
block. | ||
|
||
```python | ||
with db.sessionmaker() as session: | ||
... | ||
``` | ||
|
||
|
||
### Async | ||
|
||
SQLAlchemy warns that the async sessions it provides are _not_ safe to be used | ||
across concurrent tasks. For example, the same session should not be passed to | ||
multiple tasks when using `asyncio.gather`. Either use | ||
{meth}`db.get_async_session(name) <SQLAlchemy.get_async_session>` with a unique | ||
name for each task, or use {attr}`db.async_sessionmaker` to manage sessions | ||
and their lifetime manually. The latter is what SQLAlchemy recommends. | ||
|
||
|
||
## Multiple Binds | ||
|
||
If the `"default"` engine key is defined when initializing the extension, it | ||
will be set as the default bind for sessions. This is optional, but if you don't | ||
configure it up front, you'll want to call `db.sessionmaker.configure(bind=...)` | ||
later to set the default bind, or otherwise specify a bind for each query. | ||
|
||
SQLAlchemy supports using different engines when querying different tables or | ||
models. This requires specifying a mapping from a model, base class, or table to | ||
an engine object. When using the extension, you can set this up generically | ||
in `session_options` by mapping to names instead of engine objects. During | ||
initialization, the extension will substitute each name for the configured | ||
engine. You can also call `db.sessionmaker.configure(binds=...)` after the fact | ||
and pass the engines using {meth}`~.SQLAlchemy.get_engine` yourself. | ||
|
||
```python | ||
db = SQLAlchemy(session_options={"binds": { | ||
User: "auth", | ||
Role: "auth", | ||
ExternalBase: "external", | ||
}}) | ||
``` |
Oops, something went wrong.