📖 Теория: Agile Projects ч.3 — ключевые концепции
⚡ Ключевые концепции кратко
- get_serializer_class — разный сериализатор для GET и POST в одном view
- AbstractBaseUser — полная замена User с произвольным USERNAME_FIELD
- FileResponse — стриминг файла с заголовком Content-Disposition: attachment
- validate_password — встроенная проверка надёжности пароля от Django
- SlugRelatedField — связь по email вместо pk
1. get_serializer_class — динамический выбор сериализатора
Generic Views в DRF поддерживают метод get_serializer_class(), который вызывается каждый раз перед сериализацией. Это позволяет использовать разные сериализаторы в зависимости от метода HTTP:
class ProjectFileListGenericView(ListCreateAPIView):
def get_serializer_class(self):
if self.request.method == 'GET':
return AllProjectFilesSerializer
return CreateProjectFileSerializer
Паттерн особенно полезен когда GET-ответ должен содержать больше данных (вложенные объекты), а POST-запрос — минимальный набор полей для создания.
2. AbstractBaseUser — кастомная модель пользователя
Django позволяет полностью заменить встроенную модель пользователя через AbstractBaseUser. Это нужно когда стандартные поля (username как логин, first_name/last_name ограниченной длины) не подходят.
Ключевые атрибуты:
USERNAME_FIELD = "email"— поле для аутентификации (вместо username)REQUIRED_FIELDS = [...]— поля, запрашиваемые приcreatesuperuserobjects = UserManager()— стандартный менеджерAUTH_USER_MODEL = 'users.User'в settings.py — должно быть установлено ДО первой миграции
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username", "first_name", "last_name"]
objects = UserManager()
PermissionsMixin добавляет поля is_superuser, groups, user_permissions для работы со стандартной системой прав Django.
3. Валидация пароля в сериализаторе
Django предоставляет встроенные валидаторы паролей через validate_password:
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
def validate(self, data):
password = data.get("password")
re_password = data.get("re_password")
if password != re_password:
raise serializers.ValidationError({"password": "Passwords don't match"})
try:
validate_password(password)
except ValidationError as err:
raise serializers.ValidationError({"password": err.messages})
return data
Валидаторы по умолчанию: минимальная длина 8 символов, не слишком похоже на другие поля пользователя, не общеупотребительный пароль, не полностью числовой.
4. Regex-валидация полей
Модуль re позволяет проверять формат строковых полей:
import re
# Только латиница, цифры, подчёркивание
if not re.match('^[a-zA-Z0-9_]*$', username):
raise serializers.ValidationError("Invalid username format")
# Только буквы латиницы
if not re.match('^[a-zA-Z]*$', first_name):
raise serializers.ValidationError("Only latin letters allowed")
RegexValidator из Django как поле-уровневый валидатор вместо метода validate(). Подход с методом validate работает корректно в обеих версиях.5. FileResponse — скачивание файлов
FileResponse — специальный класс DRF/Django для стриминга файлов. Позволяет избежать загрузки всего файла в память:
from django.http import FileResponse
class DownloadProjectFileView(APIView):
def get(self, request, *args, **kwargs):
project_file = get_object_or_404(ProjectFile, pk=self.kwargs['pk'])
file_handle = project_file.file_path.open()
response = FileResponse(file_handle, content_type='application/octet-stream')
response['Content-Disposition'] = f'attachment; filename="{project_file.file_name}"'
return response
Заголовок Content-Disposition: attachment; filename="..." сигнализирует браузеру/клиенту, что файл нужно скачать, а не открыть инлайн.
6. SlugRelatedField — связь по человекочитаемому полю
SlugRelatedField позволяет задавать связанный объект не по pk, а по любому уникальному полю:
assignee = serializers.SlugRelatedField(
slug_field='email',
queryset=User.objects.all(),
required=False
)
При этом в API передаётся email пользователя, а DRF сам найдёт соответствующий объект User.
7. Структура проекта на этом этапе
agile_project/
├── apps/
│ ├── projects/
│ │ ├── models.py (Project, ProjectFile)
│ │ ├── serializers/
│ │ │ ├── project_serializers.py
│ │ │ └── project_file_serializers.py
│ │ ├── views/
│ │ │ ├── project_views.py
│ │ │ └── project_file_views.py # Задачи 1, 2, 7
│ │ └── utils/
│ │ └── upload_file_helpers.py
│ ├── tasks/
│ │ ├── serializers/
│ │ │ └── task_serializers.py # Задача 6 (assignee)
│ │ └── views/
│ └── users/ # Задачи 3, 4, 5
│ ├── models.py
│ ├── choices/
│ │ └── positions.py
│ ├── serializers.py
│ ├── views.py
│ └── urls.py
└── settings.py (AUTH_USER_MODEL)