backup Django

от автора

Резервное копирование Django

Короче, начал делать проект на Django с нуля, и вообще впервые с ним работаю (noob). Соответственно с нейронками в паре, так как сам в Django не особо шарю, и они делают почти всё за меня (условно). И поскольку нейросети любят переписывать код по-своему, периодически всё ломается: и код, и база, и остальная разная нечисть🌚

*Примечание: обработка для db.sqlite3 (что стандарт для Dj, и бывает многое), но с лёгкостью можете изменить под другую бд

В итоге решил делать бэкапы вместе с нейронками — может, кому-то пригодится.

Да, я в курсе, что есть django-dbbackup, контроль версий и куча других тем, но мне пока так удобнее. Дальше — больше. Делюсь этим скорее для таких же новичков, как я. Вдруг будет полезно⤵️:

Что делаем

  1. Легко и изящно пишем названием при сохранение

  2. Если всё пошло не тем путём куда хотели прийти, восстанавливаем всё обратно в 1 секунду

import os import shutil import sqlite3 import datetime import zipfile import argparse   # === Конфигурация для бэкапа ===  # Папка, в которую будут сохраняться резервные копии BACKUP_DIR = "backups"  # Папки, которые нужно исключить из бэкапа # Обычно сюда попадают временные, кэшированные или неважные данные EXCLUDE_DIRS = ['__pycache__', '.git', 'venv']  # __pycache__ — кэш Python, .git — история гита, venv — виртуальное окружение  # Отдельные файлы, которые обязательно нужно включить в бэкап # Например, база данных SQLite и список зависимостей INCLUDE_FILES = ['db.sqlite3', 'requirements.txt']  # Папки проекта, которые нужно включить в бэкап # Указываем те директории (папки noob), в которых находится основной код INCLUDE_DIRS = ['fish1', 'fish2']   def create_backup():     """Создание резервной копии проекта с возможностью задать имя"""     user_input = input("Введите название резервной копии (оставьте пустым для имени по умолчанию): ").strip()     custom_name = user_input.strip("()") if user_input else None      timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")     base_name = custom_name if custom_name else f"backup"     backup_name = f"{base_name}_{timestamp}"     backup_path = os.path.join(BACKUP_DIR, backup_name)     os.makedirs(backup_path, exist_ok=True)      print(f"Создание резервной копии в {backup_path}...")      # Копируем файлы     for item in INCLUDE_FILES:         if os.path.exists(item):             shutil.copy2(item, backup_path)      # Копируем папки     for dir_name in INCLUDE_DIRS:         if os.path.exists(dir_name):             shutil.copytree(                 dir_name,                 os.path.join(backup_path, dir_name),                 ignore=shutil.ignore_patterns(*EXCLUDE_DIRS)             )      # Дамп SQLite     if os.path.exists('db.sqlite3'):         db_backup_path = os.path.join(backup_path, 'db_dump.sql')         with sqlite3.connect('db.sqlite3') as conn:             with open(db_backup_path, 'w', encoding='utf-8') as f:                 for line in conn.iterdump():                     f.write(f'{line}\n')      # ZIP архив     zip_path = f"{backup_path}.zip"     with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:         for root, dirs, files in os.walk(backup_path):             for file in files:                 file_path = os.path.join(root, file)                 arcname = os.path.relpath(file_path, os.path.dirname(backup_path))                 zipf.write(file_path, arcname)      shutil.rmtree(backup_path)     print(f"✔ Резервная копия создана: {zip_path}")   def restore_backup(backup_file):     """Восстановление проекта из резервной копии"""     if not os.path.exists(backup_file):         print(f"Файл {backup_file} не найден!")         return      print(f"Восстановление из {backup_file}...")      # Удаляем текущие папки     for dir_name in INCLUDE_DIRS:         if os.path.exists(dir_name):             shutil.rmtree(dir_name)      # Распаковываем архив     temp_dir = "temp_restore"     with zipfile.ZipFile(backup_file, 'r') as zip_ref:         zip_ref.extractall(temp_dir)      # Определяем путь к данным     backup_name = os.path.splitext(os.path.basename(backup_file))[0]     source_path = os.path.join(temp_dir, backup_name)      # Копируем файлы обратно     for item in os.listdir(source_path):         src = os.path.join(source_path, item)         dst = os.path.join('.', item)         if os.path.isdir(src):             shutil.copytree(src, dst)         else:             shutil.copy2(src, dst)      # Восстанавливаем базу данных     db_dump = os.path.join(source_path, 'db_dump.sql')     if os.path.exists(db_dump):         if os.path.exists('db.sqlite3'):             os.remove('db.sqlite3')         with sqlite3.connect('db.sqlite3') as conn:             with open(db_dump, 'r', encoding='utf-8') as f:                 sql = f.read()             conn.executescript(sql)      shutil.rmtree(temp_dir)     print("✔ Восстановление завершено!")   def list_backups():     """Список доступных резервных копий"""     if not os.path.exists(BACKUP_DIR):         print("Папка с резервными копиями пуста!")         return      backups = sorted(         [f for f in os.listdir(BACKUP_DIR) if f.endswith('.zip')],         reverse=True     )      if not backups:         print("Резервные копии не найдены!")         return      print("📦 Доступные резервные копии:")     for i, backup in enumerate(backups, 1):         print(f"{i}. {backup}")   def main():     parser = argparse.ArgumentParser(description="Резервное копирование Django-проекта")     subparsers = parser.add_subparsers(dest='command')      subparsers.add_parser('backup', help='Создать резервную копию')      restore_parser = subparsers.add_parser('restore', help='Восстановить из резервной копии')     restore_parser.add_argument('backup', nargs='?', help='Имя zip-файла резервной копии')      subparsers.add_parser('list', help='Показать список резервных копий')      args = parser.parse_args()      os.makedirs(BACKUP_DIR, exist_ok=True)      if args.command == 'backup':         create_backup()     elif args.command == 'restore':         if args.backup:             restore_backup(os.path.join(BACKUP_DIR, args.backup))         else:             list_backups()             choice = input("Введите номер резервной копии для восстановления: ")             try:                 backups = sorted(                     [f for f in os.listdir(BACKUP_DIR) if f.endswith('.zip')],                     reverse=True                 )                 selected = backups[int(choice) - 1]                 restore_backup(os.path.join(BACKUP_DIR, selected))             except (IndexError, ValueError):                 print("Неверный выбор!")     elif args.command == 'list':         list_backups()     else:         parser.print_help()   if __name__ == "__main__":     main()   """ Создать резервную копию: python backup_restore.py backup  Показать список бэкапов: python backup_restore.py list  Восстановить проект (по номеру или имени): python backup_restore.py restore backup_20240515_143022.zip   python backup_restore.py restore backup_20250417_212905.zip python backup_restore.py restore backup_20250417_212905.zip  или python backup_restore.py restore # Затем выберите номер из списка  """

Вкратце и в грации❤️‍🔥, делаем файл рядом с manage.py (например с названием «backup_restore.py»), далее:

1. Сделали топ рабочую версию: python backup_restore.py backup (название файла по примеру, называйте файл любым другим словом, как хотите, и backup делаем по названию файла)
2. Сделали после топ версии — хавно (где всё ломается), откат в секунду: python backup_restore.py restore
2.1. Работаем дальше🎯

Мой канал: https://t.me/jollyPython
Maximum free skill Python (глубина Python, до которой не дотянется ни одна система): https://t.me/pythonNb


ссылка на оригинал статьи https://habr.com/ru/articles/905768/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *