Как загрузить несколько файлов с использованием Django REST Framework: Полное руководство

Необходимость загрузки нескольких файлов: случаи использования

Загрузка нескольких файлов – распространенная задача в веб-разработке. Рассмотрим типичные примеры:

  1. Загрузка изображений для галереи: Пользователю нужно загрузить сразу несколько фотографий для создания альбома.
  2. Прикрепление документов к заявке: Клиент отправляет заявку и прикрепляет необходимые сканы, договоры и прочие документы.
  3. Массовая загрузка данных: Загрузка CSV-файлов для импорта данных в систему (например, обновление каталога товаров).

Обзор Django REST Framework: преимущества и возможности

Django REST Framework (DRF) – мощный и гибкий инструмент для создания RESTful API в Django. Его преимущества:

  • Сериализаторы: Преобразование данных между Python и JSON.
  • Представления (Views): Обработка логики запросов и ответов.
  • Аутентификация и авторизация: Встроенные механизмы для защиты API.
  • Гибкость: Поддержка различных форматов данных и типов запросов.

Предварительные требования: установка и настройка Django и DRF

Прежде чем начать, убедитесь, что у вас установлены Django и DRF:

pip install django
pip install djangorestframework

Также необходимо добавить 'rest_framework' в INSTALLED_APPS в settings.py:

INSTALLED_APPS = [
    ...
    'rest_framework',
]

Создание API для загрузки нескольких файлов

Определение модели Django: поле для хранения файлов

Создадим модель для хранения информации о загруженных файлах. Допустим, у нас есть модель Document:

from django.db import models

class Document(models.Model):
    file = models.FileField(upload_to='documents/')
    uploaded_at = models.DateTimeField(auto_now_add=True)

    def __str__(self) -> str:
        return f'{self.file.name}'

upload_to определяет, в какой директории будут храниться загруженные файлы.

Создание сериализатора: обработка загруженных файлов

Сериализатор преобразует данные из запроса в объект Python и обратно. Создадим сериализатор для нашей модели:

from rest_framework import serializers
from .models import Document

class DocumentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Document
        fields = '__all__'

Написание представления (View): логика загрузки и сохранения

Представление обрабатывает логику загрузки и сохранения файлов. Используем CreateAPIView:

from rest_framework import generics
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework import status
from .serializers import DocumentSerializer

class DocumentUploadView(generics.CreateAPIView):
    serializer_class = DocumentSerializer
    parser_classes = (MultiPartParser, FormParser)

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data, many=True) # many=True для обработки нескольких файлов
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  • parser_classes указывает, что представление будет обрабатывать данные в формате multipart/form-data.
  • many=True указывает сериализатору, что ему необходимо обработать список данных.

Настройка URL-адресов: определение эндпоинта для загрузки

Определим URL-адрес для нашего представления в urls.py:

Реклама
from django.urls import path
from .views import DocumentUploadView

urlpatterns = [
    path('upload/', DocumentUploadView.as_view(), name='document-upload'),
]

Реализация загрузки файлов на стороне клиента

HTML форма для загрузки нескольких файлов

Создадим HTML-форму для загрузки нескольких файлов:

<form id="upload-form" enctype="multipart/form-data">
    <input type="file" name="file" multiple><br><br>
    <button type="submit">Загрузить</button>
</form>

Атрибут multiple позволяет пользователю выбирать несколько файлов.

JavaScript для отправки файлов на сервер (использование FormData)

Используем JavaScript для отправки данных на сервер:

const form = document.getElementById('upload-form');

form.addEventListener('submit', function(e) {
    e.preventDefault();

    const formData = new FormData(form);

    fetch('/api/upload/', {
        method: 'POST',
        body: formData,
    })
    .then(response => response.json())
    .then(data => {
        console.log('Success:', data);
        alert('Файлы успешно загружены!');
    })
    .catch(error => {
        console.error('Error:', error);
        alert('Ошибка загрузки файлов.');
    });
});

FormData используется для отправки файлов на сервер.

Обработка ответов сервера: отображение сообщений об успехе или ошибках

В зависимости от ответа сервера отображаем соответствующие сообщения пользователю.

Обработка ошибок и валидация файлов

Валидация размера и типа файлов на стороне сервера

Добавим валидацию размера и типа файлов в сериализатор:

from rest_framework import serializers
from .models import Document

class DocumentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Document
        fields = '__all__'

    def validate_file(self, value):
        filesize = value.size

        if filesize > 1024 * 1024 * 10:  # 10MB
            raise serializers.ValidationError("Размер файла не должен превышать 10MB")

        return value

Обработка исключений при загрузке и сохранении файлов

В представлении можно добавить обработку исключений для более надежной работы:

from rest_framework import generics
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework import status
from .serializers import DocumentSerializer

class DocumentUploadView(generics.CreateAPIView):
    serializer_class = DocumentSerializer
    parser_classes = (MultiPartParser, FormParser)

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data, many=True)
        try:
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

Возврат информативных сообщений об ошибках клиенту

Сообщения об ошибках должны быть понятными для пользователя.

Продвинутые техники и оптимизация

Использование Celery для асинхронной обработки файлов

Для обработки больших файлов или выполнения ресурсоемких операций можно использовать Celery для асинхронной обработки:

from celery import shared_task

@shared_task
def process_document(document_id):
    # Логика обработки файла
    document = Document.objects.get(pk=document_id)
    # ...

Хранение файлов в облачном хранилище (например, AWS S3, Yandex Object Storage)

Для надежного хранения и масштабируемости можно использовать облачные хранилища.

Оптимизация загрузки больших файлов: потоковая передача

Для оптимизации загрузки больших файлов используйте потоковую передачу данных.


Добавить комментарий