EmailMultiAlternatives
03. Invitation Sent Add Member
অবশ্যই! চলুন Django-এর EmailMultiAlternatives ফিচারটা সহজ, গল্প, উদাহরণ এবং কোড দিয়ে বুঝি।
১️⃣ গল্প দিয়ে শুরু করি
ধরা যাক তুমি একটা ছোট কোম্পানিতে কাজ করো। তোমার কোম্পানিতে নতুন ইউজাররা রেজিস্ট্রেশন করলে স্বাগত ইমেইল পাঠাতে হবে।
- সাধারণ ইমেইল পাঠালে শুধু টেক্সট দেখাবে।
- কিন্তু তুমি চাও ইমেইলের ভিতরে ছবি, লিঙ্ক, ফরম্যাট করা টেক্সট থাকুক।
এখানেই আসে EmailMultiAlternatives।
- এটা মাল্টিপার্ট ইমেইল পাঠাতে সাহায্য করে।
- তুমি টেক্সট + HTML দুইটাই পাঠাতে পারো।
- চাইলে এটাতে ফাইল এটাচমেন্ট ও যুক্ত করতে পারো।
২️⃣ সাধারণ উদাহরণ
from django.core.mail import EmailMultiAlternatives
from django.conf import settings
# ১. বিষয়, মেসেজ, ফ্রম, টু
subject = 'Welcome to Our Site'
text_content = 'Hello! Thanks for joining our site.'
html_content = '<h1>Hello!</h1><p>Thanks for joining our <a href="https://example.com">site</a>.</p>'
from_email = settings.DEFAULT_FROM_EMAIL
to = ['user@example.com']
# ২. ইমেইল অবজেক্ট তৈরি
msg = EmailMultiAlternatives(subject, text_content, from_email, to)
# ৩. HTML যুক্ত করা
msg.attach_alternative(html_content, "text/html")
# ৪. পাঠানো
msg.send()
৩️⃣ উদাহরণের ব্যাখ্যা
subject→ ইমেইলের শিরোনামtext_content→ সাধারণ টেক্সট ইমেইলhtml_content→ HTML ইমেইল, যা ব্রাউজারে সুন্দর দেখাবেattach_alternative()→ টেক্সট + HTML একসাথে পাঠানোর জন্যsend()→ ইমেইল পাঠানো
৪️⃣ Practical: ফাইল এটাচমেন্ট সহ
from django.core.mail import EmailMultiAlternatives
from django.conf import settings
subject = 'Report Attached'
text_content = 'Please find the report attached.'
from_email = settings.DEFAULT_FROM_EMAIL
to = ['user@example.com']
msg = EmailMultiAlternatives(subject, text_content, from_email, to)
msg.attach_alternative('<h1>Report Attached</h1><p>Check the attached PDF.</p>', "text/html")
# এটাচমেন্ট
with open('report.pdf', 'rb') as f:
msg.attach('report.pdf', f.read(), 'application/pdf')
msg.send()
✅ এখানে:
- PDF ফাইল এটাচমেন্ট করা হলো
- HTML এবং টেক্সট দুইটাই পাঠানো হলো
৫️⃣ Real-life use case
- নতুন ইউজার স্বাগতম ইমেইল
- পাসওয়ার্ড রিসেট ইমেইল
- রিপোর্ট বা ইনভয়েস পাঠানো
- নিউজলেটার / Marketing ইমেইল
💡 Tips:
DEFAULT_FROM_EMAILsettings.py-তে define করতে হবে:
DEFAULT_FROM_EMAIL = 'noreply@example.com'
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your-email@gmail.com'
EMAIL_HOST_PASSWORD = 'your-password'
- dev বা testing এ,
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'use করলে ইমেইল কনসোলে দেখাবে, পাঠাবে না।
🎉 অভিনন্দন ভাই! Phase 1 + Phase 2 শেষ – এখন পুরো Invitation
🗂️ Invitation System এর জন্য কী কী নতুন যোগ করতে হবে
| জিনিস | ফাইল / লোকেশন | কী করব |
|---|---|---|
| 1. Invitation Model | campaign_app/models.py | নতুন model যোগ করব |
| 2. Email Function | campaign_app/sendemail.py | invitation email পাঠানোর ফাংশন |
| 3. Email Template | templates/emails/invitation.html | সুন্দর HTML email |
| 4. Serializers | campaign_app/api/serializers.py | ২টা serializer |
| 5. Views | campaign_app/api/views.py | ২টা view (invite + accept) |
| 6. URLs | campaign_app/api/urls.py | ২টা endpoint যোগ করব |
| 7. settings.py | campaign_project/settings.py | TEMPLATES folder path (যদি না থাকে) |
১. Invitation Model যোগ করো (models.py তে)
import uuid
from django.db import models
class Invitation(models.Model):
# যে invite করছে (John – তোমার existing User model)
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='sent_invitations' # John.sent_invitations.all() দিয়ে দেখতে পারবে
)
# যাকে invite করা হচ্ছে
email = models.EmailField(max_length=255)
# unique link এর জন্য token
token = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
# accept হয়েছে কিনা
is_accepted = models.BooleanField(default=False)
# কবে পাঠানো হয়েছে
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ('user', 'email') # একই person কে দুইবার invite না যায়
def __str__(self):
return f"{self.user.business_owner_name} → {self.email}"Migration করো:
python manage.py makemigrations python manage.py migrate
২. Email পাঠানোর ফাংশন (sendemail.py – নতুন ফাইল বা existing এ যোগ করো)
import threading
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.conf import settings
import os
FRONTEND_URL = os.getenv("FRONTENDURL", "http://localhost:3000") # তোমার frontend
def send_invitation_email(invitation):
"""
Sarah কে invitation email পাঠায়
"""
subject = f"You've been invited to join {invitation.user.business_name}"
accept_link = f"{FRONTEND_URL}/accept-invitation/{invitation.token}"
html_content = render_to_string("emails/invitation.html", {
"business_owner_name": invitation.user.business_owner_name,
"business_name": invitation.user.business_name,
"accept_link": accept_link,
})
email = EmailMultiAlternatives(
subject=subject,
body="Click the link to join the team.",
from_email=settings.EMAIL_HOST_USER,
to=[invitation.email]
)
email.attach_alternative(html_content, "text/html")
email.send()৩. Email Template (templates/emails/invitation.html – ফোল্ডার বানিয়ে ফাইল রাখো)
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial; background: #f6f6f6; padding: 20px; }
.card { max-width: 600px; margin: auto; background: white; padding: 40px; border-radius: 12px; }
.btn { background: #007bff; color: white; padding: 16px 32px; text-decoration: none; border-radius: 8px; font-size: 18px; display: inline-block; }
</style>
</head>
<body>
<div class="card">
<h2>You're Invited to Join the Team! 🎉</h2>
<p>Hello,</p>
<p><strong>{{ business_owner_name }}</strong> has invited you to join <strong>{{ business_name }}</strong> as a team member.</p>
<p>Click below to create your account and join:</p>
<br>
<a href="{{ accept_link }}" class="btn">Accept Invitation & Create Account</a>
<br><br>
<p>This link expires in 7 days.</p>
<p>Thanks!<br>{{ business_name }} Team</p>
</div>
</body>
</html>settings.py তে TEMPLATES যদি না থাকে যোগ করো:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], # ... }, ]
৪. Serializers (api/serializers.py তে যোগ করো)
from rest_framework import serializers
class InviteMemberSerializer(serializers.Serializer):
email = serializers.EmailField()
class AcceptInvitationSerializer(serializers.Serializer):
password = serializers.CharField(write_only=True, min_length=6)
confirm_password = serializers.CharField(write_only=True)
def validate(self, data):
if data['password'] != data['confirm_password']:
raise serializers.ValidationError("Passwords do not match")
return data৫. Views (api/views.py তে যোগ করো)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework import status
from django.shortcuts import get_object_or_404
from rest_framework_simplejwt.tokens import RefreshToken
from ..models import User, Invitation
from ..sendemail import send_invitation_email
import threading
def get_tokens_for_user(user):
refresh = RefreshToken.for_user(user)
return {
"access": str(refresh.access_token),
"refresh": str(refresh),
}
class InviteMemberView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = InviteMemberSerializer(data=request.data)
if serializer.is_valid():
email = serializer.validated_data['email'].lower()
if Invitation.objects.filter(user=request.user, email=email, is_accepted=False).exists():
return Response({"message": "Already invited"}, status=400)
invitation = Invitation.objects.create(user=request.user, email=email)
threading.Thread(target=send_invitation_email, args=(invitation,)).start()
return Response({"success": True, "message": "Invitation sent!"}, status=201)
return Response(serializer.errors, status=400)
class AcceptInvitationView(APIView):
permission_classes = [AllowAny]
def post(self, request, token):
invitation = get_object_or_404(Invitation, token=token, is_accepted=False)
serializer = AcceptInvitationSerializer(data=request.data)
if serializer.is_valid():
new_user = User.objects.create_user(
email=invitation.email,
password=serializer.validated_data['password'],
business_owner_name=invitation.user.business_owner_name,
business_name=invitation.user.business_name,
industry_type=invitation.user.industry_type or "",
mobile_number=invitation.user.mobile_number or "",
address=invitation.user.address or "",
)
invitation.is_accepted = True
invitation.save()
tokens = get_tokens_for_user(new_user)
return Response({
"success": True,
"message": "Account created! Welcome to the team.",
"token": tokens
}, status=201)
return Response(serializer.errors, status=400)৬. URLs (api/urls.py তে যোগ করো)
from django.urls import path
from .views import InviteMemberView, AcceptInvitationView
urlpatterns += [
path('member/invite/', InviteMemberView.as_view()),
path('member/accept-invitation/<str:token>/', AcceptInvitationView.as_view()),
]