فصل ۱۲: Class-based Views (CBV) در Django — از مقدماتی تا پیشرفته

۱. مقدمه
تا الان ما بیشتر با Function-based Views (FBV) کار کردیم، یعنی ویوهایی که با یک تابع تعریف میشن.
مثل این:
from django.shortcuts import render
from .models import Product
def product_list(request):
products = Product.objects.all()
return render(request, 'product_list.html', {'products': products})
این روش ساده و قابل فهمه، مخصوصاً برای پروژههای کوچک.
اما وقتی پروژه بزرگتر میشه:
- کدها تکراری میشه
- مدیریت حالتها سخت میشه (GET, POST و …)
- اضافه کردن قابلیتها مثل فرم، لیست و جزییات دردسر پیدا میکنه
اینجاست که Class-based Views (CBV) کمکمون میکنن.
۲. CBV چیست؟
CBV یعنی به جای یک تابع، از یک کلاس برای تعریف ویو استفاده کنیم.
جنگو چندین کلاس آماده داره که خیلی از کارهای معمول رو برات انجام میدن.
مزایا:
- کد تمیزتر — قابلیت وراثت (inheritance) و استفاده مجدد
- جداسازی بهتر منطق — متدها برای هر حالت (
get(),post()) - زمان توسعه کمتر — Generic Views آماده جنگو
۳. سادهترین شکل CBV
یک ویو ساده با CBV:
from django.views import View
from django.http import HttpResponse
class HelloView(View):
def get(self, request):
return HttpResponse("سلام از CBV!")
📌 در urls.py:
from django.urls import path
from .views import HelloView
urlpatterns = [
path('hello/', HelloView.as_view(), name='hello'),
]
as_view()یک متد کلاس است که نمونه قابل اجرا از ویو رو میسازه.- در CBV، هر متد مثل
get()،post()و … مسئول یکی از HTTP Methodهاست.
۴. CBV vs FBV — مقایسه عملی
مثلاً یک ویوی ثبت محصول با فرم، در FBV:
def add_product(request):
if request.method == 'POST':
form = ProductForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('product_list')
else:
form = ProductForm()
return render(request, 'add_product.html', {'form': form})
همین منطق در CBV:
from django.views import View
from django.shortcuts import render, redirect
from .forms import ProductForm
class AddProductView(View):
def get(self, request):
form = ProductForm()
return render(request, 'add_product.html', {'form': form})
def post(self, request):
form = ProductForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('product_list')
return render(request, 'add_product.html', {'form': form})
۵. Generic Class-based Views — قهرمانهای آماده جنگو
جنگو CBVهای آمادهای داره که این الگوهای پرتکرار رو سریعتر میکنه:
- ListView → نمایش لیست اشیا
- DetailView → نمایش جزئیات یک شیء
- CreateView → ساخت شیء جدید
- UpdateView → ویرایش شیء
- DeleteView → حذف شیء
۶. مثال عملی — لیست محصولات با ListView
در views.py:
from django.views.generic import ListView
from .models import Product
class ProductListView(ListView):
model = Product
template_name = 'product_list.html' # اجباری نیست اگر از اسم پیشفرض استفاده کنیم
context_object_name = 'products' # نام متغیری که در قالب استفاده میکنیم
paginate_by = 5 # صفحهبندی خودکار
📌 در urls.py:
path('products/', ProductListView.as_view(), name='product_list'),
📌 در product_list.html:
<h1>لیست محصولات</h1>
{% for product in products %}
<h3>{{ product.name }}</h3>
<p>{{ product.price }} تومان</p>
{% empty %}
<p>محصولی یافت نشد</p>
{% endfor %}
<div>
{% if is_paginated %}
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">قبلی</a>
{% endif %}
<span>صفحه {{ page_obj.number }} از {{ page_obj.paginator.num_pages }}</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">بعدی</a>
{% endif %}
{% endif %}
</div>
۷. نمایش جزئیات — DetailView
در views.py:
from django.views.generic import DetailView
class ProductDetailView(DetailView):
model = Product
template_name = 'product_detail.html'
context_object_name = 'product'
📌 در urls.py:
path('products/<int:pk>/', ProductDetailView.as_view(), name='product_detail'),
📌 در product_detail.html:
<h2>{{ product.name }}</h2>
<p>قیمت: {{ product.price }} تومان</p>
{% if product.image %}
<img src="{{ product.image.url }}" width="200">
{% endif %}
<p>موجودی: {{ product.stock }}</p>
۸. ساخت محصول — CreateView
در views.py:
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .forms import ProductForm
class ProductCreateView(CreateView):
model = Product
form_class = ProductForm
template_name = 'add_product.html'
success_url = reverse_lazy('product_list') # بعد از موفقیت
📌 در urls.py:
path('products/add/', ProductCreateView.as_view(), name='add_product'),
۹. ویرایش محصول — UpdateView
from django.views.generic.edit import UpdateView
class ProductUpdateView(UpdateView):
model = Product
form_class = ProductForm
template_name = 'add_product.html'
success_url = reverse_lazy('product_list')
۱۰. حذف محصول — DeleteView
from django.views.generic.edit import DeleteView
class ProductDeleteView(DeleteView):
model = Product
template_name = 'confirm_delete.html'
success_url = reverse_lazy('product_list')
📌 در confirm_delete.html:
<h3>آیا مطمئن هستید که میخواهید "{{ object.name }}" را حذف کنید؟</h3>
<form method="post">
{% csrf_token %}
<button type="submit">بله، حذف شود</button>
<a href="{% url 'product_list' %}">انصراف</a>
</form>
۱۱. شخصیسازی QuerySet در CBV
class ProductListView(ListView):
model = Product
template_name = 'product_list.html'
context_object_name = 'products'
def get_queryset(self):
return Product.objects.filter(stock__gt=0).order_by('price')
۱۲. اضافه کردن داده اضافی به Context
class ProductListView(ListView):
model = Product
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
۱۳. ترکیب CBV با Mixins
Mixin یک کلاس کوچک با یک یا چند رفتار خاصه که میشه به CBV اضافه کرد.
مثال: فقط کاربران لاگین کرده مجاز باشن:
from django.contrib.auth.mixins import LoginRequiredMixin
class ProductCreateView(LoginRequiredMixin, CreateView):
model = Product
form_class = ProductForm
success_url = reverse_lazy('product_list')
login_url = 'login'
۱۴. وقتی باید از FBV استفاده کرد
- وقتی منطق ساده و کوتاه داری
- وقتی نیاز به کنترل کامل روی تمام جزئیات داری
- وقتی فقط یک متد HTTP رو پشتیبانی میکنی
۱۵. نکات حرفهای (Best Practices)
- استفاده از Generic Viewها برای کاهش کد
- استفاده از Mixins برای اضافه کردن قابلیتها
- Override فقط متدهایی که نیاز داری
- نامگذاری واضح برای Templateها و Contextها
۱۶. خطاهای رایج
| خطا | علت | راهحل |
|---|---|---|
TemplateDoesNotExist | مسیر قالب اشتباه | template_name رو درست بده |
NoReverseMatch | آدرس URL اشتباه در قالب | اسم درست URL Name رو بده |
| دسترسی غیرمجاز | فراموشی استفاده از LoginRequiredMixin | اضافه کردن Mixin |
۱۷. مثال کامل — مدیریت محصولات با CBV
urls.py:
urlpatterns = [
path('products/', ProductListView.as_view(), name='product_list'),
path('products/<int:pk>/', ProductDetailView.as_view(), name='product_detail'),
path('products/add/', ProductCreateView.as_view(), name='add_product'),
path('products/<int:pk>/edit/', ProductUpdateView.as_view(), name='edit_product'),
path('products/<int:pk>/delete/', ProductDeleteView.as_view(), name='delete_product'),
]
حالا ما:
- لیست محصولات با صفحهبندی اتومات
- نمایش جزئیات
- افزودن محصول
- ویرایش محصول
- حذف محصول
رو با کمتر از نصف کد FBV داریم.
۱۸. جمعبندی
در این فصل یاد گرفتیم:
- CBV چیه و چرا استفاده میکنیم
- پیادهسازی ساده با
View - قدرت Generic Class-based Views
- استفاده در پروژه فروشگاه آنلاین
- نکات بهینهسازی و امنیتی