Добавлен полнофункциональный экспорт/импорт профилей

- Кнопки 'убрать фон' для всех элементов: профиль, группы, ссылки
- Кнопка 'сбросить настройки интерфейса' с подтверждением
- Django app export_import с полным API для бэкапа и восстановления
- Экспорт: создание ZIP архивов с данными профиля и медиафайлами
- Импорт: селективная загрузка групп, ссылок, стилей, медиа
- Обработка мультипарт форм, Django транзакции, управление ошибками
- Полное тестирование: экспорт → импорт данных между пользователями
- API эндпоинты: /api/export/, /api/import/, превью архивов
- Готовая система для производственного развертывания
This commit is contained in:
2025-11-09 14:28:45 +09:00
parent ae54fb7ed1
commit d78c296e5a
16 changed files with 1110 additions and 1 deletions

View File

@@ -0,0 +1,92 @@
from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
User = get_user_model()
class ExportTask(models.Model):
"""Модель для отслеживания задач экспорта профиля"""
STATUS_CHOICES = [
('pending', 'Ожидает выполнения'),
('processing', 'В процессе'),
('completed', 'Завершен'),
('failed', 'Ошибка'),
]
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='Пользователь')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', verbose_name='Статус')
# Опции экспорта
include_groups = models.BooleanField(default=True, verbose_name='Включить группы')
include_links = models.BooleanField(default=True, verbose_name='Включить ссылки')
include_styles = models.BooleanField(default=True, verbose_name='Включить стили')
include_media = models.BooleanField(default=True, verbose_name='Включить медиафайлы')
# Файл с результатом
export_file = models.FileField(
upload_to='exports/',
null=True,
blank=True,
verbose_name='Файл экспорта'
)
# Логирование
error_message = models.TextField(blank=True, verbose_name='Сообщение об ошибке')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Создано')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Обновлено')
class Meta:
verbose_name = 'Задача экспорта'
verbose_name_plural = 'Задачи экспорта'
ordering = ['-created_at']
def __str__(self):
return f'Экспорт {self.user.username} - {self.get_status_display()}'
class ImportTask(models.Model):
"""Модель для отслеживания задач импорта профиля"""
STATUS_CHOICES = [
('pending', 'Ожидает выполнения'),
('processing', 'В процессе'),
('completed', 'Завершен'),
('failed', 'Ошибка'),
]
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='Пользователь')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', verbose_name='Статус')
# Файл для импорта
import_file = models.FileField(upload_to='imports/', verbose_name='Файл для импорта')
# Опции импорта
import_groups = models.BooleanField(default=True, verbose_name='Импортировать группы')
import_links = models.BooleanField(default=True, verbose_name='Импортировать ссылки')
import_styles = models.BooleanField(default=True, verbose_name='Импортировать стили')
import_media = models.BooleanField(default=True, verbose_name='Импортировать медиафайлы')
# Стратегия конфликтов
overwrite_existing = models.BooleanField(default=False, verbose_name='Перезаписать существующие')
# Результаты импорта
imported_groups_count = models.PositiveIntegerField(default=0, verbose_name='Импортировано групп')
imported_links_count = models.PositiveIntegerField(default=0, verbose_name='Импортировано ссылок')
imported_media_count = models.PositiveIntegerField(default=0, verbose_name='Импортировано медиафайлов')
# Логирование
error_message = models.TextField(blank=True, verbose_name='Сообщение об ошибке')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Создано')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Обновлено')
class Meta:
verbose_name = 'Задача импорта'
verbose_name_plural = 'Задачи импорта'
ordering = ['-created_at']
def __str__(self):
return f'Импорт {self.user.username} - {self.get_status_display()}'