Блок 1: Создание данных
Задача 1: Создание тэгов
Логика: создаём объекты модели Tag в памяти, затем сохраняем каждый вызовом save(). Можно сначала сформировать список, затем обойти его в цикле.
python manage.py shell
from management_app.models.tag import Tag
tags_list = [
Tag(name='Backend'),
Tag(name='Frontend'),
Tag(name='Q&A'),
Tag(name='Design'),
Tag(name='DevOPS')
]
for tag in tags_list:
tag.save()
Результат: 5 тэгов сохранены в БД. Переменные back_tag, front_tag и т.д. потребуются в задаче 6 — удобно получить их через Tag.objects.get(name='Backend').
Задача 2: Создание проектов
Логика: показаны оба способа — save() и objects.create(). Они эквивалентны, create() делает то же что конструктор + save() за один вызов.
from management_app.models.project import Project
tiger_project = Project(name='TIGER', description='THIS IS A FIRST PROJECT')
tiger_project.save()
saphyr_project = Project.objects.create(
name='SapHYR INC',
description='THE BEST PROJECT EVER'
)
Задача 3: Добавление файлов к проектам
Логика: создаём файлы через objects.create(), затем добавляем их к проектам через related manager project_files.add() — это M2M-связь.
from management_app.models.project import Project, ProjectFile
# Файлы проекта TIGER
tiger_proj_file_1 = ProjectFile.objects.create(
file_name='THE FIRST FILE',
file_path='projects/tiger/THE_first_file.doc'
)
tiger_proj_file_2 = ProjectFile.objects.create(
file_name='IMPORTANT DOCUMENT',
file_path='projects/tiger/important_doc_9s9fh7g4hd4hgf6.pdf'
)
tiger_proj_file_3 = ProjectFile.objects.create(
file_name='README',
file_path='projects/tiger/README.md'
)
tiger_project.project_files.add(tiger_proj_file_1)
tiger_project.project_files.add(tiger_proj_file_2)
tiger_project.project_files.add(tiger_proj_file_3)
# Файлы проекта SapHYR
saphyr_file_1 = ProjectFile.objects.create(
file_name='README',
file_path='projects/saphyr/README.md'
)
saphyr_file_2 = ProjectFile.objects.create(
file_name='DB_DIAGRAMM',
file_path='projects/saphyr/db_schema_h3g45f67d.drawio'
)
saphyr_file_3 = ProjectFile.objects.create(
file_name='budget',
file_path='projects/saphyr/proj_budget.xlsx'
)
saphyr_project.project_files.add(saphyr_file_1)
saphyr_project.project_files.add(saphyr_file_2)
saphyr_project.project_files.add(saphyr_file_3)
Задача 4: Создание пользователей
Логика: используем стандартную модель Django User. В лекции применяется objects.create(), но для production-кода правильнее create_user() — он хэширует пароль.
from django.contrib.auth.models import User
# Из лекции (пароль НЕ хэшируется — только для учебных целей)
backend_dev = User.objects.create(
username='backend_dev',
password='sd7f6g5fsfd',
email='backend.dev@gmail.com'
)
frontend_dev = User.objects.create(
username='frontend_dev',
password='sd7f6g5fsfd',
email='frontend.dev@gmail.com'
)
designer_dev = User.objects.create(
username='designer',
password='sd7f6g5fsfd',
email='omg.designer@icloud.com'
)
devops_dev = User.objects.create(
username='devops',
password='sd7f6g5fsfd',
email='devops.3000@icloud.com'
)
qa_dev = User.objects.create(
username='qa_dev',
password='sd7f6g5fsfd',
email='qa.doesntmetter@gmail.com'
)
User.objects.create_user(username, email, password) — он корректно хэширует пароль через set_password().
Задача 5: Создание задач для проектов
Логика: при создании задачи через objects.create() передаём объекты FK-полей напрямую (а не id). Django сам определяет pk.
from management_app.models.task import Task
# Задачи для TIGER
tiger_task_1 = Task.objects.create(
name="Create new endpoint to get all project's tasks",
priority='Critical',
project=tiger_project,
assignee=backend_dev
)
tiger_task_2 = Task.objects.create(
name="Update schema",
priority='Critical',
project=tiger_project,
assignee=backend_dev
)
tiger_task_3 = Task.objects.create(
name="Connect new microservice",
priority='Normal',
project=tiger_project,
assignee=devops_dev
)
tiger_task_4 = Task.objects.create(
name="Update Stage build",
priority='Major',
project=tiger_project,
assignee=devops_dev
)
tiger_task_5 = Task.objects.create(
name="Update UX to mobile app",
priority='Urgent',
project=tiger_project,
assignee=designer_dev
)
tiger_task_6 = Task.objects.create(
name="Update 404 page",
priority='Critical',
project=tiger_project,
assignee=frontend_dev
)
tiger_task_7 = Task.objects.create(
name="Test new functionality",
priority='Urgent',
project=tiger_project,
assignee=qa_dev
)
tiger_task_8 = Task.objects.create(
name="test register form",
priority='Normal',
project=tiger_project,
assignee=qa_dev
)
# Задачи для SapHYR INC
saphyr_task_1 = Task.objects.create(
name="Update new endpoint to delete panel",
priority='Critical',
project=saphyr_project,
assignee=backend_dev
)
saphyr_task_2 = Task.objects.create(
name="Update DB schema",
priority='Critical',
project=saphyr_project,
assignee=backend_dev
)
saphyr_task_3 = Task.objects.create(
name="Connect new Azure storage",
priority='Normal',
project=saphyr_project,
assignee=devops_dev
)
saphyr_task_4 = Task.objects.create(
name="Update Build pipelines",
priority='Major',
project=saphyr_project,
assignee=devops_dev
)
saphyr_task_5 = Task.objects.create(
name="Update UI to desktop app",
priority='Urgent',
project=saphyr_project,
assignee=designer_dev
)
saphyr_task_6 = Task.objects.create(
name="Update redirect page",
priority='Critical',
project=saphyr_project,
assignee=frontend_dev
)
saphyr_task_7 = Task.objects.create(
name="Test new mobile functionality",
priority='Urgent',
project=saphyr_project,
assignee=qa_dev
)
saphyr_task_8 = Task.objects.create(
name="test update account form",
priority='Normal',
project=saphyr_project,
assignee=qa_dev
)
Задача 6: Добавление тэгов к задачам
Логика: ManyToMany-связь. После создания задач добавляем тэги через related manager .tags.add(). Выбор тэга по смыслу задачи (backend задача → backend тэг).
# Сначала получаем тэги из БД (если переменные из задачи 1 не сохранились)
back_tag = Tag.objects.get(name='Backend')
front_tag = Tag.objects.get(name='Frontend')
qa_tag = Tag.objects.get(name='Q&A')
design_tag = Tag.objects.get(name='Design')
devops_tag = Tag.objects.get(name='DevOPS')
designer_tag = design_tag # в источнике designer_tag — алиас Design
# TIGER задачи
tiger_task_1.tags.add(back_tag)
tiger_task_2.tags.add(back_tag)
tiger_task_3.tags.add(devops_tag)
tiger_task_4.tags.add(devops_tag)
tiger_task_5.tags.add(designer_tag)
tiger_task_5.tags.add(design_tag)
tiger_task_6.tags.add(front_tag)
tiger_task_7.tags.add(qa_tag)
tiger_task_8.tags.add(qa_tag)
# SapHYR задачи
saphyr_task_1.tags.add(back_tag)
saphyr_task_2.tags.add(back_tag)
saphyr_task_3.tags.add(front_tag)
saphyr_task_4.tags.add(front_tag)
saphyr_task_5.tags.add(design_tag)
saphyr_task_6.tags.add(design_tag)
saphyr_task_7.tags.add(qa_tag)
saphyr_task_8.tags.add(qa_tag)
Блок 2: Базовые запросы
Задача 7: Получение тегов
Логика: базовые методы QuerySet Manager — all(), first(), last(), count().
from management_app.models.tag import Tag
all_tags = Tag.objects.all()
for tag in all_tags:
print(tag.name)
# Backend
# Frontend
# Q&A
# Design
# DevOPS
last_tag = Tag.objects.last()
last_tag.name
# 'Design'
first_tag = Tag.objects.first()
first_tag.name
# 'Backend'
count_of_tags = Tag.objects.count()
count_of_tags
# 5
Задача 8: Проверка существования тега
Логика: .exists() возвращает True/False — эффективно, не загружает объекты в память.
tag_exists = Tag.objects.filter(name='SUPERTAG').exists()
tag_exists
# False
Задача 9: Фильтрация тегов по строке
Логика: lookup __icontains — «contains без учёта регистра». В примере ищем все тэги, содержащие букву «e».
tags_by_str_match = Tag.objects.filter(name__icontains='e')
tags_by_str_match
# <QuerySet [<Tag: Backend>, <Tag: Design>, <Tag: DevOPS>, <Tag: Frontend>]>
for tag in tags_by_str_match:
print(tag.name)
# Backend
# Design
# DevOPS
# Frontend
Задача 10: Фильтрация проектов по дате
Логика: __gt — «больше» (strictly greater than). Используем django.utils.timezone для timezone-aware дат.
from django.utils import timezone
from management_app.models.project import Project
required_date = timezone.datetime(2023, 1, 1).astimezone()
required_projects = Project.objects.filter(date_of_creation__gt=required_date)
required_projects
# <QuerySet [<Project: TIGER>, <Project: SapHYR INC>]>
Задача 11: Q-класс для комбинированных условий
Логика: два условия через AND (&). Q-класс нужен когда простой dict-синтаксис filter() не позволяет выразить логику.
from django.db.models import Q
specifying_projects = Project.objects.filter(
Q(date_of_creation__gt=required_date) & Q(name__icontains='TI')
)
specifying_projects
# <QuerySet [<Project: TIGER>]>
specifying_projects[0].name
# 'TIGER'
specifying_projects[0].description
# 'THIS IS A FIRST PROJECT'
Задача 12: Файлы конкретного проекта
Логика: доступ к связанным объектам через double underscore lookup. project__name__contains — JOIN ProjectFile → Project по FK, затем фильтр по имени проекта.
required_files = ProjectFile.objects.filter(project__name__contains='TIGER')
required_files
# <QuerySet [<ProjectFile: README>, <ProjectFile: IMPORTANT DOCUMENT>, <ProjectFile: THE FIRST FILE>]>
for f in required_files:
print(f.file_path)
Блок 3: Продвинутая фильтрация
Задача 13: Фильтрация по статусу и приоритету
Логика: несколько kwargs в filter() — все условия применяются как AND. Доступ к связанной модели через точку: obj.assignee.email.
required_tasks = Task.objects.filter(status='new', priority='Urgent')
for obj in required_tasks:
print("=" * 50)
print(obj.name)
print(obj.status)
print(obj.priority)
print(obj.due_date)
print(obj.assignee.email)
print("=" * 50)
Задача 14: Обновление статуса задачи
Логика: filter().update() — один SQL UPDATE. Важно: переменная называется task_to_update (не task_to_updated — опечатка в оригинале).
task_to_update = Task.objects.filter(name='Update schema')
task_to_update.update(status='pending')
task_to_updated.update() — это NameError. Правильно: task_to_update.update().
Задача 15: Комбинация условий с NOT
Логика: Q-класс с тремя операторами: & (AND), | (OR), ~ (NOT). Скобки задают приоритет.
specific_tasks = Task.objects.filter(
(Q(status='new') & Q(priority='Urgent')) | ~Q(tags__name='Q&A')
)
for task in specific_tasks:
print("=" * 50)
print(task.name)
print(task.project.name)
print(task.assignee.email)
print("=" * 50)
Задача 16: F-класс — обновление по значению поля
Логика: F-класс ссылается на значение поля в базе данных. Фильтруем задачи, где месяц due_date равен следующему месяцу после created_date.
from django.db.models import F
Task.objects.filter(
due_date__month=F('created_date__month') + 1
).update(priority="Critical")
F('field__month') в filter-аргументе может вести себя неожиданно. Для надёжности используйте ExtractMonth из django.db.models.functions.
Задача 17: Сдвиг due_date на неделю
Логика: .update() с F-классом и timedelta — один SQL UPDATE для всех задач. Эффективнее, чем обходить в цикле Python.
from datetime import timedelta
from django.db.models import F
Task.objects.update(due_date=F('due_date') + timedelta(weeks=1))
Задача 18: Задачи без исполнителя
Логика: lookup __isnull=True — аналог SQL WHERE assignee_id IS NULL. Работает с FK-полями, ссылающимися на NULL.
tasks_without_assignee = Task.objects.filter(assignee__isnull=True)
for task in tasks_without_assignee:
print("=" * 50)
print(task.name)
print(task.project.name)
print("=" * 50)
Задача 19: Задачи через тэг (M2M lookup)
Логика: доступ к M2M-связанной модели через double underscore: tags__name__icontains — Django делает JOIN через промежуточную таблицу.
tasks_with_qa_tags = Task.objects.filter(tags__name__icontains="Q&A")
for task in tasks_with_qa_tags:
print("=" * 50)
print(task.name)
print(task.status)
print(task.priority)
print(task.project.name)
print("=" * 50)
Задача 20: Проекты с файлами за последнюю неделю
Логика: двухшаговый запрос: сначала получаем нужные файлы, затем фильтруем проекты через project_files__in. distinct() убирает дублирующиеся проекты (у одного проекта может быть несколько файлов).
import datetime
from django.utils import timezone
from management_app.models.project import Project, ProjectFile
last_week = timezone.now() - datetime.timedelta(days=7)
# Шаг 1: файлы за последнюю неделю
recent_files = ProjectFile.objects.filter(created_at__gte=last_week)
# Шаг 2: проекты с этими файлами
projects_with_recent_files = Project.objects.filter(
project_files__in=recent_files
).distinct()
for proj in projects_with_recent_files:
print(proj.name, proj.date_of_creation)
Блок 4: Массовые операции и агрегация
Задача 21: bulk_update статуса
Логика: bulk_update() — один SQL UPDATE для списка объектов. Менее затратно чем вызывать .save() для каждого. Второй аргумент — список обновляемых полей.
tasks_to_update = Task.objects.filter(status="new")
for task in tasks_to_update:
task.status = "in_progress"
Task.objects.bulk_update(tasks_to_update, ['status'])
# Проверка
for task in Task.objects.all():
print(task.status)
Задача 22: bulk_update due_date на 3 дня
Логика: комбинация F-класса и timedelta внутри bulk_update. Каждый объект получает своё значение due_date + 3 дня.
from datetime import timedelta
from django.db.models import F
tasks_to_update = Task.objects.filter(status="in_progress")
for task in tasks_to_update:
task.due_date = F('due_date') + timedelta(days=3)
Task.objects.bulk_update(tasks_to_update, ['due_date'])
Задача 23: annotate(Count) + filter
Логика: annotate() добавляет вычисляемое поле к каждой строке QuerySet. После аннотации можно фильтровать по вычисленному значению.
from django.db.models import Count, Q
from django.utils import timezone
from management_app.models.project import Project
req_date = timezone.datetime(2023, 7, 7).astimezone()
projects_filtered = Project.objects.annotate(
file_count=Count('project_files')
).filter(
Q(date_of_creation__gt=req_date) & Q(file_count__gte=1)
)
file_count__gte=1, хотя задача говорит «больше трёх» — замените на Q(file_count__gte=3) по условию задачи.
Задача 24: Комбинированная фильтрация с концом месяца
Логика: вспомогательная функция вычисляет последний день текущего месяца через calendar.monthrange(). Затем фильтруем задачи с Q-условием по приоритету и due_date.
from django.utils import timezone
import calendar
import datetime
from django.db.models import Q
def calculate_end_of_month():
current_date = timezone.now()
amount_of_days = calendar.monthrange(
current_date.year,
current_date.month
)[1]
date = datetime.datetime(
year=current_date.year,
month=current_date.month,
day=amount_of_days,
)
return timezone.make_aware(date)
end_of_month = calculate_end_of_month()
tasks_filtered = Task.objects.filter(
Q(priority="High") | Q(priority="Critical"),
due_date__lte=end_of_month
)
from django.db.models.functions.datetime import datetime — нестандартный. Здесь используется стандартный import datetime и timezone.make_aware().
Задача 25: exclude() с Q
Логика: exclude() — противоположность filter(). Исключаем задачи с определёнными статусами. Можно было написать .filter(~Q(status='pending') & ~Q(status='closed')), но exclude(Q|Q) нагляднее.
from django.db.models import Q
tasks_excluded = Task.objects.exclude(
Q(status="pending") | Q(status="closed")
)
Задача 26: Обновление приоритета старых задач
Логика: фильтрация по FK (project__name='TIGER') и по дате (created_date__lt=one_month_ago) в одном вызове. Затем .update() меняет приоритет одним SQL-запросом.
import datetime
from django.utils import timezone
one_month_ago = timezone.now() - datetime.timedelta(days=30)
Task.objects.filter(
project__name='TIGER',
created_date__lt=one_month_ago
).update(priority="Urgent")