Flask example¶
This example shows how to use Dependency Injector
with Flask.
The example application helps to search for repositories on the Github.
The 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."""
from dependency_injector import containers, providers
from github import Github
from . import services
class Container(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."""
from flask import request, render_template
from dependency_injector.wiring import inject, Provide
from .services import SearchService
from .containers import Container
@inject
def index(
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."""
from flask import Flask
from flask_bootstrap import Bootstrap
from .containers import Container
from . import views
def create_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 = Bootstrap()
bootstrap.init_app(app)
return app
Tests¶
Tests use Provider overriding feature to replace github client with a mock githubnavigator/tests.py
:
"""Tests module."""
from unittest import mock
import pytest
from github import Github
from flask import url_for
from .application import create_app
@pytest.fixture
def app():
app = create_app()
yield app
app.container.unwire()
def test_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
def test_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: |