Skip to main content

django-cbv-patterns

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

Informações da origem

Repositório
TheBushidoCollective/han
Última atividade na origem
11 de fevereiro de 2026 às 17:47
Idioma detectado do SKILL.md
inglês
Estrelas
198
Forks
22

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
django-cbv-patterns
user-invocable
false
description
Use when Django Class-Based Views for building modular, reusable views. Use when creating CRUD operations and complex view logic.
allowed-tools
["Bash","Read"]
# 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 = Post.objects.filter(published=False) # Use FBVs for: # 1. Simple one-off views def simple_view(request): return render(request, 'simple.html') # 2. Complex custom logic that doesn't fit CBV patterns def complex_workflow(request): if request.method == 'POST': # Complex multi-step logic step = request.POST.get('step') if step == '1': # Process step 1 pass elif step == '2': # Process step 2 pass return render(request, 'workflow.html') # 3. Views that handle multiple models in non-standard ways def dashboard(request): posts = Post.objects.filter(author=request.user) comments = Comment.objects.filter(post__author=request.user) analytics = calculate_analytics(request.user) return render(request, 'dashboard.html', { 'posts': posts, 'comments': comments, 'analytics': analytics }) ``` ## Testing CBVs Comprehensive testing strategies for class-based views. ```python from django.test import TestCase, RequestFactory from django.contrib.auth.models import User, AnonymousUser class PostListViewTest(TestCase): def setUp(self): self.factory = RequestFactory() self.user = User.objects.create_user( username='testuser', password='testpass' ) def test_list_view(self): request = self.factory.get('/posts/') request.user = self.user response = PostListView.as_view()(request) self.assertEqual(response.status_code, 200) def test_queryset_only_published(self): Post.objects.create(title='Published', author=self.user, published=True) Post.objects.create(title='Draft', author=self.user, published=False) request = self.factory.get('/posts/') request.user = self.user response = PostListView.as_view()(request) self.assertEqual(len(response.context_data['posts']), 1) def test_login_required(self): request = self.factory.get('/posts/create/') request.user = AnonymousUser() response = PostCreateView.as_view()(request) self.assertEqual(response.status_code, 302) # Redirect to login def test_author_required(self): other_user = User.objects.create_user('other', password='pass') post = Post.objects.create(title='Test', author=other_user) request = self.factory.get(f'/posts/{post.pk}/edit/') request.user = self.user response = PostUpdateView.as_view()(request, pk=post.pk) self.assertEqual(response.status_code, 302) # Redirect denied ``` ## Advanced Patterns Complex CBV patterns for production applications. ```python # Filtering with GET parameters class PostFilterView(ListView): model = Post paginate_by = 20 def get_queryset(self): queryset = super().get_queryset() author = self.request.GET.get('author') published = self.request.GET.get('published') if author: queryset = queryset.filter(author__name__icontains=author) if published is not None: queryset = queryset.filter(published=published == 'true') return queryset # Dynamic template selection class PostDetailView(DetailView): model = Post def get_template_names(self): if self.request.user == self.object.author: return ['posts/detail_owner.html'] return ['posts/detail.html'] # JSON response view from django.http import JsonResponse class PostJSONView(DetailView): model = Post def render_to_response(self, context, **response_kwargs): return JsonResponse({ 'id': self.object.id, 'title': self.object.title, 'content': self.object.content, 'author': self.object.author.name }) # Conditional form fields class PostCreateView(CreateView): model = Post template_name = 'posts/create.html' def get_form_class(self): if self.request.user.is_staff: return AdminPostForm return UserPostForm # Multiple object types from django.views.generic import TemplateView class SearchView(TemplateView): template_name = 'search.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) query = self.request.GET.get('q', '') if query: context['posts'] = Post.objects.filter(title__icontains=query) context['users'] = User.objects.filter(name__icontains=query) context['query'] = query return context ``` ## Pagination in CBVs Implement sophisticated pagination patterns. ```python from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.views.generic import ListView class PostListView(ListView): model = Post paginate_by = 20 paginate_orphans = 5 # Avoid last page with few items def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Add custom pagination data paginator = context['paginator'] page_obj = context['page_object'] # Calculate page range for display index = page_obj.number - 1 max_index = len(paginator.page_range)
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub