فصل ۱۴: Messages Framework — نمایش پیام‌ها به کاربر

""

۱. مقدمه — چرا باید پیام‌ها را جدی بگیریم؟

تقریباً توی هر سایت یا اپلیکیشن، یک بخش خیلی مهم تعامل با کاربر، اعلام نتیجه عملیات هست.
کاربر نمی‌تونه ذهن توسعه‌دهنده رو بخونه 😅؛ پس باید بفهمه که:

  • محصولش ثبت شد؟
  • خطایی توی فرم ثبت‌نام بود؟
  • خریدش موفق بود یا ناموفق؟
  • عملیات حذف رو تایید کرد یا لغو؟

همه این‌ها یعنی باید به کاربر Feedback بدهیم.

سناریوی فروشگاه آنلاین:

تو فروشگاه آنلاین ما، کاربر گاهی:

  • محصول اضافه می‌کنه
  • محصول حذف می‌کنه
  • خرید انجام میده
  • آدرس پستیش رو تغییر میده

در همه این مراحل ما باید بهش پیامی متنی بدیم:

  • سبز → موفقیت: «محصول با موفقیت اضافه شد.»
  • قرمز → خطا: «خطا در حذف محصول.»
  • زرد → هشدار: «آیا مطمئن هستید که می‌خواهید محصول را حذف کنید؟»
  • آبی → اطلاع‌رسانی: «سایت شنبه‌ها بین 1 تا 3 بامداد بروزرسانی می‌شود.»

۲. Messages Framework در Django چیست؟

Django یک سیستم داخلی به اسم Messages Framework داره که کمک می‌کنه پیام‌ها رو به صورت موقت نگه داریم و تو صفحه بعدی نمایش بدیم.

📌 نکته مهم:

  • پیام‌ها فقط برای یک درخواست بعدی نگه داشته می‌شوند (مثلاً بعد از انجام عملیات و انتقال به صفحه دیگر).
  • بر اساس Session یا Cookies پیاده‌سازی شده.
  • نیازی به نوشتن کد دیتابیس برای ذخیره پیام‌های موقت نداریم.

۳. بررسی فعال بودن Messages Framework

این سیستم به صورت پیش‌فرض فعال است، ولی باید مطمئن باشیم:

۳.۱. INSTALLED_APPS

INSTALLED_APPS = [
    # ...
    'django.contrib.messages',
]

۳.۲. MIDDLEWARE

MIDDLEWARE = [
    # ...
    'django.contrib.sessions.middleware.SessionMiddleware',  # لازم برای Session-based Messages
    'django.contrib.messages.middleware.MessageMiddleware',  # مدیریت پیام‌ها
]

۳.3. Context Processor

در بخش TEMPLATEScontext_processors:

'context_processors': [
    # ...
    'django.contrib.messages.context_processors.messages',
]

📌 این باعث میشه داخل قالب‌ها (templates) همیشه متغیر messages در دسترس باشه.


۴. انواع پیام‌ها در Django

Django دسته‌بندی مشخصی برای پیام‌ها داره:

روش استفاده در Viewسطح پیام (Level)رنگ پیشنهادی در UI
messages.debugDEBUGخاکستری (برای توسعه)
messages.infoINFOآبی (اطلاع‌رسانی)
messages.successSUCCESSسبز (موفقیت)
messages.warningWARNINGزرد (هشدار)
messages.errorERRORقرمز (خطا)

۵. اولین مثال — پیام بعد از ثبت محصول

فرض کن فرم افزودن محصول داریم که بعد از ذخیره شدن موفقیت‌آمیز، پیام موفقیت به کاربر نشون بده.

۵.۱. views.py

from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import ProductForm

def add_product(request):
    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES)  # داده‌ها و فایل‌ها
        if form.is_valid():
            form.save()
            # پیام موفقیت
            messages.success(request, "✅ محصول با موفقیت اضافه شد.")
            return redirect('product_list')
        else:
            # پیام خطا
            messages.error(request, "❌ خطا در ثبت محصول. لطفاً موارد گفته شده را بررسی کنید.")
    else:
        form = ProductForm()
    return render(request, 'add_product.html', {'form': form})

۶. نمایش پیام‌ها در قالب HTML

برای نمایش پیام‌ها باید تو قالبمون حلقه for بزنیم روی متغیر messages.

📌 معمولاً این کد رو توی base.html قرار میدیم تا همه صفحات پیام‌ها رو نمایش بدن.

۶.۱. base.html

{% if messages %}
<div class="container mt-3">
    {% for message in messages %}
        <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
            {{ message }}
            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
        </div>
    {% endfor %}
</div>
{% endif %}

۶.۲. توضیح:

  • message.tags → اتوماتیک به success، error، warning و غیره تغییر می‌کنه و برای Bootstrap عالیه.
  • دکمه btn-close در Bootstrap کاربر رو قادر می‌کنه پیام رو ببنده.
  • کلاس fade show انیمیشن محو شدن پیام رو اضافه می‌کنه.

۷. ترکیب پیام‌ها با عمل حذف محصول

وقتی محصولی رو حذف می‌کنیم، کاربر باید متوجه بشه:

from django.shortcuts import get_object_or_404
from django.contrib import messages
from .models import Product

def delete_product(request, pk):
    product = get_object_or_404(Product, pk=pk)
    product.delete()
    messages.success(request, f"🗑 محصول «{product.name}» با موفقیت حذف شد.")
    return redirect('product_list')

۸. نکته مهم: Redirect بعد از POST (الگوی PRG)

در Django وقتی از فرم‌ها استفاده می‌کنیم و پیام ارسال می‌کنیم، باید از الگوی Post/Redirect/Get استفاده کنیم:

  1. کاربر فرم رو ارسال می‌کنه (POST)
  2. عملیات انجام میشه و پیام اضافه میشه
  3. redirect() کاربر رو به صفحه لیست یا جزئیات میفرسته
  4. پیام نمایش داده میشه

📌 این باعث میشه با F5 (رفرش)، عمل دوباره انجام نشه.


۹. سطح‌بندی پیشرفته پیام‌ها

هر پیام یک سطح (Level) عددی داره که Django بر اساسش تصمیم می‌گیره:

  • DEBUG → 10
  • INFO → 20
  • SUCCESS → 25
  • WARNING → 30
  • ERROR → 40

می‌تونی بگی مثلاً پیام‌های از سطح WARNING به پایین اصلاً ذخیره نشه:

from django.contrib import messages
messages.set_level(request, messages.WARNING)  # فقط Warning و Error و...

۱۰. استفاده از Tags سفارشی

فرض کن توی Bootstrap کلاس‌های خاص خودتو داری. می‌تونی پیام‌ها رو Map کنی:

from django.contrib.messages import constants as message_constants

MESSAGE_TAGS = {
    message_constants.DEBUG: 'secondary',
    message_constants.INFO: 'info',
    message_constants.SUCCESS: 'success',
    message_constants.WARNING: 'warning',
    message_constants.ERROR: 'danger',
}

📌 اینو تو settings.py بذار تا هر پیام‌ سطح مشخصی بگیره.


۱۱. ترکیب پیام با شرایط خاص (مثال خرید)

def checkout(request):
    cart = request.session.get('cart', {})
    if not cart:
        messages.warning(request, "سبد خرید شما خالی است.")
        return redirect('cart_view')

    if process_payment(cart):
        messages.success(request, "پرداخت با موفقیت انجام شد. سفارش شما ثبت گردید.")
        request.session['cart'] = {}
    else:
        messages.error(request, "مشکلی در پرداخت پیش آمد.")
    return redirect('order_history')

۱۲. چند پیام در یک درخواست

مشکلی نداره چند پیام پشت سر هم اضافه بشه:

messages.info(request, "سایت فردا صبح به‌روزرسانی می‌شود.")
messages.success(request, "محصول ثبت شد.")
messages.warning(request, "موجودی انبار رو بررسی کنید.")

توی قالب همه رو می‌بینی.


۱۳. پیام‌ها با طول عمر Session

پیش‌فرض، پیام‌ها یکبار نمایش داده میشن، ولی می‌تونی از storage backendهای دیگر استفاده کنی، مثلاً Session Storage یا Cookie Storage.

در settings.py:

MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'

۱۴. خطاهای رایج

مشکلدلیلراه‌حل
پیام نمایش داده نمی‌شودحلقه نمایش در قالب وجود ندارد{% for message in messages %} اضافه کن
پیام بدون استایلBootstrap یا CSS وصل نشدهلینک CSS رو درست اضافه کن
پیام بعد از رفرش نیسترفتار طبیعی Frameworkاگر موندگاری بخوای، باید Custom Storage بسازی

۱۵. نکات بهترین عملکرد (Best Practices)

  1. همیشه بعد از عملیات POST ریدایرکت کن → هم از ارسال دوباره جلوگیری می‌کنه، هم پیام درست نشون داده میشه.
  2. پیام‌ها در base.html باشن، که توی همه صفحات دیده بشه.
  3. سطوح پیام رو درست استفاده کن (از error برای همه چیز استفاده نکن!)
  4. ترکیب با AJAX → اگه فرم‌ها رو با AJAX ارسال می‌کنی، پیام‌ها رو تو JSON پاسخ بده.
  5. اموجی یا آیکن اضافه کن → کاربر سریع‌تر منظور رو می‌فهمه.

۱۶. مثال کامل CRUD با پیام‌ها در فروشگاه آنلاین

views.py

# لیست محصولات
class ProductListView(ListView):
    model = Product
    template_name = 'product_list.html'
    context_object_name = 'products'
    paginate_by = 6

# جزئیات
class ProductDetailView(DetailView):
    model = Product
    template_name = 'product_detail.html'

# افزودن محصول
class ProductCreateView(CreateView):
    model = Product
    fields = ['name', 'price', 'description', 'image']
    template_name = 'product_form.html'
    success_url = reverse_lazy('product_list')

    def form_valid(self, form):
        messages.success(self.request, "محصول با موفقیت اضافه شد.")
        return super().form_valid(form)

# ویرایش
class ProductUpdateView(UpdateView):
    model = Product
    fields = ['name', 'price', 'description', 'image']
    template_name = 'product_form.html'
    success_url = reverse_lazy('product_list')

    def form_valid(self, form):
        messages.info(self.request, "محصول با موفقیت ویرایش شد.")
        return super().form_valid(form)

# حذف
class ProductDeleteView(DeleteView):
    model = Product
    template_name = 'product_confirm_delete.html'
    success_url = reverse_lazy('product_list')

    def delete(self, request, *args, **kwargs):
        messages.warning(request, "محصول حذف شد.")
        return super().delete(request, *args, **kwargs)

۱۷. نتیجه‌گیری

الان تو سناریوی فروشگاه آنلاین ما:

  • هر عملیاتی (افزودن، حذف، خرید) نتیجه‌ش به کاربر نمایش داده میشه.
  • پیام‌ها استایل Bootstrap دارن.
  • از تکرار درخواست جلوگیری میشه.
  • کاربر همیشه می‌دونه چی اتفاق افتاده.
محمد وب‌سایت

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *