Token导航 LogoToken导航

Agent Skill · 待分类

django-cbv-patterns(Django CBV 模式)

用于辅助 Python 项目开发、测试、依赖管理和常见框架工作流。它适合让 Agent 阅读 Python 代码、定位测试问题、整理运行命令、生成脚本或分析数据处理逻辑。使用时需要确认项目虚拟环境、依赖版本和测试入口;涉及执行脚本、读写文件、访问数据库或调用外部 API 时,应先明确运行目录和输入输出范围,避免误改生产数据。

django-cbv-patterns 是待分类类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描有未通过项,按安装量在待分类类的 11,209 个 Skill 里排第 7,324。

总安装 372周安装 16GitHub Stars 142下载 131核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:django-cbv-patterns(Django CBV 模式)
来源仓库:https://github.com/thebushidocollective/han
仓库路径:skills/django-cbv-patterns
安装命令:
npx skills add https://github.com/thebushidocollective/han --skill django-cbv-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/thebushidocollective/han --skill django-cbv-patterns

这个 Skill 是做什么的

这个 Skill 的分类还没最终确定,用法以来源仓库的说明为准。

专注于 Django 类视图(CBV)的设计模式和最佳实践。提供通用视图的使用指南,如 ListView、DetailView 和 CreateView。帮助构建模块化、可复用的视图逻辑,实现关注点分离。安装方式:github,通过 npx skills add 命令添加。

适用于需要复杂视图逻辑的中大型 Django 项目。

来源仓库内路径:skills/django-cbv-patterns

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/thebushidocollective/han --skill django-cbv-pa…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

django-cbv-patterns 怎么安装?

在终端执行 npx skills add https://github.com/thebushidocollective/han --skill django-cbv-patterns。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

django-cbv-patterns 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

django-cbv-patterns 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

django-cbv-patterns 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

django-cbv-patterns 用的人多吗?

公开数据是累计安装 372 次,近一周安装 16 次,下载 131 次,GitHub 142 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

django-cbv-patterns 安全吗?

第三方扫描有未通过项,风险较高。除非你确认过源码,否则不建议直接安装到常用环境。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。

先想清楚的地方

  • 第三方扫描存在提醒项或未通过项,需要人工逐条确认。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理待分类类任务、又不想每次都手写提示词的人。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

待分类类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
infsh-cli

infsh CLI · Codex、Claude

4,374,576382权限未标注未明确未收录
ai-video-generation

AI 视频生成 · Codex、Claude

4,313,376382调用外部服务未明确有提醒
lark-openapi-explorer

云雀 OpenAPI 浏览器 · Codex、Claude

2,064,1209,011需要联网未明确有提醒
lark-workflow-standup-report

Lark 工作流程站立报告 · Codex、Claude

2,041,6328,978需要联网未明确通过
use-my-browser

使用我的浏览器 · Codex、Claude

1,848,49140操作浏览器未明确未通过
django-cbv-patterns

Django CBV 模式 · 本页

372142需要联网MIT未通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

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.

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.

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.

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.

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

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.

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

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.

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

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)
        start_index = index - 3 if index >= 3 else 0
        end_index = index + 4 if index <= max_index - 4 else max_index

        context['page_range'] = list(paginator.page_range)[start_index:end_index]
        context['total_pages'] = paginator.num_pages

        return context

# AJAX pagination
class AjaxPostListView(ListView):
    model = Post
    paginate_by = 10
    template_name = 'posts/list.html'

    def get_template_names(self):
        if self.request.is_ajax():
            return ['posts/partials/post_list.html']
        return [self.template_name]

    def render_to_response(self, context, **response_kwargs):
        if self.request.is_ajax():
            from django.http import JsonResponse
            posts = [
                {
                    'id': post.id,
                    'title': post.title,
                    'author': post.author.name
                }
                for post in context['object_list']
            ]
            return JsonResponse({
                'posts': posts,
                'has_next': context['page_obj'].has_next(),
                'page': context['page_obj'].number
            })
        return super().render_to_response(context, **response_kwargs)

# Infinite scroll pagination
class InfiniteScrollListView(ListView):
    model = Post
    paginate_by = 20

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['is_infinite_scroll'] = True
        return context

Context Data Manipulation

Master advanced context manipulation techniques.

class PostDetailView(DetailView):
    model = Post

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        # Add related data
        post = self.object
        context['related_posts'] = Post.objects.filter(
            category=post.category
        ).exclude(id=post.id)[:5]

        # Add user-specific data
        if self.request.user.is_authenticated:
            context['has_liked'] = post.likes.filter(
                user=self.request.user
            ).exists()
            context['is_bookmarked'] = post.bookmarks.filter(
                user=self.request.user
            ).exists()

        # Add computed data
        context['reading_time'] = post.calculate_reading_time()
        context['share_url'] = self.request.build_absolute_uri()

        return context

# Multiple context mixins
class AnalyticsMixin:
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['analytics_enabled'] = True
        context['tracking_id'] = settings.ANALYTICS_ID
        return context

class BreadcrumbMixin:
    breadcrumbs = []

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['breadcrumbs'] = self.get_breadcrumbs()
        return context

    def get_breadcrumbs(self):
        return self.breadcrumbs

class PostDetailView(AnalyticsMixin, BreadcrumbMixin, DetailView):
    model = Post
    breadcrumbs = [
        ('Home', '/'),
        ('Posts', '/posts/'),
    ]

    def get_breadcrumbs(self):
        breadcrumbs = super().get_breadcrumbs()
        breadcrumbs.append((self.object.title, None))
        return breadcrumbs

Method Override Patterns

Override specific methods for fine-grained control.

class PostCreateView(CreateView):
    model = Post
    fields = ['title', 'content', 'category']

    # Control initial form data
    def get_initial(self):
        initial = super().get_initial()
        if self.request.user.is_authenticated:
            initial['author'] = self.request.user
        return initial

    # Control form kwargs
    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['user'] = self.request.user
        kwargs['categories'] = Category.objects.filter(active=True)
        return kwargs

    # Control form class selection
    def get_form_class(self):
        if self.request.user.is_staff:
            return AdminPostForm
        return PostForm

    # Control success URL dynamically
    def get_success_url(self):
        if 'save_and_add' in self.request.POST:
            return reverse('post-create')
        return reverse('post-detail', kwargs={'pk': self.object.pk})

    # Customize form validation
    def form_valid(self, form):
        form.instance.author = self.request.user
        form.instance.ip_address = self.request.META.get('REMOTE_ADDR')

        # Additional validation
        if form.instance.published and not form.instance.content:
            form.add_error('content', 'Published posts must have content')
            return self.form_invalid(form)

        response = super().form_valid(form)

        # Post-save actions
        messages.success(self.request, 'Post created successfully')

        return response

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

# Override get_object for custom logic
class PostUpdateView(UpdateView):
    model = Post

    def get_object(self, queryset=None):
        obj = super().get_object(queryset)

        # Track view
        obj.views += 1
        obj.save(update_fields=['views'])

        # Check permissions
        if obj.author != self.request.user and not self.request.user.is_staff:
            raise PermissionDenied('You can only edit your own posts')

        return obj

    def get_queryset(self):
        queryset = super().get_queryset()

        # Filter based on user
        if not self.request.user.is_staff:
            queryset = queryset.filter(author=self.request.user)

        # Optimize queries
        queryset = queryset.select_related('author', 'category')

        return queryset

Advanced Mixin Composition

Build complex functionality through mixin composition.

from django.contrib import messages
from django.shortcuts import redirect
from django.core.exceptions import PermissionDenied

class SetHeadlineMixin:
    """Add a headline to the context."""
    headline = None

    def get_headline(self):
        return self.headline

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['headline'] = self.get_headline()
        return context

class SetButtonTextMixin:
    """Add button text to the context."""
    button_text = 'Submit'

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

class FormValidMessageMixin:
    """Display success message after form submission."""
    success_message = 'Form submitted successfully'

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

    def get_success_message(self, form):
        return self.success_message

class DeleteConfirmMixin:
    """Require confirmation before deletion."""
    def delete(self, request, *args, **kwargs):
        if not request.POST.get('confirm'):
            messages.warning(request, 'Please confirm deletion')
            return redirect(self.get_success_url())

        messages.success(request, 'Item deleted successfully')
        return super().delete(request, *args, **kwargs)

class StaffRequiredMixin:
    """Require staff user."""
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_staff:
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)

class AuditMixin:
    """Track creation and updates."""
    def form_valid(self, form):
        if not form.instance.pk:
            form.instance.created_by = self.request.user
        form.instance.updated_by = self.request.user
        return super().form_valid(form)

# Compose multiple mixins
class PostCreateView(
    LoginRequiredMixin,
    SetHeadlineMixin,
    SetButtonTextMixin,
    FormValidMessageMixin,
    AuditMixin,
    CreateView
):
    model = Post
    fields = ['title', 'content']
    headline = 'Create New Post'
    button_text = 'Create Post'
    success_message = 'Post created successfully!'

Search and Filter Views

Implement advanced search and filtering.

from django.db.models import Q
from django.views.generic import ListView

class PostSearchView(ListView):
    model = Post
    template_name = 'posts/search.html'
    paginate_by = 20

    def get_queryset(self):
        queryset = super().get_queryset()
        query = self.request.GET.get('q')

        if query:
            queryset = queryset.filter(
                Q(title__icontains=query) |
                Q(content__icontains=query) |
                Q(author__name__icontains=query)
            )

        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['query'] = self.request.GET.get('q', '')
        context['result_count'] = context['paginator'].count
        return context

class PostFilterView(ListView):
    model = Post
    template_name = 'posts/filter.html'
    paginate_by = 20

    def get_queryset(self):
        queryset = super().get_queryset()

        # Category filter
        category = self.request.GET.get('category')
        if category:
            queryset = queryset.filter(category_id=category)

        # Author filter
        author = self.request.GET.get('author')
        if author:
            queryset = queryset.filter(author_id=author)

        # Date range filter
        date_from = self.request.GET.get('date_from')
        date_to = self.request.GET.get('date_to')
        if date_from:
            queryset = queryset.filter(created_at__gte=date_from)
        if date_to:
            queryset = queryset.filter(created_at__lte=date_to)

        # Published filter
        published = self.request.GET.get('published')
        if published is not None:
            queryset = queryset.filter(published=published == 'true')

        # Sorting
        sort = self.request.GET.get('sort', '-created_at')
        allowed_sorts = ['created_at', '-created_at', 'title', '-title', 'views', '-views']
        if sort in allowed_sorts:
            queryset = queryset.order_by(sort)

        return queryset.select_related('author', 'category')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['categories'] = Category.objects.all()
        context['authors'] = User.objects.filter(posts__isnull=False).distinct()
        context['filters'] = self.request.GET
        return context

File Upload Views

Handle file uploads with CBVs.

from django.views.generic.edit import FormView
from django.core.files.storage import default_storage

class FileUploadView(FormView):
    template_name = 'upload.html'
    form_class = FileUploadForm
    success_url = '/success/'

    def form_valid(self, form):
        file = form.cleaned_data['file']

        # Save file
        filename = default_storage.save(f'uploads/{file.name}', file)

        # Process file
        self.process_file(filename)

        messages.success(self.request, f'File {file.name} uploaded successfully')
        return super().form_valid(form)

    def process_file(self, filename):
        # Custom file processing logic
        pass

class MultipleFileUploadView(FormView):
    template_name = 'upload_multiple.html'
    form_class = MultipleFileUploadForm
    success_url = '/success/'

    def form_valid(self, form):
        files = self.request.FILES.getlist('files')

        for file in files:
            # Validate file
            if file.size > 10 * 1024 * 1024:  # 10MB limit
                form.add_error('files', f'{file.name} exceeds size limit')
                return self.form_invalid(form)

            # Save file
            filename = default_storage.save(f'uploads/{file.name}', file)

            # Create database record
            FileUpload.objects.create(
                filename=filename,
                original_name=file.name,
                size=file.size,
                uploaded_by=self.request.user
            )

        messages.success(self.request, f'{len(files)} files uploaded successfully')
        return super().form_valid(form)

class ImageUploadView(CreateView):
    model = Image
    fields = ['title', 'image', 'description']
    template_name = 'images/upload.html'

    def form_valid(self, form):
        form.instance.uploaded_by = self.request.user

        # Validate image
        image = form.cleaned_data['image']
        if image.size > 5 * 1024 * 1024:  # 5MB
            form.add_error('image', 'Image too large')
            return self.form_invalid(form)

        # Process image (resize, thumbnail, etc.)
        form.instance.thumbnail = self.create_thumbnail(image)

        return super().form_valid(form)

    def create_thumbnail(self, image):
        # Thumbnail creation logic
        pass

Performance Optimization

Optimize CBVs for better performance.

class OptimizedPostListView(ListView):
    model = Post
    paginate_by = 20

    def get_queryset(self):
        return Post.objects.select_related(
            'author'
        ).prefetch_related(
            'comments'
        ).only(
            'id', 'title', 'created_at', 'author__name'
        ).filter(
            published=True
        )

# Caching
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator

@method_decorator(cache_page(60 * 15), name='dispatch')
class CachedPostListView(ListView):
    model = Post

# Conditional caching based on user
class SmartCachedView(ListView):
    model = Post

    @method_decorator(cache_page(60 * 15))
    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated:
            # Don't cache for authenticated users
            return super().dispatch(request, *args, **kwargs)
        return super().dispatch(request, *args, **kwargs)

# ETags for caching
from django.views.decorators.http import condition

def latest_post(request, *args, **kwargs):
    return Post.objects.latest('updated_at').updated_at

def post_etag(request, *args, **kwargs):
    return str(Post.objects.latest('updated_at').updated_at.timestamp())

class PostListView(ListView):
    model = Post

    @method_decorator(condition(etag_func=post_etag, last_modified_func=latest_post))
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

When to Use This Skill

Use django-cbv-patterns when building modern, production-ready applications that require advanced patterns, best practices, and optimal performance.

API Views with CBVs

Build API endpoints using CBVs without DRF.

from django.http import JsonResponse
from django.views.generic import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
import json

@method_decorator(csrf_exempt, name='dispatch')
class PostAPIView(View):
    def get(self, request, *args, **kwargs):
        posts = Post.objects.filter(published=True).values(
            'id', 'title', 'content', 'author__name'
        )
        return JsonResponse(list(posts), safe=False)

    def post(self, request, *args, **kwargs):
        try:
            data = json.loads(request.body)
            post = Post.objects.create(
                title=data['title'],
                content=data['content'],
                author=request.user
            )
            return JsonResponse({
                'id': post.id,
                'title': post.title,
                'message': 'Post created successfully'
            }, status=201)
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=400)

class PostDetailAPIView(View):
    def get(self, request, pk, *args, **kwargs):
        try:
            post = Post.objects.select_related('author').get(pk=pk)
            return JsonResponse({
                'id': post.id,
                'title': post.title,
                'content': post.content,
                'author': post.author.name,
                'created_at': post.created_at.isoformat()
            })
        except Post.DoesNotExist:
            return JsonResponse({'error': 'Post not found'}, status=404)

    def put(self, request, pk, *args, **kwargs):
        try:
            post = Post.objects.get(pk=pk)
            data = json.loads(request.body)

            post.title = data.get('title', post.title)
            post.content = data.get('content', post.content)
            post.save()

            return JsonResponse({'message': 'Post updated successfully'})
        except Post.DoesNotExist:
            return JsonResponse({'error': 'Post not found'}, status=404)

    def delete(self, request, pk, *args, **kwargs):
        try:
            post = Post.objects.get(pk=pk)
            post.delete()
            return JsonResponse({'message': 'Post deleted successfully'})
        except Post.DoesNotExist:
            return JsonResponse({'error': 'Post not found'}, status=404)

Wizard and Multi-Step Forms

Implement multi-step form wizards with CBVs.

from django.views.generic import TemplateView
from django.shortcuts import redirect
from django.urls import reverse

class MultiStepFormMixin:
    """Mixin for multi-step form handling."""

    def get_step(self):
        return int(self.request.GET.get('step', 1))

    def get_session_key(self, step):
        return f'form_data_step_{step}'

    def save_step_data(self, step, data):
        self.request.session[self.get_session_key(step)] = data

    def get_step_data(self, step):
        return self.request.session.get(self.get_session_key(step), {})

    def clear_wizard_data(self):
        for key in list(self.request.session.keys()):
            if key.startswith('form_data_step_'):
                del self.request.session[key]

class UserRegistrationWizard(MultiStepFormMixin, TemplateView):
    template_name = 'registration/wizard.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        step = self.get_step()

        if step == 1:
            context['form'] = UserBasicInfoForm(initial=self.get_step_data(1))
        elif step == 2:
            context['form'] = UserProfileForm(initial=self.get_step_data(2))
        elif step == 3:
            context['form'] = UserPreferencesForm(initial=self.get_step_data(3))

        context['step'] = step
        context['total_steps'] = 3
        return context

    def post(self, request, *args, **kwargs):
        step = self.get_step()

        if step == 1:
            form = UserBasicInfoForm(request.POST)
            if form.is_valid():
                self.save_step_data(1, form.cleaned_data)
                return redirect(f'{reverse("registration-wizard")}?step=2')

        elif step == 2:
            form = UserProfileForm(request.POST)
            if form.is_valid():
                self.save_step_data(2, form.cleaned_data)
                return redirect(f'{reverse("registration-wizard")}?step=3')

        elif step == 3:
            form = UserPreferencesForm(request.POST)
            if form.is_valid():
                self.save_step_data(3, form.cleaned_data)

                # Create user with all data
                self.create_user()
                self.clear_wizard_data()

                return redirect('registration-complete')

        return self.render_to_response(self.get_context_data(form=form))

    def create_user(self):
        data1 = self.get_step_data(1)
        data2 = self.get_step_data(2)
        data3 = self.get_step_data(3)

        user = User.objects.create_user(
            username=data1['username'],
            email=data1['email'],
            password=data1['password']
        )

        Profile.objects.create(
            user=user,
            bio=data2['bio'],
            avatar=data2['avatar']
        )

        Preferences.objects.create(
            user=user,
            notifications=data3['notifications'],
            privacy=data3['privacy']
        )

Redirect and Success URL Patterns

Master URL redirection strategies.

from django.urls import reverse, reverse_lazy
from django.views.generic import CreateView, UpdateView

class PostCreateView(CreateView):
    model = Post
    fields = ['title', 'content']

    # Static success URL
    success_url = reverse_lazy('post-list')

    # Dynamic success URL based on object
    def get_success_url(self):
        return reverse('post-detail', kwargs={'pk': self.object.pk})

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

    # Success URL based on form submission button
    def get_success_url(self):
        if 'save_and_continue' in self.request.POST:
            return reverse('post-update', kwargs={'pk': self.object.pk})
        elif 'save_and_add' in self.request.POST:
            return reverse('post-create')
        else:
            return reverse('post-detail', kwargs={'pk': self.object.pk})

class FlexibleRedirectMixin:
    """Redirect to next parameter or default."""

    def get_success_url(self):
        next_url = self.request.GET.get('next') or self.request.POST.get('next')
        if next_url:
            return next_url
        return super().get_success_url()

class PostDeleteView(FlexibleRedirectMixin, DeleteView):
    model = Post
    success_url = reverse_lazy('post-list')

Template and Response Customization

Customize template selection and response rendering.

from django.views.generic import DetailView
from django.http import HttpResponse
from django.template.loader import render_to_string

class PostDetailView(DetailView):
    model = Post

    # Dynamic template selection
    def get_template_names(self):
        # Mobile template
        if self.request.user_agent.is_mobile:
            return ['posts/detail_mobile.html']

        # Owner template
        if self.request.user == self.object.author:
            return ['posts/detail_owner.html']

        # Default template
        return ['posts/detail.html']

class ExportMixin:
    """Add export functionality to views."""

    def render_to_response(self, context, **response_kwargs):
        export_format = self.request.GET.get('format')

        if export_format == 'pdf':
            return self.render_to_pdf(context)
        elif export_format == 'csv':
            return self.render_to_csv(context)
        elif export_format == 'json':
            return self.render_to_json(context)

        return super().render_to_response(context, **response_kwargs)

    def render_to_pdf(self, context):
        # PDF rendering logic
        html = render_to_string(self.template_name, context)
        # Convert to PDF
        return HttpResponse(pdf_content, content_type='application/pdf')

    def render_to_csv(self, context):
        import csv
        from io import StringIO

        output = StringIO()
        writer = csv.writer(output)

        # Write CSV data
        for obj in context['object_list']:
            writer.writerow([obj.id, obj.title, obj.author.name])

        response = HttpResponse(output.getvalue(), content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="export.csv"'
        return response

    def render_to_json(self, context):
        from django.http import JsonResponse
        data = [
            {
                'id': obj.id,
                'title': obj.title,
                'author': obj.author.name
            }
            for obj in context['object_list']
        ]
        return JsonResponse(data, safe=False)

class PostListView(ExportMixin, ListView):
    model = Post

Advanced Testing Patterns

Write comprehensive tests for CBVs.

from django.test import TestCase, RequestFactory, Client
from django.contrib.auth.models import User, AnonymousUser
from django.urls import reverse

class PostViewTestCase(TestCase):
    def setUp(self):
        self.factory = RequestFactory()
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass'
        )
        self.post = Post.objects.create(
            title='Test Post',
            author=self.user
        )

    def test_list_view_with_factory(self):
        """Test using RequestFactory."""
        request = self.factory.get('/posts/')
        request.user = self.user

        response = PostListView.as_view()(request)
        self.assertEqual(response.status_code, 200)

    def test_detail_view_with_client(self):
        """Test using Client."""
        client = Client()
        response = client.get(reverse('post-detail', kwargs={'pk': self.post.pk}))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Test Post')

    def test_create_view_requires_login(self):
        """Test login requirement."""
        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_update_view_author_only(self):
        """Test author-only access."""
        other_user = User.objects.create_user('other', password='pass')
        request = self.factory.get(f'/posts/{self.post.pk}/edit/')
        request.user = other_user

        with self.assertRaises(PermissionDenied):
            PostUpdateView.as_view()(request, pk=self.post.pk)

    def test_context_data(self):
        """Test context data."""
        request = self.factory.get('/posts/')
        request.user = self.user

        view = PostListView()
        view.request = request
        view.object_list = Post.objects.all()

        context = view.get_context_data()
        self.assertIn('object_list', context)
        self.assertIn('view', context)

    def test_form_valid(self):
        """Test form submission."""
        data = {
            'title': 'New Post',
            'content': 'Test content'
        }
        request = self.factory.post('/posts/create/', data)
        request.user = self.user

        response = PostCreateView.as_view()(request)
        self.assertEqual(response.status_code, 302)  # Redirect after success
        self.assertTrue(Post.objects.filter(title='New Post').exists())

    def test_queryset_optimization(self):
        """Test query optimization."""
        with self.assertNumQueries(1):
            request = self.factory.get('/posts/')
            request.user = self.user
            response = OptimizedPostListView.as_view()(request)
            list(response.context_data['object_list'])

Django CBV Best Practices

  1. Follow MRO carefully - Order mixins correctly: permission mixins first, then functionality mixins, base view last
  2. Use built-in mixins - Leverage LoginRequiredMixin, UserPassesTestMixin instead of writing custom permission logic
  3. Override get_queryset() - Customize querysets in get_queryset(), not in the class attribute
  4. Use get_context_data() - Add extra context properly by calling super() first
  5. Keep views focused - Each view should have a single responsibility
  6. Leverage generic views - Use built-in generic views for CRUD operations
  7. Create custom mixins - Extract reusable functionality into mixins
  8. Use get_form_kwargs() - Pass additional data to forms through get_form_kwargs()
  9. Optimize queries - Use select_related and prefetch_related in get_queryset()
  10. Test thoroughly - Use RequestFactory for unit testing views
  11. Use success_url wisely - Prefer get_success_url() for dynamic URLs
  12. Handle AJAX requests - Check request.is_ajax() and return appropriate responses
  13. Implement proper pagination - Always paginate large querysets
  14. Cache where appropriate - Use method decorators for caching expensive views
  15. Document mixin order - Comment why mixins are ordered a certain way

Django CBV Common Pitfalls

  1. Wrong mixin order - Incorrect MRO causes mixins to not work or override each other incorrectly
  2. Not calling super() - Forgetting super() breaks the inheritance chain
  3. Hardcoded querysets - Defining queryset as class attribute instead of using get_queryset()
  4. Overusing CBVs - Using CBVs for simple views that would be clearer as functions
  5. Not understanding dispatch() - Misusing dispatch() method leads to unexpected behavior
  6. Ignoring context_object_name - Templates are less readable without proper context names
  7. Mixing concerns - Putting too much logic in views instead of models or forms
  8. Not optimizing queries - N+1 problems from not using select_related/prefetch_related
  9. Testing with client only - Not unit testing with RequestFactory
  10. Complex inheritance chains - Too many mixins make code hard to understand and debug
  11. Forgetting CSRF protection - Disabling CSRF without understanding security implications
  12. Not handling exceptions - Not catching DoesNotExist or PermissionDenied in custom methods
  13. Incorrect success_url usage - Using reverse() instead of reverse_lazy() in class attributes
  14. Template name conflicts - Not setting explicit template_name when needed
  15. Missing get_object() customization - Not customizing get_object() for permission checks

Resources

平台分布

OpenCode

32.96%
按下载量换算43

Codex

22.9%
按下载量换算30

Claude Code

17.53%
按下载量换算23

windsurf

13.02%
按下载量换算17

Antigravity

7.83%
按下载量换算10

Gemini CLI

3.62%
按下载量换算5

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在提醒或未通过项,安装前请自行复核。

继续浏览同类 Skills