الگوهای تکراری را دوباره ننویسید
فهرست با صفحهبندی، صفحهی جزئیات، فرم ساخت و ویرایش، تأیید حذف: این پنج الگو در هر پروژه دهها بار تکرار میشوند. Generic viewهای جنگو اینها را آماده دارند و شما فقط تفاوتها را بازنویسی میکنید.
| View | کار | قالب پیشفرض |
|---|---|---|
| ListView | فهرست + صفحهبندی | orders/order_list.html |
| DetailView | یک شیء با pk یا slug | orders/order_detail.html |
| CreateView | فرم ساخت | orders/order_form.html |
| UpdateView | فرم ویرایش | orders/order_form.html |
| DeleteView | تأیید و حذف با POST | orders/order_confirm_delete.html |
| TemplateView / RedirectView | صفحهی ثابت / ریدایرکت | — |
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic import CreateView, DetailView, ListView
from .forms import OrderForm
from .models import Order
class OrderListView(LoginRequiredMixin, ListView):
template_name = "orders/list.html"
context_object_name = "orders"
paginate_by = 20
paginate_orphans = 3 # صفحهی آخرِ ۲ آیتمی ساخته نشود
def get_queryset(self):
qs = Order.objects.for_user(self.request.user).select_related("customer")
if q := self.request.GET.get("q", "").strip():
qs = qs.filter(tracking_code__icontains=q)
return qs
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["q"] = self.request.GET.get("q", "")
return ctx
class OrderDetailView(LoginRequiredMixin, DetailView):
template_name = "orders/detail.html"
def get_queryset(self):
return Order.objects.for_user(self.request.user).with_details()
class OrderCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
form_class = OrderForm
template_name = "orders/form.html"
def get_success_message(self, cleaned_data):
# tracking_code فیلد فرم نیست؛ پس از self.object میخوانیم
return f"سفارش {self.object.tracking_code} با موفقیت ثبت شد."
def form_valid(self, form):
form.instance.customer = self.request.user.customer
return super().form_valid(form) # ذخیره + redirect به get_absolute_url
صفحهبندی در قالب
{% if page_obj.has_next %}
<a href="?{% querystring page=page_obj.next_page_number %}">صفحهی بعد</a>
{% endif %}
<span>صفحهی {{ page_obj.number }} از {{ page_obj.paginator.num_pages }}</span>
تگ {% querystring %} (Django 5.1 به بعد) پارامترهای فعلی مثل q را حفظ میکند و فقط page را عوض میکند؛ قبلاً باید دستی مینوشتید و جستوجو با رفتن به صفحهی ۲ گم میشد.
نکتههایی که کمتر کسی میداند
- mixinهای دسترسی (LoginRequiredMixin، PermissionRequiredMixin) باید سمت چپ کلاس پایه باشند؛ به خاطر MRO پایتون، اگر راست باشند ممکن است View قبل از بررسی دسترسی اجرا شود.
- فیلتر امنیتی را در
get_querysetبگذارید نهget_object؛ اینطور DetailView، UpdateView و DeleteView همه خودکار 404 میدهند. - اگر
success_urlتعریف نکنید، CreateView و UpdateView بهget_absolute_urlشیء ریدایرکت میکنند؛ نبود هر دو خطای ImproperlyConfigured میدهد. - در urls میتوانید ویژگیها را بدون زیرکلاس عوض کنید:
TemplateView.as_view(template_name="pages/about.html"). - DeleteView از Django 4.0 از
FormMixinاستفاده میکند؛ منطق قبل از حذف را درform_validبنویسید، نه درdelete()که دیگر برای POST صدا زده نمیشود.