Flask example¶
This example shows how to use Dependency Injector with Flask.
The example application helps to search for repositories on the Github.
../_images/flask.pngThe source code is available on the Github.
Flask tutorial demonstrates how to build this application step-by-step.
Application structure¶
Application has next structure:
./ ├──githubnavigator/ │├──templates ││├──base.html ││└──index.py │├──__init__.py │├──application.py │├──containers.py │├──services.py │├──tests.py │└──views.py ├──config.yml └──requirements.txt
Container¶
Declarative container is defined in githubnavigator/containers.py:
"""Containers module.""" fromdependency_injectorimport containers, providers fromgithubimport Github from.import services classContainer(containers.DeclarativeContainer): wiring_config = containers.WiringConfiguration(modules=[".views"]) config = providers.Configuration(yaml_files=["config.yml"]) github_client = providers.Factory( Github, login_or_token=config.github.auth_token, timeout=config.github.request_timeout, ) search_service = providers.Factory( services.SearchService, github_client=github_client, )
Views¶
View has dependencies on search service and some config options. The dependencies are injected using Wiring feature.
Listing of githubnavigator/views.py:
"""Views module.""" fromflaskimport request, render_template fromdependency_injector.wiringimport inject, Provide from.servicesimport SearchService from.containersimport Container @inject defindex( search_service: SearchService = Provide[Container.search_service], default_query: str = Provide[Container.config.default.query], default_limit: int = Provide[Container.config.default.limit.as_int()], ): query = request.args.get("query", default_query) limit = request.args.get("limit", default_limit, int) repositories = search_service.search_repositories(query, limit) return render_template( "index.html", query=query, limit=limit, repositories=repositories, )
Application factory¶
Application factory creates container, wires it with the views module, creates
Flask app and setup routes.
Listing of githubnavigator/application.py:
"""Application module.""" fromflaskimport Flask fromflask_bootstrapimport Bootstrap4 from.containersimport Container from.import views defcreate_app() -> Flask: container = Container() container.config.github.auth_token.from_env("GITHUB_TOKEN") app = Flask(__name__) app.container = container app.add_url_rule("/", "index", views.index) bootstrap = Bootstrap4() bootstrap.init_app(app) return app
Tests¶
Tests use Provider overriding feature to replace github client with a mock githubnavigator/tests.py:
"""Tests module.""" fromunittestimport mock importpytest fromgithubimport Github fromflaskimport url_for from.applicationimport create_app @pytest.fixture defapp(): app = create_app() yield app app.container.unwire() deftest_index(client, app): github_client_mock = mock.Mock(spec=Github) github_client_mock.search_repositories.return_value = [ mock.Mock( html_url="repo1-url", name="repo1-name", owner=mock.Mock( login="owner1-login", html_url="owner1-url", avatar_url="owner1-avatar-url", ), get_commits=mock.Mock(return_value=[mock.Mock()]), ), mock.Mock( html_url="repo2-url", name="repo2-name", owner=mock.Mock( login="owner2-login", html_url="owner2-url", avatar_url="owner2-avatar-url", ), get_commits=mock.Mock(return_value=[mock.Mock()]), ), ] with app.container.github_client.override(github_client_mock): response = client.get(url_for("index")) assert response.status_code == 200 assert b"Results found: 2" in response.data assert b"repo1-url" in response.data assert b"repo1-name" in response.data assert b"owner1-login" in response.data assert b"owner1-url" in response.data assert b"owner1-avatar-url" in response.data assert b"repo2-url" in response.data assert b"repo2-name" in response.data assert b"owner2-login" in response.data assert b"owner2-url" in response.data assert b"owner2-avatar-url" in response.data deftest_index_no_results(client, app): github_client_mock = mock.Mock(spec=Github) github_client_mock.search_repositories.return_value = [] with app.container.github_client.override(github_client_mock): response = client.get(url_for("index")) assert response.status_code == 200 assert b"Results found: 0" in response.data
Sources¶
Explore the sources on the Github.
Sponsor the project on GitHub:
[フレーム]