Files
atabackend/backup/views.py
Beyhan Oğur d50f14bcb1 first commit
2026-04-26 22:20:45 +03:00

260 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
from datetime import datetime
from django.conf import settings
from django.utils import timezone
from .models import DatabaseBackup
from django.tasks import task
try:
import psycopg2
from psycopg2 import sql
PSYCOPG2_AVAILABLE = True
except ImportError:
PSYCOPG2_AVAILABLE = False
class BackupManager:
"""PostgreSQL veritabanı yedekleme işlemlerini yönetir - Sadece psycopg2 kullanarak"""
def __init__(self):
self.backup_dir = os.path.join(settings.BASE_DIR, 'backups')
if not os.path.exists(self.backup_dir):
os.makedirs(self.backup_dir)
def get_db_config(self):
"""Veritabanı yapılandırmasını alır"""
db_config = settings.DATABASES['default']
return {
'dbname': db_config.get('NAME'),
'user': db_config.get('USER'),
'password': db_config.get('PASSWORD'),
'host': db_config.get('HOST', 'localhost'),
'port': db_config.get('PORT', '5432'),
}
def get_connection(self):
"""PostgreSQL bağlantısı oluşturur"""
if not PSYCOPG2_AVAILABLE:
raise Exception("psycopg2 kütüphanesi yüklü değil")
db_config = self.get_db_config()
return psycopg2.connect(
dbname=db_config['dbname'],
user=db_config['user'],
password=db_config['password'],
host=db_config['host'],
port=db_config['port']
)
#@task
def create_backup(self, backup_obj):
"""
PostgreSQL veritabanının yedeğini oluşturur
Sadece psycopg2 kullanarak SQL dump oluşturur
"""
try:
backup_obj.status = 'in_progress'
backup_obj.save()
db_config = self.get_db_config()
# Yedek dosyası adını oluştur
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_filename = f"backup_{db_config['dbname']}_{timestamp}.sql"
backup_path = os.path.join(self.backup_dir, backup_filename)
# Veritabanına bağlan
conn = self.get_connection()
cursor = conn.cursor()
with open(backup_path, 'w', encoding='utf-8') as f:
# Header
f.write("-- PostgreSQL Database Backup\n")
f.write(f"-- Database: {db_config['dbname']}\n")
f.write(f"-- Date: {datetime.now()}\n")
f.write("-- Created by Django Backup System using psycopg2\n\n")
f.write("SET client_encoding = 'UTF8';\n")
f.write("SET standard_conforming_strings = on;\n")
f.write("SET check_function_bodies = false;\n")
f.write("SET client_min_messages = warning;\n\n")
# Tüm tabloları al
cursor.execute("""
SELECT tablename FROM pg_tables
WHERE schemaname = 'public'
ORDER BY tablename;
""")
tables = cursor.fetchall()
for (table_name,) in tables:
f.write(f"\n-- Table: {table_name}\n")
# Tablo yapısını al - kolon bilgilerini çek
cursor.execute("""
SELECT
column_name,
data_type,
character_maximum_length,
is_nullable,
column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = %s
ORDER BY ordinal_position;
""", [table_name])
columns_info = cursor.fetchall()
if columns_info:
f.write(f"DROP TABLE IF EXISTS {table_name} CASCADE;\n")
f.write(f"CREATE TABLE {table_name} (\n")
col_defs = []
for col_name, data_type, max_length, is_nullable, col_default in columns_info:
col_def = f" {col_name} "
# Veri tipini ekle
if max_length and data_type == 'character varying':
col_def += f"VARCHAR({max_length})"
elif max_length and data_type == 'character':
col_def += f"CHAR({max_length})"
else:
col_def += data_type.upper()
# NOT NULL
if is_nullable == 'NO':
col_def += " NOT NULL"
# DEFAULT değer
if col_default:
col_def += f" DEFAULT {col_default}"
col_defs.append(col_def)
f.write(",\n".join(col_defs))
f.write("\n);\n\n")
# Veriyi al ve INSERT komutları oluştur
cursor.execute(sql.SQL("SELECT * FROM {}").format(sql.Identifier(table_name)))
rows = cursor.fetchall()
if rows:
# Kolon isimlerini al
cursor.execute(sql.SQL("""
SELECT column_name
FROM information_schema.columns
WHERE table_name = %s
ORDER BY ordinal_position;
"""), [table_name])
columns = [row[0] for row in cursor.fetchall()]
f.write(f"-- Data for table: {table_name}\n")
for row in rows:
values = []
for val in row:
if val is None:
values.append('NULL')
elif isinstance(val, str):
# SQL injection'a karşı koruma
escaped = val.replace("'", "''")
values.append(f"'{escaped}'")
elif isinstance(val, (int, float)):
values.append(str(val))
elif isinstance(val, bool):
values.append('TRUE' if val else 'FALSE')
else:
# Diğer tipler için string'e çevir
escaped = str(val).replace("'", "''")
values.append(f"'{escaped}'")
cols_str = ', '.join(columns)
vals_str = ', '.join(values)
f.write(f"INSERT INTO {table_name} ({cols_str}) VALUES ({vals_str});\n")
f.write("\n")
# Sequence'leri sıfırla
f.write("\n-- Reset sequences\n")
cursor.execute("""
SELECT
c.relname as sequence_name,
t.relname as table_name,
a.attname as column_name
FROM pg_class c
JOIN pg_depend d ON d.objid = c.oid
JOIN pg_class t ON d.refobjid = t.oid
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = d.refobjsubid
WHERE c.relkind = 'S'
AND t.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public');
""")
sequences = cursor.fetchall()
for seq_name, tbl_name, col_name in sequences:
f.write(f"SELECT setval('{seq_name}', (SELECT COALESCE(MAX({col_name}), 1) FROM {tbl_name}));\n")
cursor.close()
conn.close()
# Başarılı
file_size = os.path.getsize(backup_path)
backup_obj.file_path = backup_path
backup_obj.file_size = file_size
backup_obj.status = 'completed'
backup_obj.completed_at = timezone.now()
backup_obj.save()
return True, f"Yedekleme başarıyla tamamlandı: {backup_filename}"
except Exception as e:
backup_obj.status = 'failed'
backup_obj.error_message = str(e)
backup_obj.save()
return False, f"Yedekleme hatası: {str(e)}"
def restore_backup(self, backup_path):
"""
Bir yedek dosyasından veritabanını geri yükler
SQL dosyasını okuyup komutları çalıştırır
"""
try:
if not os.path.exists(backup_path):
return False, "Yedek dosyası bulunamadı"
# SQL dosyasını oku
with open(backup_path, 'r', encoding='utf-8') as f:
sql_content = f.read()
# Veritabanına bağlan
conn = self.get_connection()
conn.autocommit = True
cursor = conn.cursor()
# SQL komutlarını çalıştır
# Basit bir şekilde satır satır çalıştır
statements = sql_content.split(';')
for statement in statements:
statement = statement.strip()
if statement and not statement.startswith('--'):
try:
cursor.execute(statement)
except Exception as e:
# Bazı hatalar göz ardı edilebilir (örn: tablo zaten var)
if 'does not exist' not in str(e):
print(f"Warning: {e}")
cursor.close()
conn.close()
return True, "Veritabanı başarıyla geri yüklendi"
except Exception as e:
return False, f"Geri yükleme hatası: {str(e)}"
def delete_backup_file(self, backup_path):
"""Yedek dosyasını fiziksel olarak siler"""
try:
if os.path.exists(backup_path):
os.remove(backup_path)
return True, "Yedek dosyası silindi"
else:
return False, "Yedek dosyası bulunamadı"
except Exception as e:
return False, f"Dosya silme hatası: {str(e)}"