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.

معلومات المصدر

المستودع
TheBushidoCollective/han
آخر نشاط في المصدر
١١ فبراير ٢٠٢٦ في ١٧:٤٧
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١٩٨
التفرعات
٢٢

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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)
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub