Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Использование MVC паттерна в Django
Django реализует паттерн MTV (Model-Template-View) вместо классического MVC, но функционально это очень похоже. Разберёмся, как это работает и какие лучшие практики стоит применять.
1. Архитектура Django: MTV vs MVC
MVC (Model-View-Controller):
- Model — бизнес-логика и данные
- View — презентация (что видит пользователь)
- Controller — обработка запроса и логика приложения
Django MTV (Model-Template-View):
- Model — ORM модели и бизнес-логика (как в MVC Model)
- Template — HTML с переменными (как в MVC View)
- View — обработка HTTP запроса и логика (как в MVC Controller)
Джанго фактически реализует MVC, просто называет View и Controller по-другому.
2. Model — определение данных
Модели описывают структуру базы данных и бизнес-правила:
# models.py
from django.db import models
from django.utils import timezone
class User(models.Model):
"""Model: описание пользователя"""
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.name
# Бизнес-логика
def deactivate(self):
"""Деактивировать пользователя"""
self.is_active = False
self.save()
class Post(models.Model):
"""Model: статья с автором"""
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
published_at = models.DateTimeField(null=True, blank=True)
def publish(self):
"""Опубликовать пост"""
self.published_at = timezone.now()
self.save()
def is_published(self):
return self.published_at is not None
3. Views (Controllers) — обработка запросов
Вьюхи — это функции или классы, которые обрабатывают HTTP запросы:
Функциональные представления (Function-Based Views):
# views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.http import JsonResponse
from .models import User, Post
def user_list(request):
"""View: список пользователей (как Controller)"""
users = User.objects.filter(is_active=True)
return render(request, 'users/list.html', {'users': users})
def user_detail(request, user_id):
"""View: детали пользователя"""
user = get_object_or_404(User, id=user_id)
posts = user.posts.all()
return render(request, 'users/detail.html', {
'user': user,
'posts': posts,
})
def create_post(request):
"""View: создать новый пост"""
if request.method == 'POST':
title = request.POST['title']
content = request.POST['content']
author_id = request.POST['author_id']
post = Post.objects.create(
title=title,
content=content,
author_id=author_id,
)
return redirect('post_detail', post_id=post.id)
return render(request, 'posts/create.html')
def publish_post(request, post_id):
"""View: опубликовать пост"""
post = get_object_or_404(Post, id=post_id)
# Проверка прав доступа
if request.user != post.author:
return JsonResponse({'error': 'Permission denied'}, status=403)
post.publish()
return JsonResponse({'status': 'published'})
Class-Based Views (современный подход):
from django.views import View
from django.views.generic import ListView, DetailView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
class UserListView(ListView):
"""View: список пользователей (класс-ориентированный)"""
model = User
template_name = 'users/list.html'
context_object_name = 'users'
paginate_by = 20
def get_queryset(self):
return User.objects.filter(is_active=True)
class UserDetailView(DetailView):
"""View: детали пользователя"""
model = User
template_name = 'users/detail.html'
context_object_name = 'user'
pk_url_kwarg = 'user_id'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['posts'] = self.object.posts.all()
return context
class CreatePostView(LoginRequiredMixin, CreateView):
"""View: создать пост (только для авторизованных)"""
model = Post
fields = ['title', 'content']
template_name = 'posts/create.html'
success_url = reverse_lazy('post_list')
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PublishPostView(LoginRequiredMixin, View):
"""View: опубликовать пост (произвольная логика)"""
def post(self, request, post_id):
post = get_object_or_404(Post, id=post_id)
if request.user != post.author:
return JsonResponse({'error': 'Permission denied'}, status=403)
post.publish()
return JsonResponse({'status': 'published'})
4. Templates — представление данных
Темплейты отрисовывают HTML с данными из вьюхи:
<!-- templates/users/list.html -->
{% extends 'base.html' %}
{% block content %}
<h1>Users</h1>
<ul>
{% for user in users %}
<li>
<a href="{% url 'user_detail' user.id %}">{{ user.name }}</a>
<span class="email">{{ user.email }}</span>
</li>
{% endfor %}
</ul>
{% endblock %}
<!-- templates/users/detail.html -->
{% extends 'base.html' %}
{% block content %}
<h1>{{ user.name }}</h1>
<p>Email: {{ user.email }}</p>
<p>Posts: {{ user.posts.count }}</p>
<h2>Recent Posts</h2>
<ul>
{% for post in posts %}
<li>
<a href="{% url 'post_detail' post.id %}">{{ post.title }}</a>
{% if post.is_published %}
<span class="published">Published</span>
{% endif %}
</li>
{% endfor %}
</ul>
{% endblock %}
5. URLs — маршрутизация (Controller routing)
URLы связывают маршруты с вьюхами:
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('users/', views.UserListView.as_view(), name='user_list'),
path('users/<int:user_id>/', views.UserDetailView.as_view(), name='user_detail'),
path('posts/create/', views.CreatePostView.as_view(), name='create_post'),
path('posts/<int:post_id>/publish/', views.PublishPostView.as_view(), name='publish_post'),
]
6. Лучшие практики MVC в Django
Разделение ответственности:
# ❌ Плохо: логика в темплейте
<h1>{{ user.name.upper() }}</h1>
{% if user.created_at|date:"Y" == "2024" %}
<span>New user</span>
{% endif %}
# ✅ Хорошо: логика в модели или вьюхе
class User(models.Model):
def is_new(self):
return self.created_at.year == 2024
# В темплейте:
{% if user.is_new %}
<span>New user</span>
{% endif %}
Переиспользование логики:
# ✅ Создавать менеджеры и querysets для переиспользования
class ActiveUserManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
class User(models.Model):
objects = models.Manager() # default
active = ActiveUserManager()
# Использование:
active_users = User.active.all()
all_users = User.objects.all()
Services для сложной логики:
# services.py
class UserService:
@staticmethod
def create_user_with_profile(name, email):
"""Создать пользователя с профилем (сложная операция)"""
user = User.objects.create(name=name, email=email)
UserProfile.objects.create(user=user)
return user
# в views.py
from .services import UserService
class CreateUserView(CreateView):
def form_valid(self, form):
user = UserService.create_user_with_profile(
name=form.cleaned_data['name'],
email=form.cleaned_data['email'],
)
return redirect('user_detail', user_id=user.id)
Ключевые моменты
- Django использует MTV (Model-Template-View), но это по сути MVC
- Models — бизнес-логика и данные
- Views — обработка запросов (контроллеры)
- Templates — презентация данных
- Используй Class-Based Views для типичной логики
- Разделяй ответственность между моделями, вьюхами и темплейтами
- Создавай менеджеры и services для переиспользуемой логики
Правильное применение MVC паттерна в Django делает код чистым, тестируемым и легко поддерживаемым.