~/icsd.ir — bash
SYSTEM_ONLINE

🎯 فصل ۳: ویوها و URLها

ویوها قلب اپلیکیشن جنگو هستن. اینجا منطق برنامه‌ت رو می‌نویسی.

ویوها قلب اپلیکیشن جنگو هستن. اینجا منطق برنامه‌ت رو می‌نویسی.

📦 Function-Based Views (FBV)

ساده‌ترین نوع ویو، تابعی هست که یک request می‌گیره و یک HttpResponse برمی‌گردونه.

products/views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, JsonResponse
from .models import Product

# ساده‌ترین ویو
def home(request):
    return HttpResponse("به فروشگاه ما خوش آمدید!")

# ویو با Template
def product_list(request):
    products = Product.objects.filter(is_active=True)
    return render(request, 'products/list.html', {
        'products': products,
        'title': 'لیست محصولات'
    })

# ویو با پارامتر
def product_detail(request, product_id):
    product = get_object_or_404(Product, id=product_id)
    return render(request, 'products/detail.html', {'product': product})

# برگردوندن JSON
def product_json(request, product_id):
    product = get_object_or_404(Product, id=product_id)
    return JsonResponse({
        'id': product.id,
        'name': product.name,
        'price': str(product.price)
    })
💡

get_object_or_404 چیه؟

اگه آبجکت پیدا بشه، برمی‌گردونه. اگه پیدا نشه، خودکار صفحه ۴۰۴ نمایش می‌ده. خیلی پرکاربرده!

🏛️ Class-Based Views (CBV)

ویوهای کلاس‌محور برای کارهای تکراری بسیار مناسب‌اند. جنگو ویوهای آماده‌ای داره که کارت رو ساده می‌کنن.

ویوهای ژنریک پرکاربرد

ویو کاربرد
ListView نمایش لیستی از آبجکت‌ها
DetailView نمایش جزئیات یک آبجکت
CreateView ساخت آبجکت جدید با فرم
UpdateView ویرایش آبجکت موجود
DeleteView حذف آبجکت
TemplateView نمایش یک قالب ساده

مقایسه FBV و CBV

FBV (تابعی)

views.py
def product_list(request):
    products = Product.objects.all()
    return render(request, 'products/list.html', {
        'products': products
    })

CBV (کلاسی)

views.py
from django.views.generic import ListView

class ProductListView(ListView):
    model = Product
    template_name = 'products/list.html'
    context_object_name = 'products'

⚖️

کدوم رو استفاده کنم؟

FBV برای منطق پیچیده و خاص بهتره (راحت‌تر می‌خونیش)
CBV برای کارهای استاندارد (CRUD) سریع‌تره

مثال کامل CBV

products/views.py
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Product

class ProductListView(ListView):
    model = Product
    template_name = 'products/list.html'
    context_object_name = 'products'
    paginate_by = 10  # صفحه‌بندی خودکار!
    
    def get_queryset(self):
        # فقط محصولات فعال
        return Product.objects.filter(is_active=True).order_by('-created_at')
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['page_title'] = 'لیست محصولات'
        return context

class ProductDetailView(DetailView):
    model = Product
    template_name = 'products/detail.html'
    context_object_name = 'product'

class ProductCreateView(CreateView):
    model = Product
    fields = ['name', 'description', 'price', 'stock']
    template_name = 'products/form.html'
    success_url = reverse_lazy('product_list')

class ProductUpdateView(UpdateView):
    model = Product
    fields = ['name', 'description', 'price', 'stock']
    template_name = 'products/form.html'
    success_url = reverse_lazy('product_list')

class ProductDeleteView(DeleteView):
    model = Product
    template_name = 'products/confirm_delete.html'
    success_url = reverse_lazy('product_list')

🛣️ URL Routing

الان وقتشه که این ویوها رو به URL‌ها وصل کنیم.

products/urls.py
from django.urls import path
from . import views

app_name = 'products'  # برای namespace

urlpatterns = [
    # FBV
    path('', views.product_list, name='list'),
    path('product/<int:product_id>/', views.product_detail, name='detail'),
    
    # CBV - متد as_view() مهمه
    path('all/', views.ProductListView.as_view(), name='all'),
    path('detail/<int:pk>/', views.ProductDetailView.as_view(), name='detail_cbv'),
    path('create/', views.ProductCreateView.as_view(), name='create'),
    path('update/<int:pk>/', views.ProductUpdateView.as_view(), name='update'),
    path('delete/<int:pk>/', views.ProductDeleteView.as_view(), name='delete'),
]

📌 پارامترهای URL

سه نوع converter پرکاربرد در URL‌ها داریم:

Converter پذیرش مثال
<int:id> عدد صحیح /product/42/
<str:name> هر متنی بدون اسلش /category/digital/
<slug:slug> متن slug /post/my-first-post/
<uuid:uid> UUID /order/abc-123/
<path:p> مسیر کامل با اسلش /files/img/logo.png
products/urls.py
urlpatterns = [
    path('product/<int:product_id>/', views.product_detail),
    path('category/<slug:category_slug>/', views.category_view),
    path('search/<str:keyword>/', views.search),
    path('archive/<int:year>/<int:month>/', views.archive),
]
products/views.py
def product_detail(request, product_id):  # نام پارامتر باید با URL یکی باشه
    product = get_object_or_404(Product, id=product_id)
    return render(request, 'products/detail.html', {'product': product})

def archive(request, year, month):
    products = Product.objects.filter(
        created_at__year=year,
        created_at__month=month
    )
    return render(request, 'products/archive.html', {
        'products': products,
        'year': year,
        'month': month
    })

Query String (پارامترهای GET)

برای پارامترهای اختیاری مثل ?page=2&sort=price:

products/views.py
def search(request):
    # دریافت پارامتر از query string
    query = request.GET.get('q', '')  # مقدار پیش‌فرض خالی
    sort = request.GET.get('sort', '-created_at')
    page = request.GET.get('page', 1)
    
    products = Product.objects.filter(name__icontains=query).order_by(sort)
    
    return render(request, 'products/search.html', {
        'products': products,
        'query': query
    })

# URL درخواست: /search/?q=فرش&sort=price&page=2

🔄 ریدایرکت و Reverse

ریدایرکت کردن

products/views.py
from django.shortcuts import redirect

def add_to_cart(request, product_id):
    # ... منطق اضافه کردن به سبد ...
    
    # روش ۱: با نام URL
    return redirect('products:list')
    
    # روش ۲: با URL مستقیم
    return redirect('/products/')
    
    # روش ۳: با آبجکت (اگه get_absolute_url داشته باشه)
    return redirect(product)

Reverse – تولید URL از نام

Python
from django.urls import reverse

# تولید URL از روی نام
url = reverse('products:detail', args=[5])
# نتیجه: '/products/product/5/'

url = reverse('products:archive', kwargs={'year': 1403, 'month': 6})
# نتیجه: '/products/archive/1403/6/'
⚠️

چرا reverse مهمه؟

اگه روزی URLها رو تغییر دادی، نیاز نیست کل کد رو دنبال URLها بگردی! کافیه فقط فایل urls.py رو تغییر بدی.

get_absolute_url در مدل

products/models.py
from django.urls import reverse

class Product(models.Model):
    name = models.CharField(max_length=200)
    
    def get_absolute_url(self):
        return reverse('products:detail', args=[self.id])

# حالا توی template:
# <a href="{{ product.get_absolute_url }}">{{ product.name }}</a>

📨 روش‌های HTTP

وب با چند روش (HTTP Method) کار می‌کنه:

Method کاربرد
GET دریافت اطلاعات (مثل دیدن صفحه)
POST ارسال اطلاعات (مثل ثبت فرم)
PUT/PATCH به‌روزرسانی (بیشتر در API)
DELETE حذف (بیشتر در API)
products/views.py
def contact(request):
    if request.method == 'POST':
        # کاربر فرم رو ارسال کرده
        name = request.POST.get('name')
        email = request.POST.get('email')
        message = request.POST.get('message')
        
        # ذخیره یا ارسال ایمیل
        # ...
        
        return redirect('contact_success')
    
    # درخواست GET - نمایش فرم خالی
    return render(request, 'contact.html')

🎯 مثال‌های واقعی

آسان

مثال ۱: صفحه درباره ما

core/views.py
from django.shortcuts import render

def about(request):
    context = {
        'company_name': 'ICSD',
        'founded_year': 1399,
        'team_size': 12,
    }
    return render(request, 'about.html', context)

متوسط

مثال ۲: لیست محصولات با فیلتر و جستجو

products/views.py
from django.shortcuts import render
from django.core.paginator import Paginator
from .models import Product, Category

def product_list(request):
    products = Product.objects.filter(is_active=True)
    
    # فیلتر بر اساس دسته
    category_id = request.GET.get('category')
    if category_id:
        products = products.filter(category_id=category_id)
    
    # جستجو
    query = request.GET.get('q')
    if query:
        products = products.filter(name__icontains=query)
    
    # مرتب‌سازی
    sort = request.GET.get('sort', '-created_at')
    products = products.order_by(sort)
    
    # صفحه‌بندی - 12 محصول در هر صفحه
    paginator = Paginator(products, 12)
    page_number = request.GET.get('page', 1)
    page_obj = paginator.get_page(page_number)
    
    context = {
        'page_obj': page_obj,
        'categories': Category.objects.all(),
        'current_category': category_id,
        'query': query,
    }
    return render(request, 'products/list.html', context)

پیشرفته

مثال ۳: ویو AJAX برای افزودن به سبد خرید

cart/views.py
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.contrib.auth.decorators import login_required
from products.models import Product
from .models import CartItem

@login_required
@require_POST  # فقط POST قابل قبوله
def add_to_cart(request):
    try:
        product_id = request.POST.get('product_id')
        quantity = int(request.POST.get('quantity', 1))
        
        product = Product.objects.get(id=product_id)
        
        # اگه موجودی کافی نیست
        if product.stock < quantity:
            return JsonResponse({
                'success': False,
                'message': 'موجودی کافی نیست'
            }, status=400)
        
        # اضافه یا به‌روزرسانی
        cart_item, created = CartItem.objects.get_or_create(
            user=request.user,
            product=product,
            defaults={'quantity': quantity}
        )
        
        if not created:
            cart_item.quantity += quantity
            cart_item.save()
        
        # تعداد کل آیتم‌های سبد
        total_items = CartItem.objects.filter(
            user=request.user
        ).count()
        
        return JsonResponse({
            'success': True,
            'message': 'محصول به سبد اضافه شد',
            'total_items': total_items
        })
        
    except Product.DoesNotExist:
        return JsonResponse({
            'success': False,
            'message': 'محصول یافت نشد'
        }, status=404)
    except Exception as e:
        return JsonResponse({
            'success': False,
            'message': str(e)
        }, status=500)

📝 تمرین‌ها

آسان

تمرین ۱

یه ویو بساز که عدد صحیح از URL بگیره و ضرب در ۲ رو نمایش بده. مثلاً /double/5/ بهت ۱۰ بده.

متوسط

تمرین ۲

یه CBV از نوع ListView بساز که محصولات یک دسته خاص رو نمایش بده. URL باید چیزی شبیه /category/digital/ باشه.

پیشرفته

تمرین ۳

یه ویو بساز که هم محتوای HTML برمی‌گردونه (در درخواست عادی) و هم JSON (وقتی پارامتر ?format=json داشته باشه).

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.