Email Sending in Lback Framework
Lback provides a dedicated utility for sending emails, encapsulated within the EmailSender class. This class is designed to be configurable and flexible, allowing you to integrate email functionalities like account verification, password resets, and general notifications into your application.
—
1. The EmailSender Class
The EmailSender class (located in your project’s utilities, e.g., lback/utils/email_sender.py) is responsible for handling all aspects of sending emails via an SMTP server. It leverages Python’s built-in smtplib and email.mime modules.
Key Features:
Configurable SMTP Settings: All SMTP server details (host, port, credentials, TLS) are provided during initialization, making the sender reusable.
HTML and Plain Text Support: Easily send emails in either plain text or rich HTML format.
Dedicated Methods: Includes convenience methods for common email types like verification and password reset emails, which generate pre-formatted HTML bodies.
Robust Error Logging: Comprehensive logging helps diagnose any issues during email transmission.
Initialization Parameters:
The EmailSender is initialized with the following parameters:
smtp_server (str): The address of your SMTP server (e.g.,
'smtp.mailprovider.com').smtp_port (int): The port number for your SMTP server (commonly
587for TLS or465for SSL).smtp_username (str): The username for SMTP authentication.
smtp_password (str): The password for SMTP authentication.
sender_email (str): The email address that will appear as the sender.
use_tls (bool, default: True): Whether to enable Transport Layer Security (TLS) for a secure connection. Highly recommended.
sender_name (str, default: “Your App Name”): The display name that will appear as the sender (e.g., “Lback App Support”).
—
2. Email Configuration in settings.py
To integrate email sending into your Lback application, you should define your SMTP server settings in your settings.py file. This allows for easy management and separation of sensitive credentials from your code.
Example settings.py Email Settings:
SMTP_SERVER = "smtp.example.com" # Example: "smtp.gmail.com" for Gmail
SMTP_PORT = 587
EMAIL_USERNAME = "your_app_email@example.com" # Example: "app.notifications@example.com".
EMAIL_PASSWORD = "your_email_app_password" # Example: "abcd efgh ijkl mnop" (for app passwords)
SENDER_EMAIL = "no-reply@your-domain.com" # Example: "noreply@example.com"
USE_TLS = True
SENDER_NAME = "Your Application" # Example: "MyProject Notifications"
Note: It’s a best practice to load sensitive information like passwords from environment variables (e.g., using os.environ.get()) rather than hardcoding them directly in settings.py.
—
3. Using the EmailSender in Views (or other modules)
The EmailSender object needs to be initialized with your configuration. This is typically done once when your application starts up, and then the instance is made available where needed (e.g., passed to views, or accessed via a global application context).
Example View: Sending a Password Reset Email
Let’s say you have a password reset functionality where you generate a unique token and send a link to the user’s email.
# In your views/auth.py or similar file
from lback.core.types import Request
from lback.core.config import Config
from lback.utils.email_sender import EmailSender # Assuming you put EmailSender in utils/email_sender.py
from lback.core.response import Response
from lback.utils.db_session import get_db_session # Assuming you have a way to get db_session
from lback.utils.shortcuts import render, redirect
# from your_app.models import User, PasswordResetToken # Example models
import uuid
from datetime import datetime, timedelta
def forgot_password_view(request: Request) -> Response:
db_session = get_db_session(request) # Get DB session from request context
if request.method == 'POST':
email = request.form.get('email')
if not email:
request.session['flash_messages'].append({'message': 'Please enter your email address.', 'category': 'error'})
return redirect('/forgot-password')
user = db_session.query(User).filter_by(email=email).first()
if not user:
# To prevent email enumeration, always respond as if email was sent
request.session['flash_messages'].append({'message': 'If an account with that email exists, a password reset link has been sent.', 'category': 'info'})
return redirect('/forgot-password')
# Generate a unique token
token = str(uuid.uuid4())
expires_at = datetime.utcnow() + timedelta(hours=1) # Link valid for 1 hour
# Save the token to the database (associate with user)
new_reset_token = PasswordResetToken(user_id=user.id, token=token, expires_at=expires_at)
db_session.add(new_reset_token)
db_session.commit()
# Construct the reset link (replace with your actual domain and route)
reset_link = f"{request.base_url_no_path}/reset-password?token={token}"
# Instantiate EmailSender with your config (in a real app, this might be a singleton)
app_config = Config() # Load config for demonstration
email_sender = EmailSender(
smtp_server=app_config.SMTP_SERVER,
smtp_port=app_config.SMTP_PORT,
smtp_username=app_config.SMTP_USERNAME,
smtp_password=app_config.SMTP_PASSWORD,
sender_email=app_config.SENDER_EMAIL,
use_tls=app_config.USE_TLS,
sender_name=app_config.SENDER_NAME
)
# Send the email
if email_sender.send_password_reset_email(user.email, user.username, reset_link):
request.session['flash_messages'].append({'message': 'A password reset link has been sent to your email.', 'category': 'success'})
else:
request.session['flash_messages'].append({'message': 'Failed to send password reset email. Please try again later.', 'category': 'error'})
return redirect('/forgot-password')
return render_template('forgot_password.html', request=request)
def contact_us_view(request: Request) -> Response:
db_session = get_db_session(request)
if request.method == 'POST':
name = request.form.get('name')
email = request.form.get('email')
subject = request.form.get('subject')
message = request.form.get('message')
# Basic validation
if not all([name, email, subject, message]):
request.session['flash_messages'].append({'message': 'All fields are required.', 'category': 'error'})
return redirect('/contact-us')
# Instantiate EmailSender
app_config = Config()
email_sender = EmailSender(
smtp_server=app_config.SMTP_SERVER,
smtp_port=app_config.SMTP_PORT,
smtp_username=app_config.SMTP_USERNAME,
smtp_password=app_config.SMTP_PASSWORD,
sender_email=app_config.SENDER_EMAIL,
use_tls=app_config.USE_TLS,
sender_name=app_config.SENDER_NAME
)
# Define the recipient (e.g., your admin email)
admin_email = app_config.ADMIN_EMAIL # Make sure you have this in your config
email_body = f"""
New Contact Form Submission:
Name: {name}
Email: {email}
Subject: {subject}
Message:
{message}
"""
if email_sender.send_generic_email(admin_email, f"Contact Form: {subject}", email_body, is_html=False):
request.session['flash_messages'].append({'message': 'Your message has been sent successfully!', 'category': 'success'})
else:
request.session['flash_messages'].append({'message': 'Failed to send your message. Please try again later.', 'category': 'error'})
return redirect('/contact-us')
return render_template('contact_us.html', request=request)
Important Considerations:
Asynchronous Sending: For production applications, sending emails synchronously within a web request can block the response and degrade user experience. Consider implementing an asynchronous task queue (e.g., Celery, RQ) to send emails in the background.
Error Handling: The
send_emailmethod returnsTrueon success andFalseon failure. Always check this return value and provide appropriate feedback to the user.Templating: For more complex email bodies, especially HTML emails, you might want to use a dedicated templating engine (like Jinja2) to render your email content before passing it to
EmailSender.
—
By leveraging the EmailSender class and its configuration, you can robustly integrate email communication into your Lback applications.