🏠 Домашнее задание 11: Инлайн-формы и Admin Actions
⚡ Кратко: три задания
- SubTaskInline в TaskAdmin — создавать подзадачи прямо при редактировании задачи
- Укороченный __str__ для Task — первые 10 символов + «...» в списке, полное название в Inline-выпадашке
- Action «Done» для SubTask — массовый перевод подзадач в статус Done
Задание из LMS (полный текст)
Домашнее задание: Проект "Менеджер задач" — Инлайн формы и Admin actions
Задание 1:
Добавить настройку инлайн форм для админ класса задач. При создании задачи должна появиться возможность создавать сразу и подзадачу.
Задание 2:
Названия задач могут быть длинными и ухудшать читаемость в Админ панели, поэтому требуется выводить в списке задач укороченный вариант – первые 10 символов с добавлением «...», если название длиннее, при этом при выборе задачи для создания подзадачи должно отображаться полное название. Необходимо реализовать такую возможность.
Задание 3:
Реализовать свой action для Подзадач, который поможет выводить выбранные в Админ панели объекты в статус Done.
Подготовка окружения
1. Виртуальное окружение и зависимости
# Создать и активировать venv (PowerShell)
python -m venv venv
.\venv\Scripts\Activate.ps1
# Установить Django
pip install django
# Если проект уже существует — только активировать venv
.\venv\Scripts\Activate.ps1
2. Структура проекта
Предполагаем, что у вас уже есть Django-проект с приложением tasks из предыдущих ДЗ. Если нет:
django-admin startproject task_manager .
python manage.py startapp tasks
Добавить 'tasks' в INSTALLED_APPS в settings.py.
3. Создать суперпользователя (если ещё нет)
python manage.py createsuperuser
4. Git — зафиксировать точку отсчёта
git add .
git commit -m "chore: начало ДЗ 11"
Исходные модели (напоминание)
Используем модели из предыдущих ДЗ. Минимальная структура для этого задания:
# tasks/models.py
from django.db import models
class Task(models.Model):
class Status(models.TextChoices):
TODO = 'TODO', 'To Do'
IN_PROGRESS = 'IN_PROGRESS', 'In Progress'
DONE = 'DONE', 'Done'
title = models.CharField(max_length=200)
description = models.TextField(blank=True)
status = models.CharField(
max_length=20,
choices=Status.choices,
default=Status.TODO
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Meta:
ordering = ['title']
class SubTask(models.Model):
class Status(models.TextChoices):
TODO = 'TODO', 'To Do'
IN_PROGRESS = 'IN_PROGRESS', 'In Progress'
DONE = 'DONE', 'Done'
title = models.CharField(max_length=200)
task = models.ForeignKey(
Task,
on_delete=models.CASCADE,
related_name='subtasks'
)
status = models.CharField(
max_length=20,
choices=Status.choices,
default=Status.TODO
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
python manage.py makemigrations
python manage.py migrate
Пошаговое решение
Задание 1: SubTaskInline для TaskAdmin
Логика: нужно добавить Inline-форму для SubTask на страницу редактирования Task. SubTask имеет ForeignKey на Task — это именно то отношение, которое нужно Inline.
Выбор типа: так как SubTask имеет мало полей, подходит TabularInline (компактнее). Теоретически можно и StackedInline — это на усмотрение.
# tasks/admin.py
from django.contrib import admin
from .models import Task, SubTask
class SubTaskInline(admin.TabularInline):
model = SubTask # SubTask имеет FK на Task
extra = 1 # одна пустая строка для новой подзадачи
fields = ['title', 'status'] # только нужные поля
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
inlines = [SubTaskInline] # подключаем Inline
list_display = ['title', 'status', 'created_at']
@admin.register(SubTask)
class SubTaskAdmin(admin.ModelAdmin):
list_display = ['title', 'task', 'status']
Проверка: открыть Admin → Tasks → создать/открыть задачу → внизу должна быть секция «Sub tasks» с табличной формой.
Задание 2: Укороченный __str__ для списка, полное название в Inline
Логика задачи:
- В списке задач Admin показывать первые 10 символов + «...» (если длиннее)
- В выпадающем поле выбора (Inline-форма SubTask, поле
task) — полное название
Ключевое понимание: __str__ используется везде (и в списке, и в выпадашке). Чтобы в списке показывать короткое, а в выпадашке полное — нужно добавить в list_display кастомный метод, а __str__ оставить полным.
# tasks/models.py
class Task(models.Model):
...
def __str__(self):
return self.title # полное название — для Inline выпадашки
def short_title(self):
"""Укороченное название для list_display"""
if len(self.title) > 10:
return self.title[:10] + '...'
return self.title
short_title.short_description = 'Название' # заголовок столбца
# tasks/admin.py
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
inlines = [SubTaskInline]
list_display = ['short_title', 'status', 'created_at']
# ^^^^^^^^^^^^ используем метод, не __str__
search_fields = ['title']
__str__ вызывается при формировании выпадающего списка для ForeignKey-поля в Inline. Если сделать __str__ коротким — выпадашка тоже будет с «...», что неудобно для выбора нужной задачи. Кастомный метод short_title используется только в list_display.
Задание 3: Action «Done» для SubTask
Логика: создать action, который переводит выбранные подзадачи в статус DONE. Использовать queryset.update() для эффективности.
# tasks/admin.py
from django.contrib import admin
from .models import Task, SubTask
class SubTaskInline(admin.TabularInline):
model = SubTask
extra = 1
fields = ['title', 'status']
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
inlines = [SubTaskInline]
list_display = ['short_title', 'status', 'created_at']
search_fields = ['title']
@admin.register(SubTask)
class SubTaskAdmin(admin.ModelAdmin):
list_display = ['title', 'task', 'status', 'created_at']
list_filter = ['status']
def mark_done(self, request, queryset):
queryset.update(status=SubTask.Status.DONE)
mark_done.short_description = "Перевести выбранные подзадачи в статус Done"
actions = [mark_done]
- Открыть Admin → Sub tasks
- Отметить одну или несколько подзадач чекбоксами
- В выпадающем списке «Action» выбрать «Перевести выбранные подзадачи в статус Done»
- Нажать «Go»
- Убедиться, что статус изменился на «Done»
Итоговый admin.py
# tasks/admin.py
from django.contrib import admin
from .models import Task, SubTask
class SubTaskInline(admin.TabularInline):
"""Inline для создания подзадач на странице задачи."""
model = SubTask
extra = 1
fields = ['title', 'status']
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
inlines = [SubTaskInline]
list_display = ['short_title', 'status', 'created_at']
search_fields = ['title']
list_filter = ['status']
@admin.register(SubTask)
class SubTaskAdmin(admin.ModelAdmin):
list_display = ['title', 'task', 'status', 'created_at']
list_filter = ['status', 'task']
search_fields = ['title']
def mark_done(self, request, queryset):
"""Массовый перевод в статус Done."""
queryset.update(status=SubTask.Status.DONE)
mark_done.short_description = "Перевести выбранные подзадачи в статус Done"
actions = [mark_done]
Проверка в VS Code
Через терминал
# Убедиться, что миграции применены
python manage.py migrate
# Запустить dev-сервер
python manage.py runserver
# Открыть в браузере
# http://127.0.0.1:8000/admin/
Запуск через F5 (launch.json)
Создать или обновить .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Django: runserver",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": ["runserver", "8000"],
"django": true,
"justMyCode": true
}
]
}
Нажать F5 — сервер запустится в режиме отладки. Можно ставить точки останова в admin.py (например, внутри mark_done), чтобы видеть состояние queryset.
Где ставить точки останова
mark_done— строкаqueryset.update(...): посмотреть, какие объекты в QuerySetSubTaskInline— переопределитеsave_modelесли нужна отладка сохранения
Чеклист проверки
- При открытии Task в Admin → снизу появляется секция «Sub tasks» с TabularInline
- Можно добавить SubTask прямо из формы Task (не переходя в раздел Sub tasks)
- В списке Tasks название обрезается до 10 символов + «...»
- В выпадающем поле
taskв SubTask Inline — полное название задачи - В разделе Sub tasks → выбрать несколько → Action «Перевести... в Done» → статус изменился
Связь с теорией и примерами урока
- Теория: TabularInline и StackedInline — концепция инлайн-форм
- Теория: настройки Inline (extra, fields, can_delete)
- Примеры 2–3: Publisher+BookInline — аналог SubTask+TaskInline
- Пример 5: Action update_created_at — аналог action mark_done
- Ошибки: queryset.update() не триггерит сигналы — если добавите сигналы в будущем