Резервное копирование 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