Chuyển đến nội dung chính

第 4 課:基於類別的檢視和表單

基於類別的視圖(ListView、DetailView、CreateView、UpdateView、DeleteView)。 Django 表單、模型表單、表單集。 CSRF 保護、表單驗證、文件上傳。

💻 程式設計 — 第 4 課 第 4 課:基於類別的檢視和表單

Django:從基礎到高級

第 1 部分:Django 基礎知識

亞洲開發網

1. 基於類別的視圖

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 = 20

    def get_queryset(self):
        qs = super().get_queryset().filter(is_active=True)
        search = self.request.GET.get('q')
        if search:
            qs = qs.filter(name__icontains=search)
        return qs

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

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

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

class ProductDeleteView(DeleteView):
    model = Product
    success_url = reverse_lazy('products:list')

2.Django表單

from django import forms
from .models import Product

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ['name', 'slug', 'description', 'price', 'category', 'image']
        widgets = {
            'description': forms.Textarea(attrs={'rows': 4}),
            'price': forms.NumberInput(attrs={'min': 0, 'step': '0.01'}),
        }

    def clean_price(self):
        price = self.cleaned_data['price']
        if price <= 0:
            raise forms.ValidationError('Giá phải lớn hơn 0')
        return price

    def clean(self):
        cleaned_data = super().clean()
        name = cleaned_data.get('name')
        if name and len(name) < 3:
            self.add_error('name', 'Tên phải có ít nhất 3 ký tự')
        return cleaned_data

# Contact form (không liên kết model)
class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)

3. 視圖中的表單

from django.shortcuts import render, redirect
from django.contrib import messages

def contact_view(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # Xử lý form data
            send_contact_email(form.cleaned_data)
            messages.success(request, 'Gửi thành công!')
            return redirect('contact')
    else:
        form = ContactForm()

    return render(request, 'contact.html', {'form': form})

4. 表單集

from django.forms import modelformset_factory, inlineformset_factory

# ModelFormSet
ProductFormSet = modelformset_factory(Product, fields=['name', 'price'], extra=3)

# InlineFormSet (parent-child)
ImageFormSet = inlineformset_factory(
    Product, ProductImage,
    fields=['image', 'alt_text'],
    extra=3,
    can_delete=True,
)

5. 文件上傳

def upload_view(request):
    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES)
        if form.is_valid():
            product = form.save()
            return redirect('products:detail', slug=product.slug)
    else:
        form = ProductForm()
    return render(request, 'products/form.html', {'form': form})
<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  {{ form.as_div }}
  <button type="submit">Lưu</button>
</form>

下一篇: DRF 序列化器和視圖 — Django REST 框架設定。