رحلة الطلب في Django
حين يطلب المتصفّح /blog/first/:
- Django يقرأ جدول المسارات (
urls.py) ويجد أول نمط يطابق الرابط. - يستدعي العرض (view) المربوط بهذا النمط، ويمرّر له كائن الطلب
requestوأي قيم من الرابط. - العرض يجلب البيانات من النماذج، ويمرّرها لقالب يحوّلها HTML.
- العرض يُرجع استجابة (
HttpResponse) يرسلها Django للمتصفّح.
هذا نمط MVT: Model وView وTemplate. والعرض هو المنسّق في الوسط.
أبسط عرض
# blog/views.py
from django.http import HttpResponse
def home(request):
return HttpResponse("مرحبًا من Django!")
العرض مجرّد دالة: تأخذ request وتُرجع استجابة. هذا كل العقد.
عرض يجلب بيانات ويعرض قالبًا
from django.shortcuts import render
from .models import Post
def post_list(request):
posts = Post.objects.filter(status=Post.Status.PUBLISHED)
return render(request, "blog/post_list.html", {"posts": posts})
render تأخذ الطلب، واسم القالب، وقاموس السياق (context): كل مفتاح فيه يصبح متغيّرًا في القالب (posts هنا). وتُرجع HttpResponse جاهزة بالـ HTML.
صفحة واحدة: get_object_or_404
from django.shortcuts import get_object_or_404, render
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug, status=Post.Status.PUBLISHED)
return render(request, "blog/post_detail.html", {"post": post})
لو استخدمت Post.objects.get(slug=slug) ولم يوجد المقال، سيرمي DoesNotExist ويرى الزائر خطأ 500 (عطل في الخادم). get_object_or_404 تحوّل "غير موجود" إلى 404 الصحيحة. ولاحظ الشرط الإضافي status=...: بدونه يستطيع أي زائر قراءة المسودّات بتخمين الرابط.
كائن request
def search(request):
q = request.GET.get("q", "").strip() # ?q=django ، و "" إن غاب
posts = Post.objects.filter(title__icontains=q) if q else Post.objects.none()
return render(request, "blog/search.html", {"posts": posts, "q": q})
| الخاصية | فيها |
|---|---|
request.method | "GET" أو "POST"... |
request.GET | معاملات الرابط (?q=...) |
request.POST | بيانات نموذج مُرسل |
request.user | المستخدم الحالي (أو مجهول) |
request.FILES | الملفات المرفوعة |
استخدم .get("key", default) لا request.GET["key"]: الثانية ترمي خطأ (فيرى الزائر 400) إذا غاب المعامل.
أنواع الاستجابات
from django.http import JsonResponse
from django.shortcuts import redirect
def go_home(request):
return redirect("blog:post_list") # 302 إلى مسار باسمه
def api_count(request):
return JsonResponse({"count": Post.objects.count()})
وredirect تقبل اسم مسار، أو كائنًا له get_absolute_url، أو رابطًا. واستخدام أسماء المسارات بدل كتابة الروابط يدويًا يعني أن تغيير رابط في urls.py لا يكسر شيئًا.
ربط العروض بالمسارات
# blog/urls.py
from django.urls import path
from . import views
app_name = "blog"
urlpatterns = [
path("", views.post_list, name="post_list"),
path("search/", views.search, name="search"),
path("<slug:slug>/", views.post_detail, name="post_detail"),
]
<slug:slug> تلتقط جزءًا من الرابط وتمرّره للعرض كمعامل بنفس الاسم. والترتيب مهمّ: search/ قبل <slug:slug>/، وإلا فُهمت كلمة "search" على أنها slug مقال. التفاصيل الكاملة للأنماط والأسماء والتضمين في درس المسارات والتوجيه.
قيّد الميثود
from django.views.decorators.http import require_POST
@require_POST
def delete_post(request, slug):
...
عرض يحذف أو يعدّل يجب ألا يعمل بـ GET: الروابط العادية والمتصفّحات التي تحمّل الصفحات مسبقًا قد تفتحه دون قصد. require_POST تُرجع 405 لأي ميثود آخر.
أخطاء شائعة
Model.objects.getفي عرض: 500 بدل 404. استخدمget_object_or_404.- نسيان
return:The view didn't return an HttpResponse object. request.GET["q"]: خطأ إذا غاب المعامل. استخدم.get("q", "").- عرض يعدّل البيانات عبر GET: استخدم POST مع
require_POST. - روابط مكتوبة يدويًا: تنكسر حين تغيّر
urls.py. استخدمredirect("app:name")و{% url %}.
🎯 التالي: الـ Views الصنفية.