Веб-приложение на Python Django: от идеи до реализации

Раздел: Веб-разработка -> Фреймворки (Django)

Основы создания веб-приложения на Django

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


python -m venv venv
source venv/bin/activate  # Linux/Mac
venv\Scripts\activate  # Windows
pip install django
django-admin startproject myproject
cd myproject
python manage.py startapp myapp

веб приложение python django (веб-приложение на django)

В файле models.py определяется модель:


from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

После создания модели выполняется команда для создания миграций и их применения:


python manage.py makemigrations
python manage.py migrate

В views.py определяется представление, которое возвращает список статей:


from django.shortcuts import render
from .models import Article

def article_list(request):
    articles = Article.objects.all()
    return render(request, 'myapp/article_list.html', {'articles': articles})

В urls.py приложения добавляется маршрут:


from django.urls import path
from . import views

app_name = 'myapp'
urlpatterns = [
    path('', views.article_list, name='article_list'),
]

Подключаются URL-ы приложения в корневом urls.py:


from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myapp.urls')),
]

Создается шаблон templates/myapp/article_list.html:


<!DOCTYPE html>
<html>
<head><title>Статьи</title></head>
<body>
<h1>Список статей</h1>
{% for article in articles %}
    <h2>{{ article.title }}</h2>
    <p>{{ article.content|truncatewords:30 }}</p>
{% empty %}
    <p>Нет статей.</p>
{% endfor %}
</body>
</html>

После запуска сервера (python manage.py runserver) приложение становится доступным по адресу http://127.0.0.1:8000. Данный подход подходит для простых проектов, где требуется быстрое прототипирование.

Как выбрать между функциональными и классовыми представлениями?

Функциональные представления (FBV) просты в написании и подходят для несложных страниц. Классовые представления (CBV) предоставляют готовые методы для стандартных действий (список, деталь, создание, обновление, удаление) и уменьшают дублирование кода.

Пример CBV для списка статей:


from django.views.generic import ListView
from .models import Article

class ArticleListView(ListView):
    model = Article
    template_name = 'myapp/article_list.html'
    context_object_name = 'articles'

Цель использования: CBV рекомендуется применять, когда логика представления повторяет стандартные паттерны (отображение списка, деталей, форм). Если требуется нестандартная обработка запроса, FBV даёт больше гибкости.

Типичные ошибки: в CBV часто забывают указать template_name или context_object_name, что приводит к использованию шаблонов по умолчанию, которые могут отсутствовать. В FBV разработчики иногда дублируют код в разных представлениях.

Проблема: при переопределении методов CBV (например, get_queryset) можно случайно нарушить работу пагинации. Решение: всегда вызывать родительский метод через super().

Как создать RESTful API на основе моделей Django?

Для создания API используется Django REST Framework (DRF). Он предоставляет сериализаторы, классы представлений и маршрутизацию.


# serializers.py
from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'pub_date']

# views.py
from rest_framework import generics
from .models import Article
from .serializers import ArticleSerializer

class ArticleListCreate(generics.ListCreateAPIView):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

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

urlpatterns = [
    path('api/articles/', views.ArticleListCreate.as_view()),
]

После добавления rest_framework в INSTALLED_APPS и запуска сервера API становится доступным. Для полного CRUD можно использовать ViewSet и Router:


# views.py
from rest_framework import viewsets
from .models import Article
from .serializers import ArticleSerializer

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

# urls.py
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register(r'articles', views.ArticleViewSet)

urlpatterns = router.urls

Цель использования: DRF подходит для создания бэкенда под мобильные приложения, SPA на React/Vue/Angular или для интеграции с внешними сервисами.

Ошибки: неверное указание queryset в представлении, отсутствие прав доступа (permissions), нехватка сериализаторов для вложенных объектов.

Проблема: при использовании ViewSet без аутентификации все операции доступны любому. Решение: добавить permission_classes в ViewSet, например IsAuthenticated.

Как настроить интерфейс администратора Django под свои нужды?

Django предоставляет мощную административную панель, которую можно кастомизировать с помощью класса ModelAdmin в файле admin.py.


from django.contrib import admin
from .models import Article

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'pub_date', 'is_published')
    list_filter = ('pub_date', 'is_published')
    search_fields = ('title', 'content')
    fieldsets = (
        (None, {
            'fields': ('title', 'content')
        }),
        ('Дата и статус', {
            'fields': ('pub_date', 'is_published'),
            'classes': ('collapse',)
        }),
    )

Можно также добавлять встроенные модели (inlines) для связанных объектов.


class CommentInline(admin.TabularInline):
    model = Comment
    extra = 1

class ArticleAdmin(admin.ModelAdmin):
    inlines = [CommentInline]

Цель использования: кастомизация админки полезна для удобного управления данными без написания отдельного интерфейса. Часто используется в проектах, где админка выступает основным интерфейсом для контент-менеджеров.

Ошибки: неправильно указанный model в декораторе или встроенной модели, отсутствие регистрации модели в админке.

Проблема: при добавлении встроенной модели может возникнуть ошибка, если не настроен ForeignKey на связанную модель. Решение: убедиться, что модель имеет внешний ключ на основную модель.

Как обрабатывать данные форм с кастомной валидацией?

Django предоставляет классы Form и ModelForm. Кастомная валидация реализуется через методы clean_ и общий clean.


from django import forms
from .models import Article

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'content', 'is_published']

    def clean_title(self):
        title = self.cleaned_data['title']
        if len(title) < 10:
            raise forms.ValidationError('Заголовок должен содержать не менее 10 символов')
        return title

    def clean(self):
        cleaned_data = super().clean()
        content = cleaned_data.get('content')
        if content and 'spam' in content.lower():
            raise forms.ValidationError('Контент не должен содержать слово spam')

Использование формы в представлении:


from django.shortcuts import render, redirect
from .forms import ArticleForm

def create_article(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('myapp:article_list')
    else:
        form = ArticleForm()
    return render(request, 'myapp/article_form.html', {'form': form})

Цель использования: формы с кастомной валидацией необходимы, когда стандартные проверки (тип поля, обязательность) недостаточны. Подходит для любых сайтов с пользовательским вводом.

Ошибки: вызов raise forms.ValidationError без указания кода или с неправильным аргументом, забыв выполнить super().clean() при переопределении clean.

Проблема: сообщение об ошибке может не отображаться в шаблоне, если форма не передана в контекст или шаблон не содержит {{ form.errors }}. Решение: всегда рендерить {{ form.as_p }} или выводить ошибки явно.

Расширенные примеры кода с пошаговыми инструкциями

Ниже приведены дополнительные примеры, охватывающие менее распространенные, но полезные аспекты разработки на Django.

Пример 1: Использование сигналов для автоматического создания профиля пользователя

Сигналы позволяют выполнять код при определенных событиях модели. Создадим сигнал для создания профиля при регистрации нового пользователя.

Пример

# signals.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

# apps.py
from django.apps import AppConfig

class MyappConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapp'

    def ready(self):
        import myapp.signals

# __init__.py 
# (можно оставить пустым, либо указать default_app_config)

Результат: при создании User автоматически создается связанный Profile.

Пример 2: Middleware для логирования запросов

Middleware обрабатывает запрос до и после передачи его представлению. Пример логирования времени выполнения запроса.

Пример

# middleware.py
import time
from django.utils.deprecation import MiddlewareMixin

class RequestTimingMiddleware(MiddlewareMixin):
    def process_request(self, request):
        request.start_time = time.time()

    def process_response(self, request, response):
        total = time.time() - request.start_time
        print(f"Запрос к {request.path} выполнен за {total:.4f} сек")
        return response

# settings.py
MIDDLEWARE = [
    # ...
    'myapp.middleware.RequestTimingMiddleware',
]

Результат: в консоль будет выводиться время обработки каждого запроса.

Пример 3: Тестирование представлений с помощью pytest-django

Напишем тест для проверки списка статей.

Пример

# requirements.txt (добавить pytest-django)
# tests/test_views.py
import pytest
from django.urls import reverse
from myapp.models import Article

@pytest.mark.django_db
def test_article_list_view(client):
    Article.objects.create(title='Тестовая статья', content='Содержимое')
    response = client.get(reverse('myapp:article_list'))
    assert response.status_code == 200
    assert 'Тестовая статья' in response.content.decode()

Результат: выполнение pytest покажет успешное прохождение теста.

Пример 4: Использование кастомного менеджера моделей

Менеджер позволяет инкапсулировать часто используемые запросы.

Пример

# managers.py
from django.db import models

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(is_published=True)

# models.py
class Article(models.Model):
    # ...
    is_published = models.BooleanField(default=False)
    objects = models.Manager()  # стандартный менеджер
    published = PublishedManager()  # кастомный

# Использование:
# Article.published.all() - только опубликованные

Результат: вызов Article.published.all() вернет только статьи с is_published=True.

Пример 5: Команда управления для импорта данных из CSV

Создадим кастомную команду import_articles.

Пример

# management/commands/import_articles.py
from django.core.management.base import BaseCommand
from myapp.models import Article
import csv

class Command(BaseCommand):
    help = 'Импортирует статьи из CSV файла'

    def add_arguments(self, parser):
        parser.add_argument('csv_file', type=str, help='Путь к CSV файлу')

    def handle(self, *args, **options):
        with open(options['csv_file'], newline='', encoding='utf-8') as csvfile:
            reader = csv.DictReader(csvfile)
            for row in reader:
                Article.objects.create(
                    title=row['title'],
                    content=row['content'],
                    is_published=row.get('is_published', 'True') == 'True'
                )
        self.stdout.write(self.style.SUCCESS('Импорт завершен'))

# Использование:
# python manage.py import_articles articles.csv

Результат: данные из CSV будут загружены в базу данных.

Пример 6: Использование кэширования для уменьшения нагрузки на БД

Кэширование часто запрашиваемых данных с помощью Django cache framework.

Пример

from django.core.cache import cache
from .models import Article

def get_article_list():
    articles = cache.get('article_list')
    if not articles:
        articles = Article.objects.all()
        cache.set('article_list', articles, timeout=300)  # 5 минут
    return articles

# В представлении:
# articles = get_article_list()

Результат: повторные запросы к списку статей будут браться из кэша до истечения таймаута.

Веб-приложение на Django - comments

En
веб приложение python django (python)