وبلاگ فربد | Farbod Blog

توی این وبلاگ راجع به پروژه هام صحبت می کنم

ساخت پلتفرم mail با جنگو - قسمت 1

  • ۶۳۰

سلام دوستان‌:)

 

توی پست امروز قراره با هم یک پلتفرم mail با استفاده از API با django بسازیم!

خب این پروژه رو توی دو قسمت براتون میذارم.

قسمت اول مربوط به طراحی بخش back-end میشه و بخش دوم مربوط به طراحی front-end.

خب اول از همه بدونیم که django چیه؟

جنگو یک فریم ورک تحت وب آزاد و متن باز هستش که به زبان پایتون طراحی شده و از معماری Model-View-Template استفاده میکنه.

 

حالا بریم سراغ نصب جنگو

 

برای نصب این کتابخونه توی ویندوز برین توی cmd و دستور زیر رو وارد کنین:

pip install django

برای نصب این کتابخونه توی لینوکس هم برین توی ترمینال و دستور زیر رو وارد کنین:

sudo pip3 install django

 

خب حالا که این کتابخونه رو نصب کردیم باید یکم با ساختار اون آشنا بشیم.

خب سیستم جنگو به این صورت هستش که ما یک project اصلی داریم که داخل اون یک سری app ها تعریف شده که هر کدوم مسئولیت یک کار رو به عهده میگیرن.

مثلا گوگل رو فرض کنید.

گوگل کلش یک project هست و هر کدوم از بخش های اونم مثل search, images,  youtube و.. app های اون هستن.

 

حالا که با ساختارش آشنا شدیم میتونیم پروژه خودمون رو شروع کنیم.

 

برای شروع یک پروژه میتونین در هر سیستم عاملی از دستور زیر استفاده کنین:

django-admin startproject project3

بجای project3 میتونین هر اسمی که دوست دارین بذارین ولی اینجا بهتره که همون project3 باشه.

خب حالا میتونین توی cmd یا terminal با استفاده از دستور زیر وارد پوشه ای پروژه داخلش درست شده بشین:

cd project3

خب حالا اگر در ویندوز دستور dir و در لینوکس ls رو وارد کنین، چندین فایل و فولدر میبینین.

یکی از اون فایل ها manage.py هست.

ما میتونیم با استفاده از این فایل دستور های زیادی رو اجرا کنیم که یکی از اونها startapp هستش که برای ما یک app جدید درست می کنه.

پس در cmd یا terminal دستور زیر رو وارد کنین:

python manage.py startapp mail

خب بجای project3 میتونین اسم app خودتون رو بذارین ولی اینجا همون projcet3 باشه بهتره.

 

خب حالا با استفاده از دستور cd وارد پوشه project3 بشین حالا در ویندوز dir و یا در لینوکس ls رو تایپ کنین.

حالا چندین فایل و پوشه خواهید دید.

فایل views.py مربوط به view هامون میشه. فایل models.py مربوط به model های دیتابیسمون میشه و فایل urls.py هم که بعدا خودمون میسازیمش مربوط به تنظیمات url ما خواهد بود. بقیه فایل ها هم اهمیت ندارن.

خب حالا با یک IDE (بهتون توصیه میکنم از visual studio code استفاده کنین) پوشه project خودتون رو باز کنین.

حالا فایل urls.py (در پوشه project3) باز کنین و مقادیر زیر رو داخلش قرار بدین:

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('mail.urls'))
]

اگر دقت کنین یک لیست به نام urlpatterns می بینین. مقدار اول مربوط به پنل ادمین django میشه که بعدا اونو توضیح میدم.

توی مقدار دوم یک path تعریف کردیم که توش گفتیم اگه توی url مقدار خالی وارد شد حالا بیا مقادیر فایل urls.py داخل پوشه mail رو در نظر بگیر.

 

حالا باید توی پوشه mail یک فایل به نام urls.py بسازین. بعد از اینکه فایل رو ساختین فایل رو باز کنین و مقادیر زیر رو داخلش قرار بدین:

from django.urls import path

from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path("login", views.login_view, name="login"),
    path("logout", views.logout_view, name="logout"),
    path("register", views.register, name="register"),

    # API Routes
    path("emails", views.compose, name="compose"),
    path("emails/<int:email_id>", views.email, name="email"),
    path("emails/<str:mailbox>", views.mailbox, name="mailbox"),
]

خب دوباره اگر دقت کنین 7 تا path داخل لیست urlpatterns می بینین. اولین path که مربوط به صفحه اصلی سایت میشه. سه تا path بعدی هم مربوط به ورود، ثبت نام و خروج کاربر میشن.

خب همونطور که گفتم ما قراره سایتمون رو بر اساس API طراحی کنیم حالا این یعنی چی؟

این یعنی اینکه ما یک سرور داریم که وقتی به یک آدرس مشخص داخل اون سرور یک request از نوع HTTP داده میشه سرور هم یک جوابی میده و ما میتونیم با استفاده از این نتایجی که سرور به ما داده کار هایی انجام بدیم. که این کار هم بعدا قراره با javascript انجام بدیم.

پس سه تا path بعدی مربوط به API میشن.

خب همونطور که میبینین توی ورودی دوم هر path یکی از توابع داخل فایل views.py رو اجرا میکنیم حالا باید بریم اونارو تعریف کنیم.

 

خب فایل views.py رو باز کنین و مقادیر زیر رو داخلش قرار بدین:

import json
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.db import IntegrityError
from django.http import JsonResponse
from django.shortcuts import HttpResponse, HttpResponseRedirect, render
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt

from .models import User, Email


def index(request):

    # Authenticated users view their inbox
    if request.user.is_authenticated:
        return render(request, "mail/inbox.html")

    # Everyone else is prompted to sign in
    else:
        return HttpResponseRedirect(reverse("login"))


@csrf_exempt
@login_required
def compose(request):

    # Composing a new email must be via POST
    if request.method != "POST":
        return JsonResponse({"error": "POST request required."}, status=400)

    # Check recipient emails
    data = json.loads(request.body)
    emails = [email.strip() for email in data.get("recipients").split(",")]
    if emails == [""]:
        return JsonResponse({
            "error": "At least one recipient required."
        }, status=400)

    # Convert email addresses to users
    recipients = []
    for email in emails:
        try:
            user = User.objects.get(email=email)
            recipients.append(user)
        except User.DoesNotExist:
            return JsonResponse({
                "error": f"User with email {email} does not exist.",
                "status": 400
            }, status=400)

    # Get contents of email
    subject = data.get("subject", "")
    body = data.get("body", "")

    # Create one email for each recipient, plus sender
    users = set()
    users.add(request.user)
    users.update(recipients)
    for user in users:
        email = Email(
            user=user,
            sender=request.user,
            subject=subject,
            body=body,
            read=user == request.user
        )
        email.save()
        for recipient in recipients:
            email.recipients.add(recipient)
        email.save()

    return JsonResponse({"message": "Email sent successfully.", "status": 201}, status=201)


@login_required
def mailbox(request, mailbox):

    # Filter emails returned based on mailbox
    if mailbox == "inbox":
        emails = Email.objects.filter(
            user=request.user, recipients=request.user, archived=False
        )
    elif mailbox == "sent":
        emails = Email.objects.filter(
            user=request.user, sender=request.user
        )
    elif mailbox == "archive":
        emails = Email.objects.filter(
            user=request.user, recipients=request.user, archived=True
        )
    else:
        return JsonResponse({"error": "Invalid mailbox."}, status=400)

    # Return emails in reverse chronologial order
    emails = emails.order_by("-timestamp").all()
    return JsonResponse([email.serialize() for email in emails], safe=False)


@csrf_exempt
@login_required
def email(request, email_id):

    # Query for requested email
    try:
        email = Email.objects.get(user=request.user, pk=email_id)
    except Email.DoesNotExist:
        return JsonResponse({"error": "Email not found."}, status=404)

    # Return email contents
    if request.method == "GET":
        return JsonResponse(email.serialize())

    # Update whether email is read or should be archived
    elif request.method == "PUT":
        data = json.loads(request.body)
        if data.get("read") is not None:
            email.read = data["read"]
        if data.get("archived") is not None:
            email.archived = data["archived"]
        email.save()
        return HttpResponse(status=204)

    # Email must be via GET or PUT
    else:
        return JsonResponse({
            "error": "GET or PUT request required."
        }, status=400)


def login_view(request):
    if request.method == "POST":

        # Attempt to sign user in
        email = request.POST["email"]
        password = request.POST["password"]
        user = authenticate(request, username=email, password=password)

        # Check if authentication successful
        if user is not None:
            login(request, user)
            return HttpResponseRedirect(reverse("index"))
        else:
            return render(request, "mail/login.html", {
                "message": "Invalid email and/or password."
            })
    else:
        return render(request, "mail/login.html")


def logout_view(request):
    logout(request)
    return HttpResponseRedirect(reverse("index"))


def register(request):
    if request.method == "POST":
        email = request.POST["email"]

        # Ensure password matches confirmation
        password = request.POST["password"]
        confirmation = request.POST["confirmation"]
        if password != confirmation:
            return render(request, "mail/register.html", {
                "message": "Passwords must match."
            })

        # Attempt to create new user
        try:
            user = User.objects.create_user(email, email, password)
            user.save()
        except IntegrityError as e:
            print(e)
            return render(request, "mail/register.html", {
                "message": "Email address already taken."
            })
        login(request, user)
        return HttpResponseRedirect(reverse("index"))
    else:
        return render(request, "mail/register.html")

خب اول توابع مورد نیاز رو import کردیم بعد از اون تابع index رو تعریف کردیم که میگه اگر user وارد شده بود فایل inbox.html رو برگردونه. اگر هم وارد نشده بود صفحه لاگین رو بیاره.

حالا تابع compose رو برای فرستادن ایمیل تعریف کردیم و داخلش گفتیم ایمیلی که فرستاده شده، کسی که اون ایمیل رو فرستاده و کسی که اون ایمیل رو دریافت میکنه داخل دیتابیس ذخیره کنه. بعد هم متن Email Sent Successfully رو با فرمت json بر میگردونه.

بعد تابع mailbox رو تعریف کردیم که داخلش گفتیم mailbox مورد نظر (مثل inbox, sent, archived) رو load کنه و مقادیر اون رو بصورت json برگردونه.

حالا تابع email رو تعریف کردیم که هر mail رو بر اساس primary key اون mail بصورت json بر میگردونه.

سه تا تابع بعدی هم مربوط به login, register و logout کاربر میشن.

 

خب حالا بریم سراغ فایل models.py.

 

فایل models.py رو باز کنین و مقادیر زیر رو داخلش قرار بدین:

from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    pass


class Email(models.Model):
    user = models.ForeignKey(
        "User", on_delete=models.CASCADE, related_name="emails")
    sender = models.ForeignKey(
        "User", on_delete=models.PROTECT, related_name="emails_sent")
    recipients = models.ManyToManyField("User", related_name="emails_received")
    subject = models.CharField(max_length=255)
    body = models.TextField(blank=True)
    timestamp = models.DateTimeField(auto_now_add=True)
    read = models.BooleanField(default=False)
    archived = models.BooleanField(default=False)

    def serialize(self):
        return {
            "id": self.id,
            "sender": self.sender.email,
            "recipients": [user.email for user in self.recipients.all()],
            "subject": self.subject,
            "body": self.body,
            "timestamp": self.timestamp.strftime("%b %d %Y, %I:%M %p"),
            "read": self.read,
            "archived": self.archived
        }

    def __str__(self):
        return f"From: {self.sender}, Sub: {self.subject}"

خب توی فایل models.py اول کلاس Email رو تعریف کردیم و داخلش چند تا فیلد تعریف کردیم.

فیلد sender مربوط به کسی که ایمیل رو فرستاده میشه هست، فیلد recipients مربوط به کسی ایمیل رو دریافت هست.

فیلد subject مربوط به subject اون ایمیل میشه.

فیلد body هم مربوط به body اون ایمیل میشه.

فیلد timestamp هم مربوط زمانی که اون ایمیل فرستاده شده هست.

فیلد read هم مربوط به این هستش آیا اون ایمیل خونده شده یا نه؟

فیلد archived هم مربوط به این هستش که آیا کاربر اون ایمیل رو آرشیو کرده یا نه.

 

تابع serialize هم برای برای اینه که تمام فیلد های اون دیتابیس رو بصورت مرتب شده به ما برگردونه.

 

خب حالا ما که این همه مقادیر ایمیل ها، mailbox ها و... رو برگردوندیم باید باهاشون چی کار کنیم؟؟؟

 

توی بخش بعدی منتظرش باشین :)))

 

امیدوارم براتون مفید بوده باشه D:


  • سلام
    تمام آموزشهاتون عالی و فوق العاده هستن
    فوق العاده تر و ارزشمند تر این هستش که شما دانش خودتون بدون هیچگونه چشم داشت و توقعی در اختیار دیگران قرار میدین
    دانش و اطلاعاتی که قطعا خودتون برای بدست آوردنش وقت و هزینه زیادی صرف کردین
    این کار شما جای هزاران هزار بار تقدیر و تشکر داره
    خداوند پشت و پناهتون
    پاسخ:
    سلام دوست عزیز
    خیلی از ابراز لطف و محبت شما ممنونم
    همراهی شما باعث افتخاره
    موفق باشید.
  • مرسی ار آموزش عالیتون
    پاسخ:
    سلام دوست عزیز
    ممنون از توجه و همراهی شما
    موفق باشید
  • سلام
    خیلی متفاوت و حرفه ای بود
    بیصبرانه منتظر قسمت ۲ هستم
    پاسخ:
    سلام دوست عزیز
    مننون از همراهی شما
    موفق باشید
  • چقدر خفنننننننننن
    عااااالی عااااالی عااااالی
    پاسخ:
    سلام دوست عزیز
    ممنون از محبت و توجه شما
    موفق باشید
ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی