from typing import List, Union from django.core.mail import send_mail, EmailMessage from django.contrib.auth import get_user_model from django.template.loader import render_to_string from django.conf import settings # To access EMAIL_HOST_USER, etc. UserModel = get_user_model() User = UserModel # Type alias for clarity class EmailService: """ A service class for sending single or bulk emails. """ def _send_email_internal( self, subject: str, body: str, recipient_list: List[str], from_email: str = settings.DEFAULT_FROM_EMAIL, html_content: Union[str, None] = None ) -> int: """ Internal method to handle the actual sending using Django's email backend. """ try: # Using EmailMessage for more control (e.g., HTML content) email = EmailMessage( subject=subject, body=body, from_email=from_email, to=recipient_list, ) if html_content: email.content_subtype = "html" # Main content is HTML email.body = html_content # Overwrite body with HTML # Returns the number of successfully sent emails (usually 1 or the count of recipients) sent_count = email.send(fail_silently=False) return sent_count except Exception as e: # Log the error (in a real app, use Django's logger) print(f"Error sending email to {recipient_list}: {e}") return 0 def send_single_email( self, user: User, subject: str, template_name: str, context: dict, from_email: str = settings.DEFAULT_FROM_EMAIL ) -> int: """ Sends a single, template-based email to one user. """ recipient_list = [user.email] # 1. Render content from template html_content = render_to_string(template_name, context) # You can optionally render a plain text version as well: # text_content = strip_tags(html_content) # 2. Call internal sender return self._send_email_internal( subject=subject, body="", # Can be empty if html_content is provided recipient_list=recipient_list, from_email=from_email, html_content=html_content ) def send_bulk_email( self, recipient_emails: List[str], subject: str, template_name: str, context: dict, from_email: str = settings.DEFAULT_FROM_EMAIL ) -> int: """ Sends the same template-based email to a list of email addresses. Note: Django's EmailMessage can handle multiple recipients in one transaction, which is often more efficient than sending them one-by-one. """ # 1. Render content from template (once) html_content = render_to_string(template_name, context) # 2. Call internal sender with all recipients # The result here is usually 1 if successful, as it uses a single # EmailMessage call for all recipients. sent_count = self._send_email_internal( subject=subject, body="", recipient_list=recipient_emails, from_email=from_email, html_content=html_content ) # Return the count of recipients if successful, or 0 if failure return len(recipient_emails) if sent_count > 0 else 0