Python · 04.09.2026

Обойдёмся без frontend’а

В последнее время сайт всё больше обрастает ретроспективой уже позабытых микросервисов на Python. Сегодняшний пост не исключение: наткнулся на парочку стареньких скриптов для добавления и удаления пользователей проекта на основе базы данных SQLite и решил оставить их здесь. Может быть кому-то пригодится.

Впервые эти скрипты я применил в процессе внедрения веб-приложения множественного размещения файлов. Мне очень уж не хотелось строить систему прав пользователей и городить ещё один flask-маршрут. Решил оставить как есть и обойтись скриптами с запросами к базе данных.

Упомянутое выше приложение хранило в SQLite только логины и пароли пользователей. На тот момент, данный метод хранения учётных данных мне казался простым и логичным. Использовать в качестве хранилища, например, json-файл — бессмысленно. С этим форматом нельзя пообщаться на языке запросов, да и база данных давала возможность масштабируемости в будущем.

Итак, сначала код скрипта для добавления пользователя:

## Скрипт для добавления пользователя в базу данных SQLite ##

import sys
import os
import sqlite3
import hashlib
import argparse
import getpass
import platform
import ctypes

# Основные переменные #
APPNAME = "Добавление пользователя в базу данных приложения" # <== Название приложения
APPDB = "users.db"  # <== Имя базы данных
APPTABLE = "logins"  # <== Имя таблицы
APPUSER = "name"  # <== Имя столбца с логинами
APPPASS = "password"  # <== Имя столбца с паролями
APPDESC = f"Обратите внимание, что по умолчанию используется база данных {APPDB}!"  # <== Предупреждение при старте приложения

def add_user_advanced():
    parser = argparse.ArgumentParser(description=APPNAME)
    parser.add_argument('--username', '-u', help='Имя пользователя')
    parser.add_argument('--password', '-p', help='Пароль пользователя')
    parser.add_argument('--db-path', help='Путь к базе данных (по умолчанию: автоматическое определение)')
    parser.add_argument('--table', help=f'Имя таблицы с пользователями в базе данных (по умолчанию: {APPTABLE})')
    
    args = parser.parse_args()
    
    # Если параметры не переданы, запрашиваем их интерактивно #
    username = args.username
    if not username:
        username = input("Введите имя пользователя: ").strip()
    
    password = args.password
    if not password:
        password = getpass.getpass("Введите пароль: ")
    
    table_name = args.table if args.table else APPTABLE
    if not table_name:
        table_name = input(f"Введите имя таблицы (по умолчанию {APPTABLE}): ").strip() or APPTABLE
    
    if not username or not password:
        print("Ошибка: Имя пользователя и пароль обязательны")
        return False
    
    # Определяем путь к базе данных #
    if args.db_path:
        db_path = args.db_path
    else:
        if getattr(sys, 'frozen', False):
            base_dir = os.path.dirname(sys.executable)
        else:
            base_dir = os.path.dirname(os.path.abspath(__file__))
        db_path = os.path.join(base_dir, APPDB)
        
    return add_user(username, password, db_path, table_name)

def add_user(username, password, db_path=None, table_name=None):
    if db_path is None:
        if getattr(sys, 'frozen', False):
            base_dir = os.path.dirname(sys.executable)
        else:
            base_dir = os.path.dirname(os.path.abspath(__file__))
        db_path = os.path.join(base_dir, APPDB)
    
    if table_name is None:
        table_name = APPTABLE
    
    if not os.path.exists(db_path):
        print(f"Ошибка: База данных не найдена по пути: {db_path}")
        return False
    
    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()
        
        # Проверяем существование указанной таблицы #
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
        if not cursor.fetchone():
            print(f"Ошибка: Таблица '{table_name}' не найдена в базе данных")
            return False
        
        # Проверяем существование пользователя #
        cursor.execute(f"SELECT * FROM {table_name} WHERE {APPUSER} = ?", (username,))
        if cursor.fetchone():
            print(f"Ошибка: Пользователь '{username}' уже существует в таблице '{table_name}'")
            return False
        
        # Добавляем пользователя #
        hashed_password = hash_password(password)
        cursor.execute(f"INSERT INTO {table_name} ({APPUSER}, {APPPASS}) VALUES (?, ?)", 
                      (username, hashed_password))
        conn.commit()
        conn.close()
        
        print(f"Пользователь '{username}' успешно добавлен в таблицу '{table_name}'!")
        return True
        
    except sqlite3.Error as e:
        print(f"Ошибка базы данных: {e}")
        return False

def hash_password(password):
    return hashlib.sha1(password.encode()).hexdigest()

# Заголовок окна консоли #
def set_console_title(title):
    if platform.system() == "Windows":
        ctypes.windll.kernel32.SetConsoleTitleW(title)
    else:
        print(f"\033]0;{title}\a", end='', flush=True)

if __name__ == '__main__':
    set_console_title(APPNAME)
    print(APPDESC)
    
    # Считывание аргументов #
    if len(sys.argv) > 1:
        success = add_user_advanced()
    else:
    # Интерактивный режим #
        success = add_user_advanced()
    
    sys.exit(0 if success else 1)

Обращаю особое внимание, что функция hash_password строит хэш пароля по алгоритму SHA1. Если нужен более серьёзный принцип генерации контрольной суммы можете использовать одну из этих функций:

# SHA256
def hash_password(password):
    return hashlib.sha256(password.encode()).hexdigest()

# SHA512
def hash_password(password):
    return hashlib.sha512(password.encode()).hexdigest()

Конечно, всё зависит от потребностей и реализации конкретного проекта. Можно уж совсем заморочиться и предусмотреть генерацию контрольной суммы вместе с salt, обеспечив её хранение в базе данных. Впрочем для интранет-сервисов без обработки каких-либо критичных данных этот вариант может быть избыточным.

Теперь скрипт для удаления пользователя из базы данных приложения:

## Скрипт для удаления пользователя из базы данных SQLite ##

import sys
import os
import sqlite3
import argparse
import platform
import ctypes

# Основные переменные #
APPNAME = "Удаление пользователя из базы данных приложения" # <== Название приложения
APPDB = "users.db"  # <== Имя базы данных
APPTABLE = "logins"  # <== Имя таблицы
APPUSER = "name"  # <== Имя столбца с логинами
APPDESC = f"Обратите внимание, что по умолчанию используется база данных {APPDB}!"  # <== Предупреждение при старте приложения

def remove_user_advanced():
    parser = argparse.ArgumentParser(description=APPNAME)
    parser.add_argument('--username', '-u', required=True, help='Имя пользователя для удаления')
    parser.add_argument('--db-path', help='Путь к базе данных (по умолчанию: автоматическое определение)')
    parser.add_argument('--table', help=f'Имя таблицы с пользователями в базе данных (по умолчанию: {APPTABLE})')
    
    args = parser.parse_args()
    
    username = args.username.strip()
    table_name = args.table if args.table else APPTABLE
    
    if not username:
        print("Ошибка: Имя пользователя обязательно")
        return False
    
    # Определяем путь к базе данных #
    if args.db_path:
        db_path = args.db_path
    else:
        if getattr(sys, 'frozen', False):
            base_dir = os.path.dirname(sys.executable)
        else:
            base_dir = os.path.dirname(os.path.abspath(__file__))
        db_path = os.path.join(base_dir, APPDB)
        
    return remove_user(username, db_path, table_name)

def remove_user(username, db_path=None, table_name=None):
    if db_path is None:
        if getattr(sys, 'frozen', False):
            base_dir = os.path.dirname(sys.executable)
        else:
            base_dir = os.path.dirname(os.path.abspath(__file__))
        db_path = os.path.join(base_dir, APPDB)
    
    if table_name is None:
        table_name = APPTABLE
    
    if not os.path.exists(db_path):
        print(f"Ошибка: База данных не найдена по пути: {db_path}")
        return False
    
    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()
        
        # Проверяем существование указанной таблицы #
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
        if not cursor.fetchone():
            print(f"Ошибка: Таблица '{table_name}' не найдена в базе данных")
            return False
        
        # Проверяем существование пользователя #
        cursor.execute(f"SELECT * FROM {table_name} WHERE {APPUSER} = ?", (username,))
        if not cursor.fetchone():
            print(f"Ошибка: Пользователь '{username}' не найден в таблице '{table_name}'")
            return False
        
        # Подтверждение удаления #
        confirm = input(f"Вы уверены, что хотите удалить пользователя '{username}'? (y/N): ").strip().lower()
        if confirm not in ['y', 'yes', 'д', 'да']:
            print("Удаление отменено")
            return False
        
        # Удаляем пользователя #
        cursor.execute(f"DELETE FROM {table_name} WHERE {APPUSER} = ?", (username,))
        conn.commit()
        conn.close()
        
        print(f"Пользователь '{username}' успешно удален из таблицы '{table_name}'!")
        return True
        
    except sqlite3.Error as e:
        print(f"Ошибка базы данных: {e}")
        return False

# Заголовок окна консоли #
def set_console_title(title):
    if platform.system() == "Windows":
        ctypes.windll.kernel32.SetConsoleTitleW(title)
    else:
        print(f"\033]0;{title}\a", end='', flush=True)

if __name__ == '__main__':
    set_console_title(APPNAME)
    print(APPDESC)
    
    # Считывание аргументов #
    if len(sys.argv) > 1:
        success = remove_user_advanced()
    else:
    # Интерактивный режим #
        username = input("Введите имя пользователя для удаления: ").strip()
        if username:
            success = remove_user(username)
        else:
            print("Ошибка: Имя пользователя не может быть пустым")
            success = False
    
    sys.exit(0 if success else 1)

Оба скрипта могут работать как в интерактивном режиме, так и с параметрами запуска:

# Добавление пользователя
python sqlite_adduser.py --username "ivanov" --password "Very_Strong_Pass" --table "admin_users"

# Удаление пользователя
python sqlite_deleteuser.py --username "ivanov"

Более подробно о параметрах запуска можно узнать, использовав ключ —help или -h:

# Добавление пользователя
sqlite_adduser.py [-h] [--username USERNAME] [--password PASSWORD] [--db-path DB_PATH] [--table TABLE]

# Удаление пользователя
sqlite_deleteuser.py [-h] --username USERNAME [--db-path DB_PATH] [--table TABLE]

Этот утилитарный наборчик без всяческих проблем применялся к десятку микросервисов, с учётом, что создание и удаление пользователей в проектах было далеко не ежедневной задачей.