Пример 1: Полный models.py
Финальный вид файла после всех задач практикума (Django 5.x).
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MinLengthValidator
# --- Choices ---
STATUSES_CHOICES = [
('New', 'New'),
('In_progress', 'In_progress'),
('Completed', 'Completed'),
('Closed', 'Closed'),
('Pending', 'Pending'),
('Blocked', 'Blocked'),
]
PRIORITY_CHOICES = [
('Low', 'Low'),
('Medium', 'Medium'),
('High', 'High'),
('Very High', 'Very High'),
]
# --- Модели ---
class Tag(models.Model):
"""Тег для задачи."""
name = models.CharField(max_length=20, unique=True)
def __str__(self):
return self.name
class ProjectFile(models.Model):
"""Файл, прикреплённый к проекту."""
name = models.CharField(max_length=120)
file = models.FileField(upload_to='projects/')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Meta:
verbose_name = 'Project File'
verbose_name_plural = 'Project Files'
ordering = ['-created_at']
class Project(models.Model):
"""Проект — контейнер задач."""
name = models.CharField(max_length=100, unique=True)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
files = models.ManyToManyField(ProjectFile, related_name='projects', blank=True)
def __str__(self):
return self.name
@property
def count_of_files(self):
return self.files.count()
class Meta:
ordering = ['-name']
verbose_name = 'Project'
verbose_name_plural = 'Projects'
unique_together = (('name', 'created_at'),)
class Task(models.Model):
"""Задача в рамках проекта."""
title = models.CharField(
max_length=255, unique=True,
validators=[MinLengthValidator(10)]
)
description = models.TextField(null=True, blank=True)
status = models.CharField(
max_length=15, choices=STATUSES_CHOICES, default='New'
)
priority = models.CharField(max_length=15, choices=PRIORITY_CHOICES)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
deleted_at = models.DateTimeField(null=True, blank=True)
due_date = models.DateTimeField(null=True, blank=True)
tags = models.ManyToManyField('Tag', blank=True, related_name='tasks')
assignee = models.ForeignKey(
User, null=True, blank=True, on_delete=models.SET_NULL
)
def __str__(self):
return self.title
class Meta:
ordering = ['-due_date', 'assignee']
verbose_name = 'Task'
verbose_name_plural = 'Tasks'
unique_together = (('title', 'project'),)
Отличие от исходного решения: для
assignee использован on_delete=models.SET_NULL вместо CASCADE — при удалении пользователя задача сохраняется с assignee=None. Это более безопасное поведение в реальных проектах.
Пример 2: Полный admin.py
from django.contrib import admin
from .models import Project, Task, Tag, ProjectFile
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ['name']
search_fields = ['name']
@admin.register(ProjectFile)
class ProjectFileAdmin(admin.ModelAdmin):
list_display = ['name', 'file', 'created_at']
search_fields = ['name']
list_filter = ['created_at']
@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
list_display = ['name', 'display_count_of_files', 'created_at']
search_fields = ['name']
actions = ['replace_spaces_with_underscores']
def display_count_of_files(self, obj):
return obj.count_of_files
display_count_of_files.short_description = 'Count of Files'
def replace_spaces_with_underscores(self, request, queryset):
for obj in queryset:
obj.name = obj.name.replace(' ', '_')
obj.save()
replace_spaces_with_underscores.short_description = 'Replace spaces with underscores'
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
list_display = [
'title', 'project', 'status', 'priority',
'created_at', 'due_date', 'assignee'
]
search_fields = ['title']
list_filter = ['status', 'priority', 'project', 'created_at', 'due_date', 'assignee']
actions = [
'change_status',
'change_priority_to_low',
'change_priority_to_medium',
'change_priority_to_high',
'change_priority_to_very_high',
]
def change_status(self, request, queryset):
for obj in queryset:
obj.status = 'Closed'
obj.save()
change_status.short_description = 'Mark as Closed'
def change_priority_to_low(self, request, queryset):
for obj in queryset:
obj.priority = 'Low'
obj.save()
change_priority_to_low.short_description = 'Mark as Low priority'
def change_priority_to_medium(self, request, queryset):
for obj in queryset:
obj.priority = 'Medium'
obj.save()
change_priority_to_medium.short_description = 'Mark as Medium priority'
def change_priority_to_high(self, request, queryset):
for obj in queryset:
obj.priority = 'High'
obj.save()
change_priority_to_high.short_description = 'Mark as High priority'
def change_priority_to_very_high(self, request, queryset):
for obj in queryset:
obj.priority = 'Very High'
obj.save()
change_priority_to_very_high.short_description = 'Mark as Very High priority'
Пример 3: Создание и применение fixtures
# Шаг 1: Создать директорию для fixtures
mkdir practicum_2/fixtures
# Шаг 2: Сохранить группы разрешений
python manage.py dumpdata auth.Group --natural-foreign --indent=4 \
> practicum_2/fixtures/groups_fixture.json
# Шаг 3: Сохранить пользователей
python manage.py dumpdata auth.User --natural-foreign --indent=4 \
> practicum_2/fixtures/users_fixture.json
# Шаг 4: Удалить базу данных (db.sqlite3)
# Шаг 5: Пересоздать миграции
python manage.py makemigrations
python manage.py migrate
# Шаг 6: Восстановить данные
python manage.py loaddata practicum_2/fixtures/groups_fixture.json
python manage.py loaddata practicum_2/fixtures/users_fixture.json