Урок 81. Практикум 20: Данные о сотрудниках. Часть 2

📁 Блок: Базы данных ⏱️ Время изучения: ~90 мин 🎯 Сложность: Практикум
#for #in #with #as #enumerate #from #print

⚡ Кратко: типичные ошибки

  • Проверяйте номер отдела на диапазон и тип.
  • Оператор сравнения допускайте только из белого списка.
  • Не собирайте SQL через f-string с пользовательскими данными.
  • Не забывайте вызывать fetchall() после execute().
  • Обрабатывайте ValueError циклом повторного ввода, а не одним except.

❌ Ошибка 1: Некорректный индекс отдела

selected_index = int(input("Enter department number: "))
selected_department = departments[selected_index - 1]

Сообщение об ошибке: IndexError: list index out of range

Почему возникает: пользователь может ввести 0, отрицательное число или номер больше количества отделов.

✅ Исправление

def read_department_number(departments):
    while True:
        try:
            selected_index = int(input("Enter department number: "))
            if 1 <= selected_index <= len(departments):
                return selected_index
            print("Invalid department number. Please try again.")
        except ValueError:
            print("Invalid input. Please enter a number.")

selected_index = read_department_number(departments)
selected_department = departments[selected_index - 1]

Проверяем и тип ввода, и диапазон. При ошибке просим ввести номер заново.

❌ Ошибка 2: Отсутствие проверки оператора сравнения

condition = input("Enter condition: ").strip()
value = float(input("Enter salary: "))
query = f"SELECT * FROM employees WHERE salary {condition} %s"
cursor.execute(query, (value,))

Сообщение об ошибке: pymysql.err.ProgrammingError или возможная SQL-инъекция.

Почему возникает: оператор вставляется в SQL напрямую. Недопустимый оператор ломает запрос; злонамеренный ввод может изменить смысл SQL.

✅ Исправление

valid_operators = {'>', '<', '=', '>=', '<='}
condition = input("Enter condition: ").strip()
if condition not in valid_operators:
    print("Invalid condition.")
else:
    value = float(input("Enter salary: "))
    query = f"SELECT * FROM employees WHERE salary {condition} %s"
    cursor.execute(query, (value,))

Разрешены только операторы из белого списка. Значение зарплаты всё ещё передаётся параметром.

❌ Ошибка 3: f-string SQL для пользовательских данных

user_input = input("Enter department: ")
query = f"SELECT * FROM employees WHERE department_name = '{user_input}'"
cursor.execute(query)

Сообщение об ошибке: синтаксическая ошибка SQL или SQL-инъекция.

Почему возникает: пользовательский ввод становится частью SQL-запроса. Кавычка в названии отдела сломает запрос; специально сформированный ввод может прочитать или изменить данные.

✅ Исправление

user_input = input("Enter department: ")
cursor.execute(
    "SELECT * FROM employees WHERE department_name = %s",
    (user_input,)
)

Плейсхолдер %s отделяет данные от текста SQL. PyMySQL сам экранирует значения.

❌ Ошибка 4: Забыли прочитать результат

cursor.execute("SELECT * FROM employees")
# забыли прочитать результат
print("Done")

Сообщение об ошибке: программа не падает, но ничего не выводит.

Почему возникает: после execute() данные находятся в курсоре; их нужно явно получить через fetchall(), fetchone() или итерацию.

✅ Исправление

cursor.execute("SELECT * FROM employees")
rows = cursor.fetchall()
for row in rows:
    print(row)

Считываем результат сразу после выполнения запроса.

❌ Ошибка 5: Неправильная обработка ValueError

try:
    selected_index = int(input("Enter department number: "))
    selected_department = departments[selected_index - 1]
except ValueError:
    print("Invalid input.")

Сообщение об ошибке: при вводе текста программа завершается или выводит сообщение и падает дальше.

Почему возникает: исключение отлавливается, но программа не запрашивает ввод повторно. Кроме того, IndexError от ввода 999 не обработан.

✅ Исправление

def read_department_number(departments):
    while True:
        try:
            selected_index = int(input("Enter department number: "))
            if 1 <= selected_index <= len(departments):
                return selected_index
            print("Invalid department number. Please try again.")
        except ValueError:
            print("Invalid input. Please enter a number.")

Повторяем запрос до тех пор, пока не получим корректный номер в допустимом диапазоне.