Flask example — Dependency Injector 4.46.0 documentation (original) (raw)

This example shows how to use Dependency Injector with Flask.

The example application helps to search for repositories on the Github.

../_images/flask.png

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, createsFlask app and setup routes.

Listing of githubnavigator/application.py:

"""Application module."""

from flask import Flask from flask_bootstrap import Bootstrap4

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 = 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."""

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: