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

Lesson 4: Class-Based Views & Forms

Class-Based Views (ListView, DetailView, CreateView, UpdateView, DeleteView). Django Forms, ModelForms, formsets. CSRF protection, form validation, file upload.

💻 Programming — Lesson 4 Lesson 4: Class-Based Views & Forms

Django: From Basics to Advanced

Part 1: Django Fundamentals

xdev.asia

1. Class-Based Views

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 Forms

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. Form in View

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. Formsets

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. File Upload

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>

Next article: DRF Serializers & Views — Django REST Framework setup.