🧩 الدرس الثامن: نظام تسجيل النشاطات (Activity Logs) داخل لوحة التحكم

نظام تسجيل النشاطات (Activity Logs) داخل لوحة التحكم

(من دخل؟ ماذا فعل؟ متى؟ ولمَن؟)

الهدف: تتبّع كل العمليات الحسّاسة داخل النظام (دخول، إضافة مستخدم، تعديل، حذف…).

نظام تسجيل النشاطات (Activity Logs) داخل لوحة التحكم

🎯 ماذا سنبني؟

جدول لتخزين السجلات (Logs)

تسجيل تلقائي عند:

  • تسجيل الدخول
  • إضافة مستخدم
  • تعديل مستخدم
  • حذف مستخدم

صفحة داخل الـ Dashboard لعرض السجلات (Admin فقط)

واجهة جميلة باستخدام Bootstrap


🧱 1️⃣ إنشاء موديل ActivityLog

dashboard/models.py

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

class ActivityLog(models.Model):
    ACTION_CHOICES = (
        ("login", "Login"),
        ("create_user", "Create User"),
        ("edit_user", "Edit User"),
        ("delete_user", "Delete User"),
    )

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    action = models.CharField(max_length=50, choices=ACTION_CHOICES)
    target = models.CharField(max_length=255, blank=True, null=True)
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.user.username} - {self.action} - {self.timestamp}"

ثم نفّذ:

python manage.py makemigrations
python manage.py migrate

🧠 2️⃣ دالة مساعدة لتسجيل النشاط

dashboard/utils.py

from .models import ActivityLog

def log_activity(user, action, target=None):
    ActivityLog.objects.create(user=user, action=action, target=target)

🧠 3️⃣ ربط التسجيل بالأحداث (Views)

أمثلة داخل views.py

عند تسجيل الدخول:

from .utils import log_activity

# بعد login(request, user)
log_activity(user, "login")

عند إضافة مستخدم:

log_activity(request.user, "create_user", target=user.username)

عند تعديل مستخدم:

log_activity(request.user, "edit_user", target=user.username)

عند حذف مستخدم:

log_activity(request.user, "delete_user", target=user.username)

🌐 4️⃣ View عرض السجلات (Admin فقط)

dashboard/views.py

from .models import ActivityLog

@login_required
@admin_required
def activity_logs(request):
    logs = ActivityLog.objects.select_related("user").order_by("-timestamp")[:200]
    return render(request, "dashboard/logs.html", {"logs": logs})

🌐 5️⃣ URL صفحة السجلات

dashboard/urls.py

path("logs/", activity_logs, name="activity_logs"),

🎨 6️⃣ واجهة عرض السجلات

dashboard/templates/dashboard/logs.html

{% extends "dashboard/base.html" %}
{% block content %}

<h3>سجل النشاطات</h3>

<table class="table table-striped mt-4">
    <thead class="table-dark">
        <tr>
            <th>المستخدم</th>
            <th>العملية</th>
            <th>الهدف</th>
            <th>التاريخ</th>
        </tr>
    </thead>
    <tbody>
        {% for log in logs %}
        <tr>
            <td>{{ log.user.username }}</td>
            <td>{{ log.get_action_display }}</td>
            <td>{{ log.target }}</td>
            <td>{{ log.timestamp|date:"Y-m-d H:i" }}</td>
        </tr>
        {% empty %}
        <tr>
            <td colspan="4">لا توجد سجلات بعد</td>
        </tr>
        {% endfor %}
    </tbody>
</table>

{% endblock %}

🧪 تمرين الدرس الثامن

نفّذ التالي:

  1. أضف فلترة السجلات حسب المستخدم
  2. أضف فلترة حسب نوع العملية
  3. إضافة زر تصدير CSV لسجل النشاط


✅ حل تمرين الدرس الثامن – Activity Logs (فلترة + تصدير CSV)

✔ المطلوب:

  1. فلترة السجلات حسب المستخدم
  2. فلترة حسب نوع العملية (Action)
  3. تصدير السجلات إلى ملف CSV


🧱 1️⃣ تحديث View السجلات لإضافة الفلترة

dashboard/views.py

import csv
from django.http import HttpResponse
from .models import ActivityLog

@login_required
@admin_required
def activity_logs(request):
    logs = ActivityLog.objects.select_related("user").order_by("-timestamp")

    user_id = request.GET.get("user")
    action = request.GET.get("action")

    if user_id:
        logs = logs.filter(user_id=user_id)

    if action:
        logs = logs.filter(action=action)

    # 🧾 تصدير CSV
    if request.GET.get("export") == "csv":
        response = HttpResponse(content_type="text/csv")
        response["Content-Disposition"] = 'attachment; filename="activity_logs.csv"'

        writer = csv.writer(response)
        writer.writerow(["User", "Action", "Target", "Timestamp"])

        for log in logs:
            writer.writerow([
                log.user.username,
                log.get_action_display(),
                log.target,
                log.timestamp.strftime("%Y-%m-%d %H:%M"),
            ])

        return response

    return render(request, "dashboard/logs.html", {
        "logs": logs,
    })

🧱 2️⃣ تحديث واجهة عرض السجلات (إضافة فلترة)

dashboard/templates/dashboard/logs.html

{% extends "dashboard/base.html" %}
{% block content %}

<h3>سجل النشاطات</h3>

<form method="get" class="row g-2 mb-3">
    <div class="col-md-4">
        <select name="user" class="form-select">
            <option value="">كل المستخدمين</option>
            {% for u in logs|dictsort:"user.id" %}
                <option value="{{ u.user.id }}">{{ u.user.username }}</option>
            {% endfor %}
        </select>
    </div>

    <div class="col-md-4">
        <select name="action" class="form-select">
            <option value="">كل العمليات</option>
            <option value="login">Login</option>
            <option value="create_user">Create User</option>
            <option value="edit_user">Edit User</option>
            <option value="delete_user">Delete User</option>
        </select>
    </div>

    <div class="col-md-4 d-flex gap-2">
        <button class="btn btn-primary">تصفية</button>
        <a href="?export=csv" class="btn btn-success">تصدير CSV</a>
    </div>
</form>

<table class="table table-striped">
    <thead class="table-dark">
        <tr>
            <th>المستخدم</th>
            <th>العملية</th>
            <th>الهدف</th>
            <th>التاريخ</th>
        </tr>
    </thead>
    <tbody>
        {% for log in logs %}
        <tr>
            <td>{{ log.user.username }}</td>
            <td>{{ log.get_action_display }}</td>
            <td>{{ log.target }}</td>
            <td>{{ log.timestamp|date:"Y-m-d H:i" }}</td>
        </tr>
        {% empty %}
        <tr>
            <td colspan="4">لا توجد سجلات</td>
        </tr>
        {% endfor %}
    </tbody>
</table>

{% endblock %}

💡 ملاحظة احترافية:
إذا أردت قائمة مستخدمين أدق في الفلترة، يمكننا تمرير users = User.objects.all() من الـ View بدل الاعتماد على logs فقط.


🧪 ماذا تحقق الآن؟

✔ فلترة ديناميكية حسب المستخدم
✔ فلترة حسب نوع العملية
✔ تصدير CSV بضغطة زر
✔ نظام Logs احترافي شبيه بالأنظمة التجارية
✔ مناسب للأرشفة والمراجعة الإدارية


🏁 الخلاصة

بهذا الحل:

  • أصبح لديك نظام مراقبة كامل
  • المشروع أصبح أقرب لتطبيقات SaaS حقيقية
  • جاهز لإضافة تقارير ورسوم بيانية لاحقًا

تعليقات