- Security practices ā protecting your database
- Performance optimization ā making queries faster
- Code organization ā writing maintainable code
- Connection management ā handling connections properly
- Query best practices ā writing efficient SQL
- Error handling ā robust error management
Why Best Practices Matter
Following best practices when working with MySQL in Python is essential for building secure, performant, and maintainable applications. Poor practices can lead to security breaches, slow performance, and difficult-to-maintain code.
š” Key concept: Best practices are not just rules ā they're lessons learned from years of experience. Following them saves you from common mistakes.
Security
Protect against SQL injection, data breaches, and unauthorized access.
Performance
Write efficient queries and manage connections properly for speed.
Maintainability
Organize code so it's easy to understand, modify, and debug.
The Impact of Following Best Practices
# ============================================================
# WITHOUT BEST PRACTICES
# ============================================================
# ā Insecure, slow, hard to maintain
def get_user(name):
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'") # SQL Injection!
return cursor.fetchone()
# Every request creates a new connection
def get_data():
connection = mysql.connector.connect(...) # Slow!
# ... operations
return result
# No error handling
def update_user(id, data):
cursor.execute("UPDATE users SET ...")
# If this fails, app crashes!
# ============================================================
# WITH BEST PRACTICES
# ============================================================
# ā
Secure, fast, maintainable
def get_user(name):
cursor.execute("SELECT * FROM users WHERE name = %s", (name,)) # Safe!
return cursor.fetchone()
# Connection pooling for efficiency
pool = create_connection_pool()
# Error handling with try/except
def update_user(id, data):
try:
cursor.execute("UPDATE users SET ...")
connection.commit()
except Exception as e:
connection.rollback()
logger.error(f"Update failed: {e}")
raise
# The difference is huge!
Key point: Best practices make your application secure, fast, and reliable.
Quick Check: Why are best practices important? (Answer: They ensure security, performance, and maintainability)
Security Best Practices
Protecting Your Database
# ============================================================
# SECURITY BEST PRACTICES
# ============================================================
print("1. ALWAYS USE PARAMETERIZED QUERIES")
print(" - Use %s placeholders")
print(" - Never use string concatenation or f-strings")
print(" - Protects against SQL injection")
print("\n2. NEVER STORE PASSWORDS IN PLAIN TEXT")
print(" - Hash passwords using bcrypt or argon2")
print(" - Never store actual passwords")
print("\n3. USE ENVIRONMENT VARIABLES")
print(" - Store credentials in environment variables")
print(" - Never hardcode passwords in code")
print("\n4. USE THE LEAST PRIVILEGE PRINCIPLE")
print(" - Database user should have minimal permissions")
print(" - Only what the application needs")
print("\n5. VALIDATE USER INPUT")
print(" - Validate all input before using in queries")
print(" - Check types, lengths, and format")
print("\n6. USE HTTPS FOR CONNECTIONS")
print(" - Encrypt data in transit")
print(" - Use SSL/TLS for database connections")
print("\n7. KEEP DRIVERS UPDATED")
print(" - Regular updates for security patches")
print(" - Update mysql-connector-python")
print("\n8. LOG SUSPICIOUS ACTIVITY")
print(" - Monitor for SQL injection attempts")
print(" - Log and alert on suspicious queries")
# ============================================================
# IMPLEMENTATION EXAMPLE
# ============================================================
import os
import bcrypt
import mysql.connector
class SecureDatabase:
"""Database with security best practices"""
def __init__(self):
# Credentials from environment variables
self.config = {
'host': os.getenv('DB_HOST', 'localhost'),
'user': os.getenv('DB_USER', 'root'),
'password': os.getenv('DB_PASSWORD'),
'database': os.getenv('DB_NAME', 'myapp_db')
}
if not self.config['password']:
raise ValueError("DB_PASSWORD environment variable is required")
def hash_password(self, password):
"""Hash password using bcrypt"""
salt = bcrypt.gensalt()
return bcrypt.hashpw(password.encode(), salt)
def verify_password(self, password, hashed):
"""Verify password against hash"""
return bcrypt.checkpw(password.encode(), hashed)
def create_user(self, username, password, email):
"""Create user with hashed password"""
hashed = self.hash_password(password)
query = "INSERT INTO users (username, password_hash, email) VALUES (%s, %s, %s)"
# Parameterized query - safe from injection
self.cursor.execute(query, (username, hashed, email))
self.connection.commit()
Security summary:
- Parameterized queries ā prevent SQL injection
- Environment variables ā keep credentials safe
- Least privilege ā minimize access
- Input validation ā verify all data
- Password hashing ā never store plain text
Quick Check: What is the #1 way to prevent SQL injection? (Answer: Use parameterized queries)
Performance Best Practices
Making Your Application Fast
# ============================================================
# PERFORMANCE BEST PRACTICES
# ============================================================
print("1. USE CONNECTION POOLING")
print(" - Reuse connections instead of creating new ones")
print(" - Significant speed improvement")
print("\n2. SELECT ONLY NEEDED COLUMNS")
print(" - Use specific columns instead of SELECT *")
print(" - Reduces data transfer")
print("\n3. USE INDEXES WISELY")
print(" - Index columns used in WHERE, JOIN, ORDER BY")
print(" - But don't over-index (slows INSERT/UPDATE)")
print("\n4. USE BATCH OPERATIONS")
print(" - Use executemany() for multiple inserts")
print(" - Reduces round trips")
print("\n5. USE LIMIT FOR LARGE RESULTS")
print(" - Always use LIMIT for large datasets")
print(" - Implement pagination")
print("\n6. AVOID SELECT * IN JOINS")
print(" - Specify only needed columns")
print(" - Reduces data transfer")
print("\n7. USE EXPLAIN TO ANALYZE QUERIES")
print(" - Check query execution plans")
print(" - Identify slow queries")
print("\n8. CACHE FREQUENTLY ACCESSED DATA")
print(" - Use Redis or Memcached")
print(" - Reduce database load")
# ============================================================
# PERFORMANCE COMPARISON
# ============================================================
print("""
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Practice ā Impact ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Connection Pooling ā 5-10x faster ā
ā Specific Columns ā 2-3x faster ā
ā Indexes ā 10-100x faster for searches ā
ā Batch Operations ā 5-10x faster for inserts ā
ā LIMIT ā Prevents timeouts ā
ā Caching ā 100x faster for repeated queries ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
""")
Performance summary:
- Connection pooling ā reuse connections
- Specific columns ā reduce data transfer
- Indexes ā speed up searches
- Batch operations ā reduce round trips
- LIMIT ā prevent large result sets
- Caching ā avoid repeated queries
Quick Check: What is the most important performance practice? (Answer: Use connection pooling and indexes)
Code Organization
Writing Maintainable Database Code
# ============================================================
# CODE ORGANIZATION BEST PRACTICES
# ============================================================
print("1. USE A DATABASE LAYER")
print(" - Separate database code from business logic")
print(" - Use a dedicated Database class")
print("\n2. USE CONTEXT MANAGERS")
print(" - Use 'with' statements for connections")
print(" - Automatic cleanup")
print("\n3. USE CONSTANTS FOR QUERIES")
print(" - Store queries as constants")
print(" - Easier to maintain")
print("\n4. USE TYPES AND DOCSTRINGS")
print(" - Add type hints")
print(" - Document functions and queries")
print("\n5. SEPARATE READ AND WRITE OPERATIONS")
print(" - Different functions for different operations")
print(" - Clear separation of concerns")
print("\n6. USE REPOSITORY PATTERN")
print(" - One repository per table/model")
print(" - Encapsulates database operations")
# ============================================================
# EXAMPLE: CLEAN DATABASE LAYER
# ============================================================
from contextlib import contextmanager
class Database:
"""Database layer with clean organization"""
def __init__(self, config):
self.config = config
self.pool = self._create_pool()
def _create_pool(self):
"""Create connection pool"""
from mysql.connector import pooling
return pooling.MySQLConnectionPool(**self.config)
@contextmanager
def get_connection(self):
"""Context manager for connections"""
connection = self.pool.get_connection()
try:
yield connection
finally:
connection.close()
def execute_query(self, query, params=None, fetch_all=True):
"""Execute query with automatic connection management"""
with self.get_connection() as conn:
cursor = conn.cursor()
try:
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
if query.strip().upper().startswith("SELECT"):
return cursor.fetchall() if fetch_all else cursor.fetchone()
else:
conn.commit()
return cursor.rowcount
except Exception as e:
conn.rollback()
raise
# Repository pattern
class UserRepository:
"""User repository for database operations"""
def __init__(self, db):
self.db = db
def find_by_id(self, user_id):
query = "SELECT * FROM users WHERE id = %s"
return self.db.execute_query(query, (user_id,), fetch_all=False)
def find_by_email(self, email):
query = "SELECT * FROM users WHERE email = %s"
return self.db.execute_query(query, (email,), fetch_all=False)
def create(self, username, email, password_hash):
query = "INSERT INTO users (username, email, password_hash) VALUES (%s, %s, %s)"
return self.db.execute_query(query, (username, email, password_hash))
Code organization summary:
- Database layer ā separate from business logic
- Context managers ā automatic cleanup
- Repository pattern ā organized data access
- Constants ā maintainable queries
- Type hints ā better code quality
Quick Check: What pattern is recommended for organizing database code? (Answer: Repository pattern with a database layer)
Connection Management
Handling Database Connections Properly
# ============================================================
# CONNECTION MANAGEMENT BEST PRACTICES
# ============================================================
print("1. USE CONNECTION POOLING")
print(" - Create a pool once, reuse connections")
print(" - Prevents connection overhead")
print("\n2. ALWAYS CLOSE CONNECTIONS")
print(" - Use context managers (with statement)")
print(" - Never leave connections open")
print("\n3. SET CONNECTION TIMEOUTS")
print(" - Prevent hanging on dead connections")
print(" - Set reasonable timeout values")
print("\n4. HANDLE CONNECTION LOSS")
print(" - Check if connection is alive")
print(" - Reconnect if needed")
print("\n5. USE SINGLE CONNECTION PER REQUEST")
print(" - In web apps, one connection per request")
print(" - Don't create multiple connections")
print("\n6. MONITOR CONNECTION USAGE")
print(" - Track active connections")
print(" - Detect connection leaks")
# ============================================================
# IMPLEMENTATION EXAMPLE
# ============================================================
import time
from contextlib import contextmanager
class ConnectionManager:
"""Connection management with retry and pooling"""
def __init__(self, config, pool_size=5):
self.config = config
self.pool_size = pool_size
self.pool = None
self._create_pool()
def _create_pool(self):
"""Create connection pool"""
config = self.config.copy()
config.update({
'pool_name': 'app_pool',
'pool_size': self.pool_size,
'pool_reset_session': True
})
from mysql.connector import pooling
self.pool = pooling.MySQLConnectionPool(**config)
@contextmanager
def get_connection(self, retries=3, delay=1):
"""Get connection with retry logic"""
for attempt in range(retries):
try:
connection = self.pool.get_connection()
try:
yield connection
finally:
connection.close() # Returns to pool
return
except Exception as e:
if attempt < retries - 1:
time.sleep(delay * (attempt + 1))
else:
raise
def check_connection(self):
"""Check if pool has available connections"""
# Implementation depends on connector version
return True
# Usage
manager = ConnectionManager(config)
with manager.get_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT 1")
result = cursor.fetchone()
# Connection automatically returned to pool
Connection management summary:
- Connection pooling ā reuse connections
- Context managers ā automatic cleanup
- Timeouts ā prevent hanging
- Reconnection ā handle connection loss
- Monitoring ā track usage
Quick Check: What happens when you close a connection from a pool? (Answer: It returns to the pool, doesn't close)
Query Best Practices
Writing Better SQL Queries
# ============================================================
# QUERY BEST PRACTICES
# ============================================================
print("1. USE EXPLICIT COLUMN NAMES")
print(" - Don't use SELECT *")
print(" - List only needed columns")
print("\n2. USE WHERE CLAUSE EFFECTIVELY")
print(" - Filter at the database level")
print(" - Don't filter in Python")
print("\n3. USE LIMIT FOR PAGINATION")
print(" - Use LIMIT and OFFSET")
print(" - Avoid fetching all data")
print("\n4. AVOID N+1 QUERIES")
print(" - Use JOINs to fetch related data")
print(" - Don't query in loops")
print("\n5. USE EXPLAIN TO ANALYZE")
print(" - Check query execution plans")
print(" - Identify slow queries")
print("\n6. USE PARAMETERIZED QUERIES")
print(" - Always use %s placeholders")
print(" - Security and performance")
print("\n7. USE TRANSACTIONS FOR RELATED OPERATIONS")
print(" - Group related operations")
print(" - Commit or rollback as a unit")
print("\n8. AVOID FUNCTIONS ON INDEXED COLUMNS")
print(" - WHERE YEAR(date) = 2024 is slow")
print(" - Use date BETWEEN '2024-01-01' AND '2024-12-31'")
# ============================================================
# QUERY COMPARISON
# ============================================================
# ā BAD: SELECT *
cursor.execute("SELECT * FROM users")
# Fetches all columns, even unused ones
# ā
GOOD: Specific columns
cursor.execute("SELECT id, username, email FROM users")
# Only what you need
# ā BAD: Filtering in Python
cursor.execute("SELECT * FROM users")
for user in cursor.fetchall():
if user[2] == 'admin': # Filter in Python
process(user)
# ā
GOOD: Filter in SQL
cursor.execute("SELECT * FROM users WHERE role = 'admin'")
for user in cursor.fetchall():
process(user)
# ā BAD: N+1 queries
for user in users:
cursor.execute("SELECT * FROM orders WHERE user_id = %s", (user['id'],))
# ā
GOOD: Single query with JOIN
cursor.execute("""
SELECT u.*, o.*
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
""")
Query best practices summary:
- Explicit columns ā avoid SELECT *
- Filter at database ā use WHERE effectively
- LIMIT ā for pagination
- Avoid N+1 ā use JOINs
- EXPLAIN ā analyze queries
- Parameterized ā always for security
Quick Check: What is the N+1 query problem? (Answer: Querying in a loop instead of using a JOIN)
Error Handling
Handling Errors Gracefully
# ============================================================
# ERROR HANDLING BEST PRACTICES
# ============================================================
print("1. USE TRY/EXCEPT BLOCKS")
print(" - Always wrap database operations in try/except")
print(" - Never let errors crash your program")
print("\n2. HANDLE SPECIFIC ERROR TYPES")
print(" - Different errors need different handling")
print(" - Use specific exception classes")
print("\n3. USE ROLLBACK ON ERRORS")
print(" - Rollback on any database error")
print(" - Keep data consistent")
print("\n4. LOG ERRORS FOR DEBUGGING")
print(" - Log all database errors")
print(" - Include context (query, params)")
print("\n5. PROVIDE USER-FRIENDLY MESSAGES")
print(" - Don't show raw database errors")
print(" - Show helpful messages")
print("\n6. RETRY TRANSIENT ERRORS")
print(" - Retry on timeouts and network issues")
print(" - Use exponential backoff")
print("\n7. NEVER IGNORE ERRORS")
print(" - Don't use empty except blocks")
print(" - Always handle or log errors")
# ============================================================
# IMPLEMENTATION EXAMPLE
# ============================================================
import logging
from mysql.connector import Error, IntegrityError, OperationalError
logger = logging.getLogger(__name__)
def execute_safely(query, params=None):
"""Execute query with comprehensive error handling"""
try:
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
connection.commit()
return cursor.fetchall() if query.strip().upper().startswith("SELECT") else cursor.rowcount
except IntegrityError as e:
# Data integrity errors (duplicate, foreign key)
connection.rollback()
if e.errno == 1062:
logger.warning(f"Duplicate entry: {e}")
return {"error": "duplicate", "message": "Record already exists"}
elif e.errno == 1452:
logger.warning(f"Foreign key violation: {e}")
return {"error": "foreign_key", "message": "Referenced record not found"}
else:
logger.error(f"Integrity error: {e}")
return {"error": "integrity", "message": "Data integrity error"}
except OperationalError as e:
# Connection issues, timeouts
connection.rollback()
logger.error(f"Operational error: {e}")
# Could retry here
return {"error": "operational", "message": "Database operation failed"}
except Error as e:
# Other database errors
connection.rollback()
logger.error(f"Database error: {e}")
return {"error": "database", "message": "Database error occurred"}
except Exception as e:
# Unexpected errors
connection.rollback()
logger.error(f"Unexpected error: {e}")
return {"error": "unknown", "message": "An unexpected error occurred"}
Error handling summary:
- Try/except ā always handle errors
- Specific errors ā different handling for different errors
- Rollback ā on any error
- Logging ā for debugging
- User messages ā friendly and helpful
- Retry ā for transient errors
Quick Check: What should you do on any database error? (Answer: Rollback, log, and handle appropriately)
Real-World Example: Production-Ready Service
Building a Production-Ready Database Service
# ============================================================
# PRODUCTION-READY DATABASE SERVICE
# ============================================================
import os
import logging
import time
from contextlib import contextmanager
from mysql.connector import pooling, Error
class ProductionDatabase:
"""Production-ready database service with best practices"""
def __init__(self):
self.config = self._load_config()
self.pool = self._create_pool()
self.logger = self._setup_logger()
self.max_retries = 3
self.retry_delay = 1
def _load_config(self):
"""Load configuration from environment"""
return {
'host': os.getenv('DB_HOST', 'localhost'),
'user': os.getenv('DB_USER', 'root'),
'password': os.getenv('DB_PASSWORD'),
'database': os.getenv('DB_NAME', 'myapp_db'),
'pool_name': os.getenv('DB_POOL_NAME', 'app_pool'),
'pool_size': int(os.getenv('DB_POOL_SIZE', '5')),
'charset': 'utf8mb4',
'use_unicode': True,
'autocommit': False
}
def _setup_logger(self):
"""Set up logging"""
logger = logging.getLogger(__name__)
if not logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
return logger
def _create_pool(self):
"""Create connection pool"""
try:
return pooling.MySQLConnectionPool(**self.config)
except Exception as e:
self.logger.error(f"Failed to create connection pool: {e}")
raise
@contextmanager
def get_connection(self):
"""Get connection with automatic cleanup"""
connection = None
try:
connection = self.pool.get_connection()
yield connection
except Exception as e:
if connection:
connection.rollback()
self.logger.error(f"Connection error: {e}")
raise
finally:
if connection:
connection.close()
def execute(self, query, params=None, retry=True):
"""Execute query with retry and error handling"""
def _execute():
with self.get_connection() as conn:
cursor = conn.cursor()
try:
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
if query.strip().upper().startswith('SELECT'):
return cursor.fetchall()
else:
conn.commit()
return cursor.rowcount
except Exception as e:
conn.rollback()
raise
if not retry:
return _execute()
for attempt in range(self.max_retries):
try:
return _execute()
except Error as e:
self.logger.warning(
f"Query attempt {attempt + 1} failed: {e}"
)
if attempt < self.max_retries - 1:
time.sleep(self.retry_delay * (attempt + 1))
else:
self.logger.error(f"Query failed after {self.max_retries} attempts")
raise
def get_stats(self):
"""Get database statistics"""
try:
result = self.execute("SELECT COUNT(*) FROM information_schema.tables")
return {
'status': 'healthy',
'table_count': result[0][0] if result else 0
}
except:
return {'status': 'unhealthy'}
def close(self):
"""Close all connections"""
try:
self.pool.close()
self.logger.info("Connection pool closed")
except Exception as e:
self.logger.error(f"Error closing pool: {e}")
# ============================================================
# APPLICATION ENTRY POINT
# ============================================================
def main():
"""Main application with production database"""
# Initialize database
db = ProductionDatabase()
try:
# Health check
stats = db.get_stats()
print(f"Database status: {stats['status']}")
# Example operations
result = db.execute("SELECT * FROM students LIMIT 5")
print(f"Students: {len(result)}")
except Exception as e:
print(f"Application error: {e}")
finally:
db.close()
if __name__ == "__main__":
main()
This production service includes:
- Connection pooling with configuration
- Context managers for cleanup
- Retry logic with exponential backoff
- Comprehensive logging
- Error handling and rollback
- Health monitoring
- Environment-based configuration
Quick Check: What makes a database service production-ready? (Answer: Connection pooling, retry logic, logging, error handling, and proper configuration)
Complete Best Practices Checklist
Quick Reference for All Practices
# ============================================================
# BEST PRACTICES CHECKLIST
# ============================================================
print("SECURITY:")
print(" ā Use parameterized queries for all user input")
print(" ā Store credentials in environment variables")
print(" ā Hash passwords using bcrypt or argon2")
print(" ā Use least privilege database users")
print(" ā Validate all user input")
print(" ā Keep drivers updated")
print(" ā Use SSL/TLS for connections")
print("\nPERFORMANCE:")
print(" ā Use connection pooling")
print(" ā Select only needed columns")
print(" ā Use indexes on WHERE/JOIN columns")
print(" ā Use batch operations (executemany)")
print(" ā Use LIMIT for pagination")
print(" ā Avoid N+1 queries")
print(" ā Cache frequently accessed data")
print(" ā Use EXPLAIN to analyze queries")
print("\nCODE ORGANIZATION:")
print(" ā Use a database layer")
print(" ā Use repository pattern")
print(" ā Use context managers")
print(" ā Store queries as constants")
print(" ā Use type hints")
print(" ā Write docstrings")
print(" ā Separate read and write operations")
print("\nCONNECTION MANAGEMENT:")
print(" ā Use connection pooling")
print(" ā Always close connections")
print(" ā Set connection timeouts")
print(" ā Handle connection loss")
print(" ā Monitor connection usage")
print("\nQUERY BEST PRACTICES:")
print(" ā Use explicit column names")
print(" ā Filter at the database level")
print(" ā Use LIMIT for pagination")
print(" ā Avoid N+1 queries")
print(" ā Use EXPLAIN to analyze")
print(" ā Use parameterized queries")
print(" ā Use transactions")
print(" ā Avoid functions on indexed columns")
print("\nERROR HANDLING:")
print(" ā Use try/except blocks")
print(" ā Handle specific error types")
print(" ā Use rollback on errors")
print(" ā Log errors with context")
print(" ā Provide user-friendly messages")
print(" ā Retry transient errors")
print(" ā Never ignore errors")
Use this checklist when building any MySQL application with Python.
Quick Check: What should you do before deploying any database application? (Answer: Review this checklist and ensure all points are covered)
Try It Yourself
See how best practices compare to bad practices in the editor below.
BEST PRACTICES - PRACTICE
========================================
1. SECURITY COMPARISON
----------------------------------------
Good Practice - Parameterized Query:
[Good] Executing: SELECT * FROM users WHERE name ... with params: ("Robert'; DROP TABLE users; --",)
[Good] SQL injection prevented
Bad Practice - String Concatenation:
[Bad] Executing: SELECT * FROM users WHERE name ...
[Bad] SQL injection possible
2. PERFORMANCE COMPARISON
----------------------------------------
Good Practice - Specific Columns:
SELECT id, username, email FROM users
ā Only what you need, faster
Bad Practice - SELECT *:
SELECT * FROM users
ā All columns, slower
3. CONNECTION MANAGEMENT
----------------------------------------
Good Practice - Connection Pool:
pool.get_connection() # Reuses connections
ā 100 requests = 5 connections created
Bad Practice - New Connection:
mysql.connector.connect(...) # Creates new connection
ā 100 requests = 100 connections created
4. CODE ORGANIZATION
----------------------------------------
Good Practice - Repository Pattern:
user_repo.find_by_id(1)
user_repo.create(data)
ā Clean, organized, maintainable
Bad Practice - Scattered Queries:
db.execute('SELECT * FROM users WHERE id = 1')
db.execute('INSERT INTO users ...')
ā Hard to maintain
5. ERROR HANDLING
----------------------------------------
Good Practice - Try/Except:
try:
cursor.execute(query)
except Exception as e:
logger.error(e)
connection.rollback()
ā Graceful error handling
Bad Practice - No Error Handling:
cursor.execute(query)
ā Crashes on error
š Best Practices Summary:
āāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Category ā Key Practice ā
āāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Security ā Parameterized queries ā
ā Performance ā Connection pooling + indexes ā
ā Code ā Repository pattern ā
ā Connections ā Use pools, always close ā
ā Queries ā Specific columns, JOIN over N+1 ā
ā Errors ā Try/except + rollback + logging ā
āāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Following best practices makes your application secure and reliable!
You've Got It!
You now know the best practices for using MySQL with Python. You understand security, performance, code organization, and error handling.
Quick Quiz
Test what you've learned:
Frequently Asked Questions
What is the most important best practice?
How do I know if my code follows best practices?
What is a common interview question about MySQL best practices?
Should I use an ORM or write raw SQL?
How often should I review my database code?
Where to Go From Here
Now that you know the best practices, here are some next steps:
Practice Assignments
Test your knowledge with practical exercises.
Practice Now āConnection Management
Review connection handling in detail.
Learn More āParameterized Queries
Deep dive into secure queries.
Learn More ā