如何在Django中对不同目标的相似逻辑视图实现DRY原则?
Django社交网络项目:复用相似视图的方案探讨
现有模型
class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) followers = models.ManyToManyField('self', symmetrical=False, related_name='following') class Post(models.Model): owner = models.ForeignKey(Profile, on_delete=models.CASCADE) content = models.CharField(max_length=255) likes = models.ManyToManyField(Profile, related_name='liked_posts')
现有三个视图的核心逻辑
add_likes、add_followers、edit_post三个视图均遵循以下流程:
- 接收指定结构的JSON请求体
- 验证并解析JSON数据
- 检查目标对象是否存在
- 验证当前用户的操作权限
- 执行对应修改操作(添加/移除关联、更新字段)
- 返回JSON格式的操作结果
各视图的差异点
- add_likes:操作
Post.likes关联,权限要求「当前用户不是帖子所有者」,根据like_status执行添加/移除 - add_followers:操作
Profile.followers关联,权限要求「目标不是自身」,根据follow_status执行添加/移除 - edit_post:更新
Post.content字段,权限要求「当前用户是帖子所有者」,根据new_content修改内容
我尝试的两种方案及顾虑
模型继承方案:
- 思路:创建包含
owner和follower字段的基础Followable模型,让Profile和Post继承该模型(将Post.likes映射为follower),在模型内封装更新逻辑 - 顾虑:未来扩展点赞逻辑(如增加点踩)会受限于继承结构;查询用户关注内容时会同时返回
Post和Profile实例,易引发业务逻辑混淆
- 思路:创建包含
通用函数方案:
- 思路:编写处理步骤2-5的通用函数,根据请求JSON内容分支处理不同操作
- 顾虑:需要大量
if-then分支判断,代码可读性和可维护性下降
推荐的优化方案
方案1:基于Django CBV的通用视图类
利用Django类视图(CBV)特性,抽象通用基类,子类通过配置参数实现具体功能,完全避免重复代码:
from django.views import View from django.http import JsonResponse from django.shortcuts import get_object_or_404 from .models import Profile, Post import json class BaseActionView(View): model = None target_id_field = None permission_check = None action_func = None def post(self, request, *args, **kwargs): # 解析JSON try: data = json.loads(request.body) except json.JSONDecodeError: return JsonResponse({'status': 'fail', 'reason': '无效的JSON数据'}) # 获取目标对象 target_id = data.get(self.target_id_field) if not target_id: return JsonResponse({'status': 'fail', 'reason': f'缺少{self.target_id_field}参数'}) try: target_obj = get_object_or_404(self.model, id=target_id) except ValueError: return JsonResponse({'status': 'fail', 'reason': '无效的ID格式'}) # 权限校验 user_profile = get_object_or_404(Profile, user=request.user) if not self.permission_check(user_profile, target_obj): return JsonResponse({'status': 'fail', 'reason': '无操作权限'}) # 执行操作 try: self.action_func(user_profile, target_obj, data) return JsonResponse({'status': 'success'}) except Exception as e: return JsonResponse({'status': 'fail', 'reason': str(e)}) # 具体视图实现 class AddLikesView(BaseActionView): model = Post target_id_field = 'target_post_id' def permission_check(self, user_profile, target_post): return user_profile != target_post.owner def action_func(self, user_profile, target_post, data): like_status = data.get('like_status') if like_status: target_post.likes.add(user_profile) else: target_post.likes.remove(user_profile) class AddFollowersView(BaseActionView): model = Profile target_id_field = 'target_profile_id' def permission_check(self, user_profile, target_profile): return user_profile != target_profile def action_func(self, user_profile, target_profile, data): follow_status = data.get('follow_status') if follow_status: target_profile.followers.add(user_profile) else: target_profile.followers.remove(user_profile) class EditPostView(BaseActionView): model = Post target_id_field = 'target_post_id' def permission_check(self, user_profile, target_post): return user_profile == target_post.owner def action_func(self, user_profile, target_post, data): new_content = data.get('new_content') if new_content: target_post.content = new_content target_post.save() else: raise ValueError('缺少new_content参数')
优点:
- 通用逻辑完全复用,子类仅需配置参数和实现差异部分
- 扩展性强,新增类似视图只需继承基类并补充少量代码
- 结构清晰,每个视图职责明确
方案2:带配置参数的通用函数
若偏好函数视图(FBV),可通过传递配置字典替代分支判断:
from django.http import JsonResponse from django.shortcuts import get_object_or_404 from .models import Profile, Post import json def generic_action_view(request, config): # 解析JSON try: data = json.loads(request.body) except json.JSONDecodeError: return JsonResponse({'status': 'fail', 'reason': '无效的JSON数据'}) # 获取目标对象 target_id = data.get(config['target_id_field']) if not target_id: return JsonResponse({'status': 'fail', 'reason': f'缺少{config["target_id_field"]}参数'}) try: target_obj = get_object_or_404(config['model'], id=target_id) except ValueError: return JsonResponse({'status': 'fail', 'reason': '无效的ID格式'}) # 权限校验 user_profile = get_object_or_404(Profile, user=request.user) if not config['permission_check'](user_profile, target_obj): return JsonResponse({'status': 'fail', 'reason': '无操作权限'}) # 执行操作 try: config['action_func'](user_profile, target_obj, data) return JsonResponse({'status': 'success'}) except Exception as e: return JsonResponse({'status': 'fail', 'reason': str(e)}) # 各视图的配置与调用 def add_likes(request): config = { 'model': Post, 'target_id_field': 'target_post_id', 'permission_check': lambda up, tp: up != tp.owner, 'action_func': lambda up, tp, d: tp.likes.add(up) if d.get('like_status') else tp.likes.remove(up) } return generic_action_view(request, config) def add_followers(request): config = { 'model': Profile, 'target_id_field': 'target_profile_id', 'permission_check': lambda up, tp: up != tp, 'action_func': lambda up, tp, d: tp.followers.add(up) if d.get('follow_status') else tp.followers.remove(up) } return generic_action_view(request, config) def edit_post(request): def edit_action(up, tp, d): new_content = d.get('new_content') if not new_content: raise ValueError('缺少new_content参数') tp.content = new_content tp.save() config = { 'model': Post, 'target_id_field': 'target_post_id', 'permission_check': lambda up, tp: up == tp.owner, 'action_func': edit_action } return generic_action_view(request, config)
优点:
- 复用通用逻辑,通过配置参数替代分支判断
- 代码简洁,各视图的差异点集中在配置中,易于维护
内容的提问来源于stack exchange,提问作者Jadhen
相关产品推荐
相关产品推荐

