سادهترین شکل View: یک تابع
View تابعی (FBV) یک HttpRequest میگیرد و یک HttpResponse برمیگرداند. همین. هر چیز دیگری — قالب، ریدایرکت، JSON — فقط انواع مختلف پاسخ است. FBV صریح و خواناست و برای منطقهای غیرمعمول اغلب از CBV بهتر است.
آنچه در request دارید
| ویژگی | محتوا |
|---|---|
request.method | "GET"، "POST" و… |
request.GET / request.POST | QueryDict پارامترها؛ getlist() برای چندمقداری |
request.FILES | فایلهای آپلودی |
request.user | کاربر فعلی یا AnonymousUser |
request.headers | هدرها؛ غیرحساس به بزرگی حروف |
request.session | دیکشنری Session |
request.META["REMOTE_ADDR"] | IP (پشت پراکسی: IP خود پراکسی!) |
# apps/orders/views.py
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POST
from apps.catalog.models import Carpet
from .models import Order
@login_required
def order_detail(request, pk):
order = get_object_or_404(
Order.objects.select_related("customer").prefetch_related("items__carpet"),
pk=pk,
customer__user=request.user, # جلوگیری از دیدن سفارش دیگران
)
return render(request, "orders/detail.html", {"order": order})
@login_required
@require_POST
def cancel_order(request, pk):
order = get_object_or_404(Order, pk=pk, customer__user=request.user)
if order.status != Order.Status.PENDING:
messages.error(request, "فقط سفارش در انتظار پرداخت قابل لغو است.")
else:
order.status = Order.Status.CANCELLED
order.save(update_fields=["status"])
messages.success(request, f"سفارش {order.tracking_code} لغو شد.")
return redirect(order) # از get_absolute_url استفاده میکند
def carpet_price(request, code):
carpet = get_object_or_404(Carpet, code=code, status="active")
return JsonResponse({"code": carpet.code, "name": carpet.name, "price": carpet.price},
json_dumps_params={"ensure_ascii": False})
الگوی POST/Redirect/GET
بعد از هر POST موفق همیشه redirect کنید، نه render. اگر render کنید و کاربر صفحه را رفرش کند، مرورگر فرم را دوباره میفرستد و سفارش دو بار ثبت یا لغو میشود. پیام موفقیت را هم با messages به صفحهی بعد منتقل کنید.
نکتههایی که کمتر کسی میداند
- شرط
customer__user=request.userدر get_object_or_404 جلوی آسیبپذیری IDOR را میگیرد: کاربری که عدد آدرس را عوض کند بهجای سفارش دیگران 404 میبیند، نه 403 که وجود سفارش را لو بدهد. - بدون
ensure_ascii=Falseمتن فارسی در JsonResponse بهصورتک...میرود؛ از نظر فنی درست است اما در دیباگ و لاگ خوانا نیست. JsonResponseبهطور پیشفرض فقط dict میپذیرد؛ برای برگرداندن لیست بایدsafe=Falseبدهید.- ترتیب دکوراتورها مهم است:
login_requiredبیرونی باشد تا کاربر مهمان به صفحهی ورود برود، نه اینکه اول خطای 405 بگیرد. redirect()هم شیء مدل، هم نام url با آرگومان (redirect("orders:detail", pk=5)) و هم آدرس خام را میپذیرد؛ برای ریدایرکت دائمیpermanent=Trueبدهید.