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

This example shows how to use Dependency Injector with Django.

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

../_images/django.png

The source code is available on the Github.

Application structure

Application has standard Django project structure. It consists of githubnavigator project package andweb application package:

./ ├── githubnavigator/ │ ├── init.py │ ├── asgi.py │ ├── containers.py │ ├── services.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── web/ │ ├── templates/ │ │ ├── base.html │ │ └── index.html │ ├── init.py │ ├── apps.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── manage.py └── 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):

config = providers.Configuration()

github_client = providers.Factory(
    Github,
    login_or_token=config.GITHUB_TOKEN,
    timeout=config.GITHUB_REQUEST_TIMEOUT,
)

search_service = providers.Factory(
    services.SearchService,
    github_client=github_client,
)

Container instance is created in githubnavigator/__init__.py:

"""Project package."""

from .containers import Container from . import settings

container = Container() container.config.from_dict(settings.dict)

Views

View has dependencies on search service and some config options. The dependencies are injected using Wiring feature.

Listing of web/views.py:

"""Views module."""

from typing import List

from django.http import HttpRequest, HttpResponse from django.shortcuts import render from dependency_injector.wiring import inject, Provide

from githubnavigator.containers import Container from githubnavigator.services import SearchService

@inject def index( request: HttpRequest, 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()], limit_options: List[int] = Provide[Container.config.LIMIT_OPTIONS], ) -> HttpResponse: query = request.GET.get("query", default_query) limit = int(request.GET.get("limit", default_limit))

repositories = search_service.search_repositories(query, limit)

return render(
    request,
    template_name="index.html",
    context={
        "query": query,
        "limit": limit,
        "limit_options": limit_options,
        "repositories": repositories,
    }
)

App config

Container is wired to the views module in the app config web/apps.py:

"""Application config module."""

from django.apps import AppConfig

from githubnavigator import container

class WebConfig(AppConfig): name = "web"

def ready(self):
    container.wire(modules=[".views"])

Tests

Tests use Provider overriding feature to replace github client with a mock web/tests.py:

"""Tests module."""

from unittest import mock

from django.urls import reverse from django.test import TestCase from github import Github

from githubnavigator import container

class IndexTests(TestCase):

def test_index(self):
    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 container.github_client.override(github_client_mock):
        response = self.client.get(reverse("index"))

    self.assertContains(response, "Results found: 2")

    self.assertContains(response, "repo1-url")
    self.assertContains(response, "repo1-name")
    self.assertContains(response, "owner1-login")
    self.assertContains(response, "owner1-url")
    self.assertContains(response, "owner1-avatar-url")

    self.assertContains(response, "repo2-url")
    self.assertContains(response, "repo2-name")
    self.assertContains(response, "owner2-login")
    self.assertContains(response, "owner2-url")
    self.assertContains(response, "owner2-avatar-url")

def test_index_no_results(self):
    github_client_mock = mock.Mock(spec=Github)
    github_client_mock.search_repositories.return_value = []

    with container.github_client.override(github_client_mock):
        response = self.client.get(reverse("index"))

    self.assertContains(response, "Results found: 0")

Sources

Explore the sources on the Github.

Sponsor the project on GitHub: