Claude
Skills
Sign in
Back

django-cbv-patterns

Included with Lifetime
$97 forever

Use when Django Class-Based Views for building modular, reusable views. Use when creating CRUD operations and complex view logic.

Backend & APIs

What this skill does


# Django Class-Based Views

Master Django Class-Based Views for building modular, reusable view
logic with proper separation of concerns.

## Generic Views

Use Django's built-in generic views for common patterns.

```python
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy

class PostListView(ListView):
    model = Post
    template_name = 'posts/list.html'
    context_object_name = 'posts'
    paginate_by = 10
    ordering = ['-created_at']

    def get_queryset(self):
        queryset = super().get_queryset()
        # Only show published posts
        return queryset.filter(published=True).select_related('author')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['total_posts'] = self.get_queryset().count()
        return context

class PostDetailView(DetailView):
    model = Post
    template_name = 'posts/detail.html'
    context_object_name = 'post'

    def get_queryset(self):
        return super().get_queryset().select_related('author').prefetch_related('comments')

class PostCreateView(CreateView):
    model = Post
    fields = ['title', 'content', 'published']
    template_name = 'posts/create.html'
    success_url = reverse_lazy('post-list')

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

class PostUpdateView(UpdateView):
    model = Post
    fields = ['title', 'content', 'published']
    template_name = 'posts/update.html'

    def get_success_url(self):
        return reverse_lazy('post-detail', kwargs={'pk': self.object.pk})

class PostDeleteView(DeleteView):
    model = Post
    template_name = 'posts/confirm_delete.html'
    success_url = reverse_lazy('post-list')
```

## Built-in Mixins

Leverage Django's built-in mixins for common functionality.

```python
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin, PermissionRequiredMixin
from django.views.generic import CreateView, UpdateView

class PostCreateView(LoginRequiredMixin, CreateView):
    model = Post
    fields = ['title', 'content']
    login_url = '/login/'
    redirect_field_name = 'next'

class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Post
    fields = ['title', 'content']

    def test_func(self):
        post = self.get_object()
        return self.request.user == post.author

    def handle_no_permission(self):
        # Custom handling when test fails
        messages.error(self.request, 'You can only edit your own posts')
        return redirect('post-list')

class AdminPostListView(PermissionRequiredMixin, ListView):
    model = Post
    permission_required = 'posts.view_post'
    raise_exception = True  # Return 403 instead of redirect
```

## Custom Mixins

Create reusable mixins for common patterns.

```python
from django.views.generic import View
from django.shortcuts import redirect
from django.contrib import messages

class AuthorRequiredMixin:
    """Ensure the current user is the object's author."""

    def dispatch(self, request, *args, **kwargs):
        obj = self.get_object()
        if obj.author != request.user:
            messages.error(request, 'You do not have permission')
            return redirect('post-list')
        return super().dispatch(request, *args, **kwargs)

class FormMessageMixin:
    """Add success messages to form views."""
    success_message = ''

    def form_valid(self, form):
        response = super().form_valid(form)
        if self.success_message:
            messages.success(self.request, self.success_message)
        return response

class AjaxableResponseMixin:
    """Handle AJAX requests differently."""

    def form_valid(self, form):
        if self.request.is_ajax():
            data = {
                'pk': form.instance.pk,
                'success': True
            }
            return JsonResponse(data)
        return super().form_valid(form)

    def form_invalid(self, form):
        if self.request.is_ajax():
            return JsonResponse(form.errors, status=400)
        return super().form_invalid(form)

# Usage
class PostUpdateView(LoginRequiredMixin, AuthorRequiredMixin, FormMessageMixin, UpdateView):
    model = Post
    fields = ['title', 'content']
    success_message = 'Post updated successfully'
```

## Method Resolution Order (MRO)

Understand how Django resolves methods in CBVs.

```python
# MRO matters! Order from left to right
class PostUpdateView(
    LoginRequiredMixin,      # Check login first
    AuthorRequiredMixin,     # Then check authorship
    FormMessageMixin,        # Add messages
    UpdateView               # Base view last
):
    model = Post
    fields = ['title', 'content']

# View the MRO
print(PostUpdateView.__mro__)

# Bad example - wrong order
class BadPostUpdateView(
    UpdateView,              # Base view first - wrong!
    LoginRequiredMixin,
    AuthorRequiredMixin
):
    pass  # Mixins won't work correctly

# Override dispatch to control flow
class CustomView(LoginRequiredMixin, UpdateView):
    def dispatch(self, request, *args, **kwargs):
        # Custom logic before any other processing
        if not request.user.is_verified:
            return redirect('verify-email')
        return super().dispatch(request, *args, **kwargs)
```

## Form Handling in CBVs

Advanced form handling patterns.

```python
from django.views.generic.edit import FormView
from django.contrib import messages

class ContactFormView(FormView):
    template_name = 'contact.html'
    form_class = ContactForm
    success_url = '/thanks/'

    def get_form_kwargs(self):
        """Pass request to form."""
        kwargs = super().get_form_kwargs()
        kwargs['request'] = self.request
        return kwargs

    def get_initial(self):
        """Pre-populate form."""
        initial = super().get_initial()
        if self.request.user.is_authenticated:
            initial['email'] = self.request.user.email
            initial['name'] = self.request.user.name
        return initial

    def form_valid(self, form):
        form.send_email()
        messages.success(self.request, 'Message sent!')
        return super().form_valid(form)

    def form_invalid(self, form):
        messages.error(self.request, 'Please correct the errors')
        return super().form_invalid(form)

# Multiple forms in one view
class ProfileUpdateView(LoginRequiredMixin, TemplateView):
    template_name = 'profile.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if 'user_form' not in context:
            context['user_form'] = UserForm(instance=self.request.user)
        if 'profile_form' not in context:
            context['profile_form'] = ProfileForm(instance=self.request.user.profile)
        return context

    def post(self, request, *args, **kwargs):
        user_form = UserForm(request.POST, instance=request.user)
        profile_form = ProfileForm(request.POST, instance=request.user.profile)

        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()
            messages.success(request, 'Profile updated')
            return redirect('profile')

        return self.render_to_response(
            self.get_context_data(user_form=user_form, profile_form=profile_form)
        )
```

## When to Use CBVs vs FBVs

Guidelines for choosing between class-based and function-based views.

```python
# Use CBVs for:
# 1. Standard CRUD operations
class PostListView(ListView):
    model = Post

# 2. Reusable view logic
class OwnerRequiredMixin:
    def get_queryset(self):
        return super().get_queryset().filter(owner=self.request.user)

# 3. Multiple similar views
class UserPostListView(OwnerRequiredMixin, ListView):
    model = Post

class UserDraftListView(OwnerRequiredMixin, ListView):
    model = Post
    queryset =

Related in Backend & APIs