Django example¶
This example shows how to use Dependency Injector
with Django.
The example application helps to search for repositories on the Github.
The source code is available on the Github.
Application structure¶
Application has standard Django project structure. It consists of githubnavigator
project package and
web
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: |