1. Home
  2. Sms Campaign Project
  3. Project Setup
  4. 03. Invitation Sent Add Member

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()

৩️⃣ উদাহরণের ব্যাখ্যা

  1. subject → ইমেইলের শিরোনাম
  2. text_content → সাধারণ টেক্সট ইমেইল
  3. html_content → HTML ইমেইল, যা ব্রাউজারে সুন্দর দেখাবে
  4. attach_alternative() → টেক্সট + HTML একসাথে পাঠানোর জন্য
  5. 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

  1. নতুন ইউজার স্বাগতম ইমেইল
  2. পাসওয়ার্ড রিসেট ইমেইল
  3. রিপোর্ট বা ইনভয়েস পাঠানো
  4. নিউজলেটার / Marketing ইমেইল

💡 Tips:

  • DEFAULT_FROM_EMAIL settings.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 Modelcampaign_app/models.pyনতুন model যোগ করব
2. Email Functioncampaign_app/sendemail.pyinvitation email পাঠানোর ফাংশন
3. Email Templatetemplates/emails/invitation.htmlসুন্দর HTML email
4. Serializerscampaign_app/api/serializers.py২টা serializer
5. Viewscampaign_app/api/views.py২টা view (invite + accept)
6. URLscampaign_app/api/urls.py২টা endpoint যোগ করব
7. settings.pycampaign_project/settings.pyTEMPLATES 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()),
]

How can we help?