Django đã có sẵn hàm send_mail() để gửi email — bạn chỉ cần cấu hình SMTP một lần trong settings.py rồi gọi một dòng là gửi được. Dùng khi cần gửi mail chào mừng sau đăng ký, xác nhận đơn hàng, đặt lại mật khẩu hay thông báo hệ thống. Không cần cài thêm thư viện, cũng không cần tự viết code SMTP bằng smtplib.
Vấn đề
Nhiều dự án mới gọi send_mail() ngay mà chưa cấu hình gì, vì nghĩ Django “tự biết” gửi mail đi đâu. Thực tế Django mặc định dùng SMTP backend trỏ về localhost:25 — máy dev thường không có mail server nên request treo rồi lỗi ConnectionRefusedError, người dùng đăng ký xong chờ mãi không thấy mail chào mừng. Lỗi khác cũng hay gặp là hardcode mật khẩu email thẳng vào settings.py rồi vô tình commit lên Git — tài khoản mail bị lộ, kẻ xấu dùng để gửi spam.
from django.core.mail import send_mailsend_mail( "Chao mung ban den voi MyShop", "Cam on ban da dang ky tai khoan.", "noreply@example.com", ["khach@example.com"], fail_silently=False,)# ConnectionRefusedError: [Errno 111] Connection refused (localhost:25)
Giải pháp
Cách đúng là tách cấu hình ra biến môi trường và để Django lo phần SMTP. Trong settings.py, khai báo backend SMTP cùng host, cổng và tài khoản — mật khẩu lấy từ biến môi trường, tuyệt đối không ghi thẳng vào code. Khi phát triển local, đổi sang console backend để mail in ra terminal thay vì gửi thật, khỏi lo gửi nhầm cho người dùng thật.
import osEMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"EMAIL_HOST = "smtp.gmail.com"EMAIL_PORT = 587EMAIL_USE_TLS = TrueEMAIL_HOST_USER = os.environ["EMAIL_HOST_USER"]EMAIL_HOST_PASSWORD = os.environ["EMAIL_HOST_PASSWORD"]DEFAULT_FROM_EMAIL = "MyShop <noreply@example.com>"# Dev local: in mail ra terminal thay vi gui that# EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
Sau đó, ở bất cứ đâu trong code — view, signal hay management command — chỉ cần một lời gọi send_mail() với 4 tham số: tiêu đề, nội dung text, địa chỉ người gửi và danh sách người nhận. Hàm trả về số mail gửi thành công, thường là 1; nếu SMTP lỗi và fail_silently=False (mặc định), Django ném exception để bạn xử lý thay vì im lặng nuốt lỗi. Cần bản HTML đẹp thì truyền thêm html_message, Django tự đính kèm bản text làm phương án dự phòng cho client mail cũ.
from django.core.mail import send_mailsent = send_mail( "Don hang #123 da duoc xac nhan", "Don hang #123 cua ban da duoc xac nhan.", "noreply@example.com", ["khach@example.com"], html_message="<h1>Don hang #123</h1><p>Cam on ban da mua hang.</p>",)print(sent) # 1
Áp dụng vào dự án của bạn
Gắn send_mail() vào đúng điểm chạm: mail chào mừng sau khi user đăng ký (kết hợp signal post_save), mail xác nhận sau khi đơn hàng được tạo, mail đặt lại mật khẩu. Vì gửi qua SMTP thường mất 1–3 giây, đừng gọi trực tiếp trong view nếu không muốn người dùng chờ — đẩy sang background task hoặc chạy sau khi transaction commit thành công, và luôn log lại khi gửi thất bại để còn gửi bù.
Bài viết liên quan
Ba bài dưới đây cùng chủ đề gửi mail và xử lý việc nặng:
- Flask: gửi email bằng Flask-Mail — đừng để SMTP chặn request trong 5 phút
- FastAPI: xử lý việc nặng ở background với BackgroundTasks, client không phải chờ
- Django: chạy tác vụ sau khi commit thành công bằng transaction.on_commit trong 5 phút
Câu hỏi thường gặp
Gửi mail bằng Gmail được không?
Được. Dùng host smtp.gmail.com, cổng 587, bật TLS, và dùng App Password thay cho mật khẩu chính — Gmail đã chặn đăng nhập bằng mật khẩu thường từ 2022. Nhớ bật xác thực hai bước trước khi tạo App Password.
Làm sao test gửi mail mà không gửi thật?
Đặt EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' trong settings dev — mail sẽ in ra terminal thay vì rời khỏi máy. Muốn assert trong test thì dùng locmem backend, rồi kiểm tra django.core.mail.outbox.
send_mail() gửi được cho nhiều người cùng lúc không?
Được — truyền danh sách nhiều địa chỉ vào recipient_list, tất cả cùng nhận một mail. Nếu cần gửi hàng loạt mỗi người một nội dung khác nhau, dùng send_mass_mail() để mở một kết nối SMTP duy nhất thay vì mở lại cho từng mail.
Mail gửi đi bị rơi vào spam thì làm sao?
Kiểm tra ba thứ: địa chỉ người gửi phải thuộc domain bạn sở hữu, cấu hình SPF/DKIM/DMARC cho domain đó, và nội dung tránh từ ngữ giống spam. Ngoài ra, gửi từ tài khoản SMTP uy tín thấp cũng dễ bị lọc — cân nhắc dùng dịch vụ gửi mail chuyên dụng khi lượng mail lớn.
💬 Bạn đã thử cách này trong dự án của mình chưa? Nếu gặp lỗi hoặc có cách làm gọn hơn, để lại bình luận bên dưới — mình đọc hết và sẽ bổ sung những ý hay vào bài.
Nguồn tham khảo
- Django documentation — Sending email: https://docs.djangoproject.com/en/stable/topics/email/
- Django documentation — django.core.mail: https://docs.djangoproject.com/en/stable/topics/email/#send-mail
