Резервное копирование Django
Короче, начал делать проект на Django с нуля, и вообще впервые с ним работаю (noob). Соответственно с нейронками в паре, так как сам в Django не особо шарю, и они делают почти всё за меня (условно). И поскольку нейросети любят переписывать код по-своему, периодически всё ломается: и код, и база, и остальная разная нечисть🌚
*Примечание: обработка для db.sqlite3 (что стандарт для Dj, и бывает многое), но с лёгкостью можете изменить под другую бд
В итоге решил делать бэкапы вместе с нейронками — может, кому-то пригодится.
Да, я в курсе, что есть django-dbbackup, контроль версий и куча других тем, но мне пока так удобнее. Дальше — больше. Делюсь этим скорее для таких же новичков, как я. Вдруг будет полезно⤵️:
Что делаем
-
Легко и изящно пишем названием при сохранение
-
Если всё пошло не тем путём куда хотели прийти, восстанавливаем всё обратно в 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/
Добавить комментарий