Generic Views — классы
from rest_framework.generics import (
ListAPIView, # GET список
CreateAPIView, # POST создание
ListCreateAPIView, # GET список + POST создание
RetrieveAPIView, # GET объект
DestroyAPIView, # DELETE
UpdateAPIView, # PUT, PATCH
RetrieveUpdateAPIView, # GET + PUT/PATCH
RetrieveDestroyAPIView, # GET + DELETE
RetrieveUpdateDestroyAPIView, # GET + PUT/PATCH + DELETE
)
PageNumberPagination
from rest_framework.pagination import PageNumberPagination
class StandardResultsSetPagination(PageNumberPagination):
page_size = 5 # записей на странице
page_size_query_param = 'page_size' # ?page_size=10
max_page_size = 15 # максимум
# Использование в APIView
class MyListView(APIView):
paginator = StandardResultsSetPagination
def get(self, request):
qs = MyModel.objects.all()
paginator = self.paginator()
page = paginator.paginate_queryset(qs, request, view=self)
if page is not None:
s = MySerializer(page, many=True)
return paginator.get_paginated_response(s.data)
s = MySerializer(qs, many=True)
return Response(s.data)
Сериализаторы: SlugRelatedField
from rest_framework import serializers
# Только чтение (отображение)
field = serializers.SlugRelatedField(read_only=True, slug_field='name')
# Чтение и запись (создание/обновление)
field = serializers.SlugRelatedField(
slug_field='name',
queryset=MyModel.objects.all()
)
Полевая валидация
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = (...)
# validate_<fieldname> — на уровне класса, НЕ внутри Meta
def validate_name(self, value: str) -> str:
if len(value) < 10:
raise serializers.ValidationError("Too short")
return value
def validate_deadline(self, value):
from django.utils import timezone
if timezone.is_naive(value):
value = timezone.make_aware(value, timezone.get_current_timezone())
if value < timezone.now():
raise serializers.ValidationError("Cannot be in the past")
return value
Переопределение create() и update()
def create(self, validated_data: dict) -> Task:
tags = validated_data.pop('tags', [])
task = Task.objects.create(**validated_data)
for tag in tags:
task.tags.add(tag)
task.save()
return task
def update(self, instance: Task, validated_data: dict) -> Task:
tags = validated_data.pop('tags', [])
for attr, value in validated_data.items():
setattr(instance, attr, value)
if tags:
for tag in tags:
instance.tags.add(tag)
instance.save()
return instance
Утилиты файлов (upload_file_helpers.py)
import os
from pathlib import Path
ALLOWED_EXTENSIONS = ['.csv', '.doc', '.pdf', '.xlsx']
def check_extension(filename: str) -> bool:
return Path(filename).suffix in ALLOWED_EXTENSIONS
def check_file_size(file, required_size: int = 2) -> bool:
return (file.size / (1024 * 1024)) <= required_size
def create_file_path(project_name: str, file_name: str) -> str:
stem, ext = file_name.rsplit('.', 1)
return "documents/{}/{}.{}".format(
project_name.replace(' ', '_'),
stem.replace(' ', '_'),
ext
)
def save_file(file_path: str, file_content) -> str:
os.makedirs(os.path.dirname(Path(file_path)), exist_ok=True)
with open(file_path, 'wb') as f:
for chunk in file_content.chunks():
f.write(chunk)
return file_path
def delete_file(file_path: str) -> None:
os.remove(os.path.realpath(file_path))
AbstractBaseUser — кастомная модель User
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin, UserManager
class User(AbstractBaseUser, PermissionsMixin):
# ... поля ...
USERNAME_FIELD = "email" # поле для входа
REQUIRED_FIELDS = [ # поля для createsuperuser
"username", "first_name", "last_name", "position"
]
objects = UserManager()
# settings.py
AUTH_USER_MODEL = 'users.User'
Django-команды в проекте
# Создание нового приложения
python manage.py startapp users
# Миграции
python manage.py makemigrations
python manage.py migrate
# Создание суперпользователя (после кастомного User)
python manage.py createsuperuser
# Запуск сервера
python manage.py runserver
# Git-workflow по каждой задаче
git add .
git commit -m "feat: task N — description"
git push origin <branch_name>
git checkout -b feature/task-N # новая ветка для задачи
# ... работа ...
# merge request / pull request