Skip to main content

Рефакторинг уровней доступа к данным

Copilot Chat может предложить способы отделить код доступа к данным от бизнес-логики, что упрощает обслуживание и масштабирование приложения.

Жестко закодированные запросы SQL и код жесткого доступа к данным могут затруднить масштабирование или переключение баз данных, и они часто приводят к повторяемой логике.

Copilot Chat может рекомендовать реализацию шаблона репозитория или уровня доступа к данным, который абстрагирует взаимодействие с базой данных, что делает код более модульным и уменьшающим избыточность. Кроме того, он может помочь рефакторингу запросов SQL в оптимизированные версии, повысить производительность и удобство обслуживания.

Note

Ответы, описанные в этой статье, являются примерами. Ответы Copilot Chat являются недетерминированными, поэтому вы можете получить различные ответы, показанные здесь.

Пример сценария

Этот код Python подключается к базе данных SQLite, извлекает запись пользователя и возвращает данные пользователя. Однако она не может абстрагировать логику подключения к базе данных и использует жестко закодированный запрос, который уязвим для внедрения SQL.

import sqlite3

def get_user_by_id(user_id):
    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute(f"SELECT display_name FROM users WHERE id = {user_id}")
    user = cursor.fetchone()
    conn.close()
    return user

Пример запроса 1

Для начала можно задать Copilot общий вопрос о том, как улучшить код.

How can I improve this code to make it safe and easier to update and expand? List possible improvements but don't show revised code.

Пример ответа 1

Copilot предлагает несколько предложений по улучшению кода:

  • Абстрагирование сведений о подключении к базе данных.
  • Используйте параметризованные запросы, чтобы предотвратить внедрение SQL.
  • Используйте диспетчер контекстов, чтобы убедиться, что подключение к базе данных правильно закрыто при возникновении ошибки.
  • Добавьте обработку ошибок.

Пример запроса 2

Вы можете использовать ответ на первый запрос, чтобы написать более конкретный запрос.

Rewrite this code to make it more scalable and easier to maintain. Use a context manager. Avoid hardcoded SQL queries and tightly coupled data access code. Instead, use a repository pattern to abstract database interactions and make the code more modular and reusable. Where possible optimize the code to improve performance. Include error trapping, and make sure the code is not vulnerable to SQL injection.

Пример ответа 2

Copilot предлагает разделить код на отдельные файлы, каждый из которых имеет определенную цель. Он также содержит пример кода для возврата сведений о пользователе.

Предлагаемые изменения реализуют шаблон репозитория и используют параметризованные запросы, чтобы избежать внедрения SQL.

# database.py
import sqlite3

class Database:
    def __init__(self, db_path):
        self.db_path = db_path

    def __enter__(self):
        self.conn = sqlite3.connect(self.db_path)
        self.cursor = self.conn.cursor()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type or exc_val or exc_tb:
            self.conn.rollback()
        else:
            self.conn.commit()
        self.conn.close()

# user_repository.py
class UserRepository:
    def __init__(self, db):
        self.db = db

    def get_user_by_id(self, user_id):
        try:
            self.db.cursor.execute("SELECT display_name FROM users WHERE id = ?", (user_id,))
            user = self.db.cursor.fetchone()
            return user
        except sqlite3.Error as e:
            print(f"An error occurred: {e}")
            return None

# main.py
from database import Database
from user_repository import UserRepository

def get_user_by_id(user_id):
    with Database('database.db') as db:
        user_repo = UserRepository(db)
        return user_repo.get_user_by_id(user_id)

# Example usage
if __name__ == "__main__":
    user = get_user_by_id(1)
    if user:
        print(user)
    else:
        print("User not found or an error occurred.")

Измененный код улучшает исходный код несколькими способами:

  • Теперь код реализует базовый уровень доступа к данным.
  • Класс Database обрабатывает подключение к базе данных SQLite, реализуя протокол диспетчера контекстов с __enter__ помощью методов и __exit__ методов. Это гарантирует правильное управление подключением к базе данных, включая фиксацию транзакций и закрытие подключения.
  • Класс UserRepository инкапсулирует логику доступа к данным пользователя.
  • Значения запросов параметризованы, чтобы предотвратить внедрение SQL.
  • Ошибки перехватываются с подробными сведениями, напечатанными в консоли.

Дополнительные материалы