Введение в модель-вид-контроллер в Джанго

Модель-View-Controller (MVC) шаблон является архитектурным дизайном, который выдержал испытание временем для создания веб-приложений, которые являются модульными, поддерживающими и тестируемыми. В то время как Django, веб-фреймворк высокого уровня Python, использует свою собственную терминологию - Модели, Виды и Шаблоны - основные принципы тесно связаны с MVC. Понимая, как картировать и расширять эти концепции, вы можете создать код, который легче рассуждать, расширять и проверять с помощью автоматизированных тестов.

В этой статье подробно рассматривается шаблон MVC, показывающий, как Django реализует каждый компонент и как вы можете применять дополнительные уровни абстракции, чтобы сохранить свою кодовую базу чистой и масштабируемой. Мы рассмотрим лучшие практики для организации бизнес-логики, написания эффективных тестов и использования встроенных инструментов Django для обеспечения разделения проблем.

Расшифровка триады MVC в Джанго

Модель MVC разделяет приложение на три взаимосвязанных компонента:

  • Модель — управляет данными и бизнес-логикой.
  • View — Обрабатывает логику представления и пользовательский интерфейс.
  • Контроллер (FLT:0) — обрабатывает пользовательский ввод и координаты между моделью и представлением.

Django немного переосмысляет эти роли. Его «Модель» соответствует непосредственно модели MVC. «Вид» в Django действует как контроллер — он получает HTTP-запросы, запрашивает модель и возвращает ответ. «Темплат» служит MVC View, визуализируя HTML (или другие форматы вывода) для клиента. Это отображение имеет решающее значение для предотвращения путаницы при обсуждении архитектуры Django.

Модельный слой Django

Django предоставляет мощную систему объектно-реляционного картирования (ORM), которая позволяет определять модели данных как классы Python. Каждая модель отображает таблицу баз данных, а экземпляры соответствуют строкам. Используя поля моделей Django, вы можете определять столбцы, отношения (ForeignKey, ManyToManyField, OneToOneField) и ограничения. ORM абстрагирует SQL, позволяя писать код Python для операций с базами данных.

Чтобы держать модели в чистоте и сосредоточенности, следуйте этим практикам:

  • Сохраняйте бизнес-логику вне моделей, когда это возможно — Модели должны в первую очередь определять структуру данных и отношения, а не сложные алгоритмы или правила проверки, которые охватывают несколько объектов.
  • Использовать менеджеры моделей и наборы запросов — Инкапсулировать общие запросы в пользовательских менеджеров. Это сохраняет взгляды наклонными и способствует повторному использованию.
  • Методы моделирования рычагов для простых операций — Методы, подобные или вычисление свойства, приемлемы, но избегают тяжелой логики, которая взаимодействует с другими моделями.
  • Письменные миграции для изменения схемы — миграционная система Django отслеживает изменения и позволяет безопасно развертывать их в различных средах.

Django рассматривает как контроллеры

Виды Django выполняют роль контроллера: они принимают HTTP-запрос, выполняют необходимые операции (часто запрашивая базу данных через модели) и возвращают HTTP-ответ. Django поддерживает два основных шаблона просмотра: представления на основе функций (FBV) и представления на основе классов (CBV).

Представления на основе функциональности просты и явны. Они идеально подходят для небольших одноцелевых конечных точек. Однако по мере роста вашего приложения вы можете обнаружить, что повторяете код (например, обрабатываете пагинацию, перечисляете объекты или проверяете форму).

Классовые представления обеспечивают встроенное общее поведение для общих задач (ListView, DetailView, CreateView и т. д. Они поощряют повторное использование кода посредством наследования и микширования. Например, ListView автоматически обрабатывает патгинацию, фильтрацию запросов и рендеринг контекста. Вы можете переопределить такие методы, как или , чтобы настроить поведение без переписывания boilerplate.

Независимо от выбранного шаблона, сохраняйте тонкие взгляды. Введите бизнес-логику в служебные модули, формы или функции полезности. Это облегчает тестирование просмотров и уменьшает дублирование.

Шаблоны как виды

Шаблонный движок Django отображает HTML, комбинируя статическую разметку с динамическими переменными контекста. Шаблоны образуют слой представления («Вид» в классическом MVC). Система наследования шаблонов Django позволяет создавать базовый скелет и расширять его в детских шаблонах, уменьшая избыточность и упрощая изменения макета.

Наилучшие практики для шаблонов включают:

  • Сохраняйте логику минимальной — Шаблоны должны содержать только логику представления (отслеживание списков, условное отображение элементов).
  • Используйте пользовательские шаблонные теги и фильтры — Для сложного форматирования или многоразовых компонентов пользовательского интерфейса создайте свои собственные теги или фильтры, а не помещайте логику в шаблон.
  • Организуйте шаблоны с помощью приложения — размещайте шаблоны в каталогах, названных в честь приложения (например, ), чтобы избежать столкновений пространства имен.

Создание модульной кодовой базы с помощью приложений Django

Модульность в Django начинается с концепции приложений. Каждый проект Django может состоять из нескольких автономных приложений, каждое из которых отвечает за отдельный домен или функцию. Например, сайт электронной коммерции может иметь приложения для , , и . Каждое приложение инкапсулирует свои собственные модели, представления, шаблоны, статические файлы и тесты.

Чтобы приложения были действительно модульными:

  • Определение четких границ — приложение должно иметь единую ответственность и минимальную связь с другими приложениями. Используйте сигналы Django или пользовательское промежуточное ПО для связи между приложениями без жесткого импорта.
  • Многоразовые приложения — Разработайте свои приложения, чтобы их можно было извлекать и повторно использовать в других проектах. Это означает избегание жестко закодированных настроек или прямых ссылок на корневые URL-адреса проекта.
  • Использовать инъекцию зависимости — Когда приложение нуждается в чём-то из другого приложения, передайте его в качестве параметра или используйте сервисный уровень, который можно высмеять в тестах.

Разделение бизнес-логики: уровень сервиса

Одним из наиболее эффективных способов повышения проверяемости и модульности является введение уровня сервиса. Модуль сервиса содержит чистую бизнес-логику, свободную от обработки запросов/ответов Django. Такое разделение позволяет тестировать критические алгоритмы без включения веб-сервера или базы данных (если только это не требуется для сохранения данных).

Например, вместо того, чтобы писать логику обработки внутри представления, которое обрабатывает платеж:

def process_payment(request):
 # ... fetch user, order
 # ... call Stripe API
 # ... update order status
 return redirect('success')

Вы можете перевести взаимодействие Stripe в сервис:

# services/payment_service.py
class PaymentService:
 def process(self, user, order, payment_info):
 # Stripe API calls, business rules, etc.
 # Return result and possibly new order status
 pass

Затем вид просто вызывает службу и обрабатывает ответ. Такой подход позволяет легко протестировать логику оплаты, высмеивая внешние API и вызовы базы данных.

Написание тестируемого кода в Django

Тестируемость является прямым преимуществом чистого разделения MVC. Когда компоненты разъединены, вы можете тестировать их изолированно. Встроенная тестовая структура Django (на основе Python ) предоставляет инструменты для создания тестов для моделей, просмотров, шаблонов и форм. Кроме того, сторонние библиотеки, такие как , предлагают более лаконичный синтаксис и мощные светильники.

Тестирование моделей

Тесты моделей должны проверять, что структура данных и бизнес-правила (если они встроены в модель) работают правильно. Используйте класс и создайте примеры моделей в рамках методов тестирования. Например, проверьте, что метод модели возвращает ожидаемую строку, или что метод пользовательского менеджера фильтрует правильно.

from django.test import TestCase
from .models import Product

class ProductModelTest(TestCase):
 def test_string_representation(self):
 product = Product(name='Test Product')
 self.assertEqual(str(product), product.name)

Для более сложной логики модели рассмотрите возможность использования для замены внешних зависимостей, таких как отправка электронной почты или сторонние вызовы API.

Тестирование (контроллеры)

Тестовый клиент Django позволяет имитировать HTTP-запросы и проверять ответы. Это идеально подходит для интеграционных тестов, которые проверяют маршрутизацию, аутентификацию и рендеринг шаблонов. Однако для модульного тестирования логики внутри просмотров следует разделить эту логику на службы или другие модули.

При тестировании классовых просмотров вы можете непосредственно инстанцировать класс просмотра (или использовать тестовый клиент) и утверждать на контекстных данных, кодах состояния и перенаправлениях. Убедитесь, что вы тестируете как счастливые пути, так и сценарии ошибок (например, отказ в разрешении, ошибки проверки формы).

Тестирование шаблонов

Логика шаблонов лучше всего тестируется косвенно с помощью тестов просмотра, которые проверяют, что отображаемый HTML содержит ожидаемые элементы. Для сложных тегов шаблонов или фильтров напишите выделенные тесты блоков. Классы Django и позволяют отображать строку шаблона и утверждать на выходе.

from django.template import Template, Context
from django.test import TestCase

class CustomTagTest(TestCase):
 def test_uppercase_filter(self):
 t = Template('{% load my_filters %}{{ value|my_upper }}')
 c = Context({'value': 'hello'})
 self.assertEqual(t.render(c), 'HELLO')

Смешивание и инъекция зависимости

Чтобы писать быстрые, сфокусированные единичные тесты, используйте для имитации внешних сервисов, запросов к базе данных или даже ORM Django. Например, при тестировании службы, которая отправляет электронное письмо, высмеивайте функцию , чтобы избежать фактической доставки электронной почты.

Инъекция зависимостей — это еще один метод: передавайте все зависимости (сессии баз данных, внешние API, конфигурация) явно своим функциям или конструкторам классов.

Лучшие практики для чистого кода Django

Помимо базового картирования MVC, несколько методов помогают поддерживать модульное и тестируемое приложение Django:

  • Использовать встроенные сигналы Django экономно — Сигналы могут создавать скрытые зависимости и усложнять отладку. Предпочитают явные вызовы для методов обслуживания по логике, управляемой сигналом.
  • Сохраняйте URL-адреса чистыми — Организуйте шаблоны URL в приложении, используйте именные пространства и избегайте вставки сложной логики .
  • Переносное ПО для межсекторальных задач — Аутентификация, регистрация и проверка запросов являются хорошими кандидатами на промежуточное ПО.
  • Написать документацию и подсказки типа — Хорошо задокументированный код с аннотациями типа легче понять и рефакторировать. Используйте модуль Python и рассмотрите такие инструменты, как для статического анализа.
  • Принять последовательный стиль кодирования — Следуйте PEP 8, используйте литеры (flake8, pylint) и формататоры (черный, исорт).Это снижает когнитивную нагрузку и делает обзоры кода более плавными.

Пример из реального мира: создание блога с модульным MVC в Джанго

Давайте применим принципы к простому приложению для блога. Мы определим модель , сервис для создания постов с валидацией, классный вид для списка и создания постов и шаблон с наследованием.

Модель :

from django.db import models
from django.urls import reverse

class Post(models.Model):
 title = models.CharField(max_length=200)
 content = models.TextField()
 published = models.BooleanField(default=False)
 created_at = models.DateTimeField(auto_now_add=True)

 def get_absolute_url(self):
 return reverse('post_detail', args=[self.pk])

Услуга :

from .models import Post

class PostService:
 def create_post(self, title, content, user):
 # business logic: check permissions, validate content length, etc.
 if len(content) < 50:
 raise ValueError("Content must be at least 50 characters.")
 post = Post.objects.create(title=title, content=content, author=user)
 return post

Вид :

from django.views.generic import ListView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .services import PostService
from .models import Post

class PostListView(ListView):
 model = Post
 template_name = 'blog/post_list.html'
 queryset = Post.objects.filter(published=True)

class PostCreateView(LoginRequiredMixin, CreateView):
 model = Post
 fields = ['title', 'content']
 template_name = 'blog/post_form.html'

 def form_valid(self, form):
 # Use service to enforce additional rules
 service = PostService()
 try:
 post = service.create_post(
 title=form.cleaned_data['title'],
 content=form.cleaned_data['content'],
 user=self.request.user
 )
 except ValueError as e:
 form.add_error(None, str(e))
 return self.form_invalid(form)
 return super().form_valid(form)

Запишите :

{% extends "base.html" %}
{% block content %}
 {% for post in post_list %}
 
 {% endfor %}
{% endblock %}

This structure keeps the view lean, the model focused, and the business logic isolated in a service that can be unit-tested easily.

Тестирование сервиса блога

Вот модульный тест для FLT:33, использующий макеты объектов, чтобы избежать попаданий в базу данных (вы также можете использовать тестовую базу данных Django для интеграции):

from django.test import TestCase
from unittest.mock import patch, MagicMock
from .services import PostService

class PostServiceTest(TestCase):
 @patch('blog.services.Post.objects.create')
 def test_create_post_short_content_raises_error(self, mock_create):
 service = PostService()
 with self.assertRaises(ValueError):
 service.create_post("Title", "Short", mock_some_user)
 mock_create.assert_not_called()

Для более тщательных тестов используйте Django и реальную базу данных, чтобы убедиться, что на самом деле сохраняет сообщение и что поле автора установлено правильно.

Внешние рекомендации

Чтобы углубить свое понимание MVC в Django и связанных с ним методов тестирования, изучите эти ресурсы:

Заключение

Реализация шаблона MVC в Django заключается не в строгом следовании определениям учебников, а в принятии основной идеи разделения проблем. Рассматривая модели Django как представление данных, представления как контроллеры и шаблоны как уровень представления, вы, естественно, создаете более модульное и тестируемое приложение. Внедрение уровня обслуживания еще больше разъединяет бизнес-логику, облегчая написание быстрых единичных тестов и адаптацию к изменяющимся требованиям.

По мере роста ваших проектов эти принципы становятся еще более ценными. Хорошо структурированная кодовая база Django уменьшает технический долг, упрощает абордаж для новых разработчиков и гарантирует, что ваше приложение может развиваться без каскадных поломок. Примите дух MVC, и ваши приложения Django будут надежными, поддерживающими и готовыми к вызовам производства.