1. Django视图:FBV与CBV深度解析
在Django开发中,视图(View)是处理HTTP请求并返回响应的核心组件。经过多年实战,我发现开发者对视图编写方式的选择直接影响项目的可维护性和扩展性。下面我将结合生产经验,详细剖析两种视图编写范式。
1.1 FBV:函数式编程的简洁之美
FBV(Function-Based Views)是Django最原始的视图编写方式。最近在重构一个老项目时,我再次体会到FBV的独特价值。
1.1.1 基础实现模式
典型的FBV包含三个关键要素:
python复制from django.http import HttpResponse
from django.shortcuts import render
def article_detail(request, article_id):
# 业务逻辑处理
article = get_object_or_404(Article, pk=article_id)
# 上下文准备
context = {
'article': article,
'related_articles': article.get_related(),
}
# 响应返回
return render(request, 'blog/detail.html', context)
实战经验:
- 保持函数单一职责,超过100行代码就该考虑拆分
- 使用
get_object_or_404替代直接查询,自动处理404场景 - 上下文变量命名要有语义,避免使用通用名如
object
1.1.2 参数处理进阶技巧
URL参数和查询参数的组合处理是常见需求:
python复制def article_archive(request, year, month=None):
articles = Article.objects.filter(
pub_date__year=year,
pub_date__month=month
) if month else Article.objects.filter(pub_date__year=year)
# 处理分页
paginator = Paginator(articles, 10)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'blog/archive.html', {'page_obj': page_obj})
踩坑记录:
- 查询参数必须做类型验证和默认值处理
- 分页参数建议使用Django内置的Paginator类
- 复杂查询应考虑使用Q对象组合条件
1.1.3 装饰器的艺术
装饰器链可以优雅地实现横切关注点:
python复制@cache_page(60 * 15)
@login_required
@require_http_methods(['GET'])
def premium_content(request, content_id):
content = get_premium_content(content_id)
if not request.user.has_premium_access():
raise PermissionDenied
return render(...)
性能优化点:
- 装饰器顺序影响执行流程,安全相关应放在最外层
- 缓存装饰器要合理设置超时时间
- 生产环境建议使用更健壮的缓存后端如Redis
1.2 CBV:面向对象的强大抽象
在电商项目开发中,CBV(Class-Based Views)帮我们减少了70%的重复代码。下面分享我的实战心得。
1.2.1 核心类继承体系
Django CBV的类继承结构值得深入研究:
code复制View (基础视图)
│
├── TemplateView (模板视图)
├── RedirectView (重定向视图)
│
└── Generic display views (展示视图)
├── DetailView
└── ListView
│
└── Generic edit views (编辑视图)
├── FormView
├── CreateView
├── UpdateView
└── DeleteView
设计启示:
- 通过Mixin模式实现功能组合
- 每个类职责单一,符合SOLID原则
- 方法覆盖比条件判断更清晰
1.2.2 通用视图实战
商品列表页的典型实现:
python复制class ProductListView(ListView):
model = Product
template_name = 'shop/product_list.html'
context_object_name = 'products'
paginate_by = 20
def get_queryset(self):
# 基础查询集
qs = super().get_queryset().filter(is_active=True)
# 添加分类过滤
category_slug = self.kwargs.get('category')
if c
