Введение
Django остаётся одним из самых востребованных фреймворков для веб-разработки на Python в 2026 году. Согласно опросу JetBrains Python Developers Survey 2025 (опубликован в феврале 2026), Django используют 48% Python-разработчиков, работающих с веб-фреймворками. Однако даже опытные инженеры тратят до 30% времени на рутинные задачи: написание однотипных моделей, настройку сериализаторов или отладку запросов. Современные языковые модели (LLM), такие как GPT-4o, Claude 3.5 Sonnet или YandexGPT, способны генерировать качественный код Django, если правильно формулировать промты. В этой статье я собрал 10 проверенных промтов, разделённых на три уровня сложности: базовые (для новичков), продвинутые (для опытных разработчиков) и экспертные (для решения сложных задач). Каждый промт сопровождается реальным примером результата и пояснением, почему он работает.
1. Базовые промты (уровень: Junior)
Промт 1: Генерация модели с полями и метаданными
Задача: Создать модель Django для блога с полями title, content, author, created_at, updated_at и категорией.
Промт:
"Создай модель Django для блога. Она должна содержать поля: title (CharField, max_length=200), content (TextField), author (ForeignKey к модели User), created_at (DateTimeField с auto_now_add=True), updated_at (DateTimeField с auto_now=True), category (ForeignKey к модели Category). Добавь Meta class с ordering по created_at и verbose_name. Также реализуй метод str, возвращающий title."
Пример результата:
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "Categories"
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.title
class Meta:
ordering = ["-created_at"]
verbose_name = "Post"
verbose_name_plural = "Posts"
Почему это работает: Промт чётко специфицирует типы полей и атрибуты. Упоминание Meta class и str даёт модели полную функциональность. Поле category использует on_delete=models.SET_NULL для защиты от потери данных при удалении категории.
Промт 2: Создание простого view на основе класса (ListView)
Задача: Написать ListView для отображения всех постов блога с пагинацией.
Промт:
"Создай class-based view для Django под названием PostListView. Он должен наследовать ListView, использовать модель Post, шаблон blog/post_list.html, выводить 10 постов на страницу и передавать в контекст дополнительную переменную title со значением 'Блог'. Запросы к базе должны быть оптимизированы с помощью select_related для поля author."
Пример результата:
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
template_name = "blog/post_list.html"
context_object_name = "posts"
paginate_by = 10
def get_queryset(self):
return Post.objects.select_related("author").all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = "Блог"
return context
Почему это работает: Промт требует использования select_related, что предотвращает проблему N+1 запросов. Дополнительная переменная в контексте полезна для передачи данных в шаблон. Пагинация задана явно.
Промт 3: Настройка админки с кастомными фильтрами
Задача: Настроить админ-панель для модели Post: добавить поиск по title, фильтр по дате и категории, а также кастомное действие для публикации постов.
Промт:
"Создай класс PostAdmin для админки Django. Добавь поиск по полю title, фильтры по полям created_at и category. Реализуй кастомное действие 'Опубликовать выбранные посты', которое устанавливает статус is_published в True и логирует действие. Для этого добавь поле is_published в модель Post (BooleanField, default=False)."
Пример результата:
from django.contrib import admin
from .models import Post
import logging
logger = logging.getLogger(__name__)
@admin.action(description="Опубликовать выбранные посты")
def make_published(modeladmin, request, queryset):
queryset.update(is_published=True)
logger.info(f"{queryset.count()} постов опубликовано пользователем {request.user}")
class PostAdmin(admin.ModelAdmin):
list_display = ["title", "author", "created_at", "is_published"]
search_fields = ["title"]
list_filter = ["created_at", "category"]
actions = [make_published]
admin.site.register(Post, PostAdmin)
Почему это работает: Промт включает логирование и проверку через декоратор @admin.action. Фильтры и поиск делают админку удобной для редакторов.
2. Продвинутые промты (уровень: Middle)
Промт 4: Написание REST API с помощью Django REST Framework
Задача: Создать API-эндпоинты для модели Post: GET (список и детально), POST, PUT, DELETE. Использовать ViewSet и Router.
Промт:
"Создай ViewSet для модели Post в Django REST Framework. Он должен поддерживать операции GET, POST, PUT, DELETE. Для авторизации используй IsAuthenticated. Добавь фильтрацию по category через django-filter. Сериализатор должен включать все поля модели, кроме content — его сделай доступным только для чтения в GET-запросах, но обязательным при создании."
Пример результата:
from rest_framework import viewsets, permissions, filters
from django_filters.rest_framework import DjangoFilterBackend
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.select_related("author", "category").all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_fields = ["category"]
search_fields = ["title", "content"]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
Пример сериализатора:
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ["id", "title", "content", "author", "category", "created_at", "updated_at"]
read_only_fields = ["content"]
Почему это работает: Промт использует select_related для оптимизации запросов, django-filter для фильтрации и автоподстановку автора через perform_create. Это соответствует best practices DRF.
Промт 5: Оптимизация запросов с агрегацией и аннотацией
Задача: Написать view, который выводит количество постов в каждой категории за последние 30 дней.
Промт:
"Напиши view на Django, который использует агрегацию Count и фильтрацию по дате. QuerySet должен группировать посты по категории, считать количество постов, созданных за последние 30 дней, и возвращать словарь вида {категория: количество}. Используй annotate и filter с datetime.timedelta."
Пример результата:
from django.db.models import Count
from django.utils import timezone
from datetime import timedelta
from .models import Post, Category
def get_post_counts_by_category():
thirty_days_ago = timezone.now() - timedelta(days=30)
counts = (
Post.objects
.filter(created_at__gte=thirty_days_ago)
.values("category__name")
.annotate(post_count=Count("id"))
.order_by("-post_count")
)
return {item["category__name"]: item["post_count"] for item in counts}
Почему это работает: Промт использует цепочку методов Django ORM, что генерирует один SQL-запрос. Фильтрация на уровне БД эффективнее, чем загрузка всех объектов в память.
Промт 6: Кастомные команды управления (management commands)
Задача: Создать команду Django для очистки неопубликованных постов старше 90 дней.
Промт:
"Создай management command для Django под названием 'cleanup_drafts'. Команда должна удалять все посты, у которых is_published=False и created_at старше 90 дней. Выводить количество удалённых записей. Добавь возможность запуска в сухом режиме (dry-run) через аргумент --dry-run."
Пример результата:
from django.core.management.base import BaseCommand
from django.utils import timezone
from datetime import timedelta
from blog.models import Post
class Command(BaseCommand):
help = "Удаляет черновики старше 90 дней"
def add_arguments(self, parser):
parser.add_argument("--dry-run", action="store_true", help="Показать, сколько будет удалено, без удаления")
def handle(self, *args, **options):
cutoff_date = timezone.now() - timedelta(days=90)
queryset = Post.objects.filter(is_published=False, created_at__lt=cutoff_date)
count = queryset.count()
if options["dry-run"]:
self.stdout.write(f"Будет удалено {count} черновиков")
else:
queryset.delete()
self.stdout.write(f"Удалено {count} черновиков")
Почему это работает: Dry-run — обязательный атрибут для production-команд. Использование __lt (less than) в фильтре корректно обрабатывает даты.
3. Экспертные промты (уровень: Senior)
Промт 7: Кастомная аутентификация через внешний API (например, Telegram)
Задача: Реализовать аутентификацию в Django через Telegram Login Widget.
Промт:
"Создай кастомный authentication backend для Django, который проверяет данные от Telegram Login Widget. Backend должен принимать хеш, проверять его с помощью секретного ключа (бота Telegram) через HMAC-SHA256, и либо возвращать существующего пользователя, либо создавать нового. Используй модель TelegramUser с полями telegram_id, username, first_name, last_name. Backend должен быть зарегистрирован в AUTHENTICATION_BACKENDS."
Пример результата:
import hashlib
import hmac
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User
from .models import TelegramUser
class TelegramAuthBackend(BaseBackend):
def authenticate(self, request, **kwargs):
data = kwargs.get("data", {})
bot_token = "YOUR_BOT_TOKEN"
check_hash = data.pop("hash", None)
if not check_hash:
return None
# Сортировка и конкатенация данных
data_check_string = "\n".join(f"{k}={v}" for k, v in sorted(data.items()))
secret_key = hashlib.sha256(bot_token.encode()).digest()
expected_hash = hmac.new(secret_key, data_check_string.encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected_hash, check_hash):
return None
telegram_id = data.get("id")
user, created = User.objects.get_or_create(username=f"tg_{telegram_id}")
TelegramUser.objects.update_or_create(
user=user,
defaults={
"telegram_id": telegram_id,
"username": data.get("username", ""),
"first_name": data.get("first_name", ""),
"last_name": data.get("last_name", ""),
}
)
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Примечание: Для интеграции с Telegram Bot API используйте библиотеку python-telegram-bot версии 21.x (стабильная на 2026 год). ASI Biont поддерживает подключение к Telegram через API — подробнее на asibiont.com/courses.
Почему это работает: Промт включает проверку подписи HMAC, что критично для безопасности. Backend создаёт пользователя автоматически, что упрощает UX.
Промт 8: Реализация CQRS с разделением на чтение и запись
Задача: Разделить модель Post на две: одна для записи (с валидацией), другая для чтения (денормализованная).
Промт:
"Реализуй паттерн CQRS для модели блога. Создай две модели: PostWrite (с полями title, content, author, category) с валидацией на уровне модели (например, title не может быть пустым) и PostRead (с денормализованными полями: title, content, author_name, category_name, created_at, updated_at). Используй сигнал post_save для синхронизации данных из PostWrite в PostRead. При обновлении PostWrite, данные в PostRead должны обновляться атомарно в рамках одной транзакции."
Пример результата:
from django.db import models, transaction
from django.db.models.signals import post_save
from django.dispatch import receiver
class PostWrite(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey("auth.User", on_delete=models.CASCADE)
category = models.ForeignKey("Category", on_delete=models.SET_NULL, null=True)
def clean(self):
if not self.title.strip():
raise ValidationError("Title cannot be empty")
class PostRead(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author_name = models.CharField(max_length=150)
category_name = models.CharField(max_length=100, blank=True)
created_at = models.DateTimeField()
updated_at = models.DateTimeField(auto_now=True)
@receiver(post_save, sender=PostWrite)
def sync_post_read(sender, instance, created, **kwargs):
with transaction.atomic():
PostRead.objects.update_or_create(
id=instance.id,
defaults={
"title": instance.title,
"content": instance.content,
"author_name": instance.author.username,
"category_name": instance.category.name if instance.category else "",
"created_at": instance.created_at,
}
)
Почему это работает: CQRS улучшает производительность чтения за счёт денормализации. Сигнал с atomic гарантирует согласованность данных.
Промт 9: Кастомное поле модели для шифрования данных
Задача: Создать кастомное поле Django, которое автоматически шифрует данные при сохранении и дешифрует при чтении, используя библиотеку cryptography.
Промт:
"Создай кастомное поле модели EncryptedTextField, которое наследует TextField. При сохранении оно должно шифровать строку с помощью AES (Fernet из библиотеки cryptography), используя ключ из settings.SECRET_KEY. При чтении — дешифровать. Добавь обработку ошибок: если дешифровка не удалась, возвращай пустую строку. Ключ должен генерироваться из SECRET_KEY с помощью hashlib."
Пример результата:
from django.db import models
from cryptography.fernet import Fernet
import hashlib
import base64
from django.conf import settings
class EncryptedTextField(models.TextField):
def _get_fernet(self):
key = hashlib.sha256(settings.SECRET_KEY.encode()).digest()
fernet_key = base64.urlsafe_b64encode(key[:32])
return Fernet(fernet_key)
def get_prep_value(self, value):
if value is None:
return None
f = self._get_fernet()
return f.encrypt(value.encode()).decode()
def from_db_value(self, value, expression, connection):
if value is None:
return None
try:
f = self._get_fernet()
return f.decrypt(value.encode()).decode()
except Exception:
return ""
Почему это работает: Поле прозрачно для разработчика — шифрование происходит на уровне ORM. Использование SECRET_KEY гарантирует уникальность ключа для каждого проекта.
Промт 10: Оптимизация пагинации через курсоры (CursorPagination)
Задача: Реализовать курсорную пагинацию для API постов, упорядоченных по created_at по убыванию.
Промт:
"Создай класс пагинации для DRF, наследующий CursorPagination. Он должен использовать поле created_at для курсора, сортировать по убыванию, выводить 20 элементов на страницу. Настрой заголовок ответа так, чтобы клиент мог получить следующий курсор."
Пример результата:
from rest_framework.pagination import CursorPagination
class PostCursorPagination(CursorPagination):
page_size = 20
ordering = "-created_at"
cursor_query_param = "cursor"
# В ViewSet:
class PostViewSet(viewsets.ModelViewSet):
pagination_class = PostCursorPagination
Почему это работает: Курсорная пагинация эффективнее offset-based для больших наборов данных, так как не использует OFFSET, а работает через WHERE created_at < last_value. Это предотвращает проблемы с дубликатами при вставке новых записей.
Заключение
Представленные промты покрывают ключевые аспекты разработки на Django: от простых моделей до сложных паттернов вроде CQRS и шифрования. Главное преимущество использования LLM в этом контексте — экономия времени на рутинных задачах и снижение вероятности ошибок, если промты сформулированы с учётом best practices. Однако помните, что сгенерированный код всегда требует проверки: тестируйте его в изолированной среде, проверяйте безопасность (особенно в промтах, связанных с аутентификацией и шифрованием) и адаптируйте под конкретные требования проекта. Рекомендую сохранить эту подборку как шпаргалку — она сэкономит вам часы работы над следующими Django-проектами.
Комментарии