← Назад к вопросам

Как создать представления (views) в Django?

1.0 Junior🔥 261 комментариев
#Django#REST API и HTTP

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как создать представления (views) в Django

View (представление) в Django — это функция или класс, которые получают Web request и возвращают Web response. Это основной компонент архитектуры MVC в Django, обрабатывающий бизнес-логику.

1. Function-Based Views (FBV)

Самый простой способ создать view — как функцию:

from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, get_object_or_404
from myapp.models import Post

# Простой view
def hello_world(request):
    return HttpResponse("Hello, World!")

# View с шаблоном
def post_list(request):
    posts = Post.objects.all()
    return render(request, 'post_list.html', {'posts': posts})

# View с параметром из URL
def post_detail(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    return render(request, 'post_detail.html', {'post': post})

# JSON response
def api_posts(request):
    posts = list(Post.objects.values('id', 'title', 'content'))
    return JsonResponse({'posts': posts})

# Обработка разных методов
def post_handler(request):
    if request.method == 'GET':
        posts = Post.objects.all()
        return render(request, 'posts.html', {'posts': posts})
    elif request.method == 'POST':
        title = request.POST.get('title')
        content = request.POST.get('content')
        post = Post.objects.create(title=title, content=content)
        return JsonResponse({'id': post.id, 'message': 'Created'})

2. Class-Based Views (CBV)

Для сложной логики лучше использовать классы:

from django.views import View
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin

# Простой класс-view
class PostListView(View):
    def get(self, request):
        posts = Post.objects.all()
        return render(request, 'post_list.html', {'posts': posts})
    
    def post(self, request):
        title = request.POST.get('title')
        content = request.POST.get('content')
        post = Post.objects.create(title=title, content=content)
        return JsonResponse({'id': post.id})

# Встроенный ListView (автоматически)
class PostListView(ListView):
    model = Post
    template_name = 'post_list.html'  # по умолчанию: post_list.html
    context_object_name = 'posts'      # по умолчанию: object_list
    paginate_by = 10
    
    def get_queryset(self):
        return Post.objects.filter(published=True)
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'All Posts'
        return context

# DetailView для одного объекта
class PostDetailView(DetailView):
    model = Post
    template_name = 'post_detail.html'
    context_object_name = 'post'
    pk_url_kwarg = 'post_id'  # параметр из URL

# CreateView для создания
class PostCreateView(LoginRequiredMixin, CreateView):
    model = Post
    fields = ['title', 'content']
    template_name = 'post_form.html'
    success_url = reverse_lazy('post_list')
    
    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

# UpdateView для редактирования
class PostUpdateView(LoginRequiredMixin, UpdateView):
    model = Post
    fields = ['title', 'content']
    template_name = 'post_form.html'
    pk_url_kwarg = 'post_id'
    success_url = reverse_lazy('post_list')

# DeleteView для удаления
class PostDeleteView(LoginRequiredMixin, DeleteView):
    model = Post
    pk_url_kwarg = 'post_id'
    success_url = reverse_lazy('post_list')

3. Миксины (Mixins) для переиспользования логики

from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from django.http import Http404

# Кастомный миксин для проверки владельца
class OwnerRequiredMixin(LoginRequiredMixin):
    """Проверяет, что пользователь — владелец объекта"""
    def get_object(self, queryset=None):
        obj = super().get_object(queryset)
        if obj.author != self.request.user:
            raise Http404("You don't own this post")
        return obj

# Миксин для логирования доступа
class LogAccessMixin:
    """Логирует доступ к view"""
    def get(self, request, *args, **kwargs):
        print(f"User {request.user} accessed {self.__class__.__name__}")
        return super().get(request, *args, **kwargs)

# Комбинирование миксинов
class MyProtectedView(OwnerRequiredMixin, LogAccessMixin, DetailView):
    model = Post
    template_name = 'post_detail.html'

4. API Views с JSON

from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
import json

# Декоратор для ограничения методов
@require_http_methods(["GET", "POST"])
def api_posts(request):
    if request.method == 'GET':
        posts = Post.objects.values('id', 'title')
        return JsonResponse({'data': list(posts)})
    
    elif request.method == 'POST':
        try:
            data = json.loads(request.body)
            post = Post.objects.create(
                title=data['title'],
                content=data['content']
            )
            return JsonResponse({
                'id': post.id,
                'message': 'Created'
            }, status=201)
        except json.JSONDecodeError:
            return JsonResponse({'error': 'Invalid JSON'}, status=400)

# REST API view
from django.shortcuts import get_object_or_404

@require_http_methods(["GET", "PUT", "DELETE"])
def api_post_detail(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    
    if request.method == 'GET':
        return JsonResponse({
            'id': post.id,
            'title': post.title,
            'content': post.content
        })
    
    elif request.method == 'PUT':
        data = json.loads(request.body)
        post.title = data.get('title', post.title)
        post.content = data.get('content', post.content)
        post.save()
        return JsonResponse({'message': 'Updated'})
    
    elif request.method == 'DELETE':
        post.delete()
        return JsonResponse({'message': 'Deleted'}, status=204)

5. Обработка формм

from django import forms
from django.shortcuts import render, redirect
from myapp.models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content']

def post_create(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            form.instance.author = request.user
            form.save()
            return redirect('post_list')
    else:
        form = PostForm()
    
    return render(request, 'post_form.html', {'form': form})

6. Request и Response объекты

from django.http import HttpResponse, JsonResponse, FileResponse
from django.shortcuts import render

def request_info(request):
    # Информация о request
    method = request.method  # GET, POST, PUT и т.д.
    path = request.path     # /posts/
    user = request.user     # Текущий пользователь
    
    # GET параметры
    page = request.GET.get('page', 1)
    search = request.GET.get('search', '')
    
    # POST данные
    if request.method == 'POST':
        title = request.POST.get('title')
        content = request.POST.get('content')
    
    # JSON data
    if request.content_type == 'application/json':
        import json
        data = json.loads(request.body)
    
    # Headers
    user_agent = request.META.get('HTTP_USER_AGENT')
    
    # Различные responses
    return render(request, 'template.html')  # HTML
    return JsonResponse({'status': 'ok'})    # JSON
    return HttpResponse("Text")              # Plain text
    return FileResponse(open('file.pdf'))    # File download

7. Middleware и декораторы для views

from django.utils.decorators import decorator_from_middleware
from django.http import HttpResponseForbidden
from functools import wraps

# Кастомный декоратор
def admin_only(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if not request.user.is_staff:
            return HttpResponseForbidden("Admin only")
        return view_func(request, *args, **kwargs)
    return wrapper

@admin_only
def admin_panel(request):
    return render(request, 'admin.html')

# Встроенные декораторы
from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import csrf_exempt

@cache_page(60 * 5)  # Кэш на 5 минут
def cached_view(request):
    return render(request, 'expensive.html')

@csrf_exempt  # Отключить CSRF проверку (будьте осторожны!)
def webhook_view(request):
    return JsonResponse({'status': 'ok'})

8. Асинхронные views (Django 3.1+)

from django.views import View
from django.views.decorators.http import require_http_methods
import asyncio

# Асинхронная функция-view
async def async_posts(request):
    posts = await sync_to_async(Post.objects.all)()
    context = {'posts': posts}
    return render(request, 'posts.html', context)

# Асинхронный класс-view
class AsyncPostListView(View):
    async def get(self, request):
        from django.db.models import Count
        from asgiref.sync import sync_to_async
        
        posts = await sync_to_async(list)(Post.objects.all())
        return render(request, 'posts.html', {'posts': posts})

9. Обработка ошибок в views

from django.http import HttpResponseNotFound, HttpResponseServerError
from django.core.exceptions import PermissionDenied

def safe_view(request, post_id):
    try:
        post = Post.objects.get(id=post_id)
    except Post.DoesNotExist:
        return HttpResponseNotFound("Post not found")
    
    if not post.is_published and post.author != request.user:
        raise PermissionDenied("You can't access this post")
    
    try:
        # Опасная операция
        data = expensive_operation(post)
    except Exception as e:
        return HttpResponseServerError(f"Error: {e}")
    
    return render(request, 'post_detail.html', {'post': post, 'data': data})

10. URLs и routing

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    # FBV
    path('posts/', views.post_list, name='post_list'),
    path('posts/<int:post_id>/', views.post_detail, name='post_detail'),
    
    # CBV
    path('posts/', views.PostListView.as_view(), name='post_list'),
    path('posts/<int:pk>/', views.PostDetailView.as_view(), name='post_detail'),
    path('posts/create/', views.PostCreateView.as_view(), name='post_create'),
    path('posts/<int:pk>/edit/', views.PostUpdateView.as_view(), name='post_edit'),
    path('posts/<int:pk>/delete/', views.PostDeleteView.as_view(), name='post_delete'),
    
    # API
    path('api/posts/', views.api_posts, name='api_posts'),
    path('api/posts/<int:post_id>/', views.api_post_detail, name='api_post_detail'),
]

Best Practices

# ✓ Хорошо: Logic в views, templates для рендеринга
def post_list(request):
    posts = Post.objects.filter(published=True)
    return render(request, 'post_list.html', {'posts': posts})

# ✗ Плохо: HTML в view
def post_list(request):
    posts = Post.objects.all()
    html = f"<h1>Posts: {len(posts)}</h1>"
    return HttpResponse(html)

# ✓ Хорошо: Используй get_object_or_404
post = get_object_or_404(Post, id=post_id)

# ✗ Плохо: Обработка исключения вручную
try:
    post = Post.objects.get(id=post_id)
except Post.DoesNotExist:
    return "Not found"

# ✓ Хорошо: Миксины для переиспользования
class MyProtectedView(LoginRequiredMixin, DetailView):
    pass

Заключение

Django views — это сердце приложения, где обрабатывается бизнес-логика. Выбор между FBV и CBV зависит от сложности: для простого кода — функции, для сложного с переиспользованием — классы и миксины. Главное — разделение ответственности: views получают данные из моделей, рендерят шаблоны, и возвращают responses пользователю.