Las consultas SQL codificadas de forma rígida y el código de acceso a datos estrechamente acoplado pueden dificultar el escalado o cambio de bases de datos y, a menudo, dan lugar a una lógica repetida.
El Copilot Chat puede recomendar la implementación de un patrón de repositorio o una capa de acceso a datos que abstraiga las interacciones de la base de datos, lo que hace que el código sea más modular y reduce la redundancia. También puede ayudar a refactorizar las consultas SQL en versiones optimizadas, lo que mejora el rendimiento y la capacidad de mantenimiento.
Note
Las respuestas descritas en este artículo son ejemplos. Las respuestas del Copilot Chat no son deterministas, por lo que puedes obtener respuestas diferentes a las aquí mostradas.
Escenario de ejemplo
Este código de Python se conecta a una base de datos de SQLite, recupera un registro de usuario y devuelve los datos del usuario. Sin embargo, no puede abstraer la lógica de conexión de la base de datos y se usa una consulta codificada de forma rígida que es vulnerable a la inyección de código 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
Indicación de ejemplo 1
Para empezar, formula a Copilot una pregunta general sobre cómo mejorar el código.
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.
Respuesta de ejemplo 1
Copilot realiza varias sugerencias para mejorar el código:
- Abstrae los detalles de conexión de la base de datos.
- Usa consultas con parámetros para evitar la inyección de código SQL.
- Usa un administrador de contextos para asegurarte de que la conexión de la base de datos se cierre correctamente si se produce un error.
- Agrega control de errores.
Indicación de ejemplo 2
Puedes usar la respuesta a la primera indicación para escribir una indicación más específica.
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.
Respuesta de ejemplo 2
Copilot sugiere dividir el código en archivos independientes, cada uno con un propósito específico. También proporciona código de ejemplo para devolver los detalles de un usuario.
Los cambios sugeridos implementan un patrón de repositorio y usan consultas con parámetros para evitar la inyección de código 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.")
El código revisado mejora el original de varias maneras:
- El código implementa ahora una capa de acceso a datos básica.
- La clase
Database
controla la conexión a la base de datos SQLite e implementa el protocolo del administrador de contextos con los métodos__enter__
y__exit__
. Esto garantiza que la conexión de base de datos se administre correctamente, incluidos la confirmación de transacciones y el cierre de la conexión. - La clase
UserRepository
encapsula la lógica para acceder a los datos de usuario. - Los valores de las consultas se parametrizan para evitar la inyección de código SQL.
- Se detectan errores y los detalles se imprimen en la consola.