You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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三个视图均遵循以下流程:

  1. 接收指定结构的JSON请求体
  2. 验证并解析JSON数据
  3. 检查目标对象是否存在
  4. 验证当前用户的操作权限
  5. 执行对应修改操作(添加/移除关联、更新字段)
  6. 返回JSON格式的操作结果

各视图的差异点

  • add_likes:操作Post.likes关联,权限要求「当前用户不是帖子所有者」,根据like_status执行添加/移除
  • add_followers:操作Profile.followers关联,权限要求「目标不是自身」,根据follow_status执行添加/移除
  • edit_post:更新Post.content字段,权限要求「当前用户是帖子所有者」,根据new_content修改内容

我尝试的两种方案及顾虑

  1. 模型继承方案:

    • 思路:创建包含owner和follower字段的基础Followable模型,让Profile和Post继承该模型(将Post.likes映射为follower),在模型内封装更新逻辑
    • 顾虑:未来扩展点赞逻辑(如增加点踩)会受限于继承结构;查询用户关注内容时会同时返回Post和Profile实例,易引发业务逻辑混淆
  2. 通用函数方案:

    • 思路:编写处理步骤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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 20:36:09