0%

Django-DRF-分层架构

Django DRF 分层架构最佳实践

Django DRF 分层架构最佳实践(ViewSet + Service + 统一Response格式)

目录结构

apps/article/
├── views.py # 接口层(ViewSet)
├── services.py # 业务逻辑层
├── serializers.py # 序列化器
├── response.py # 统一返回格式封装
├── urls.py # URL注册
├── models.py # 模型(假定已存在)

response.py — 统一Response返回格式

from rest_framework.response import Response  # 导入DRF的Response对象

# 统一返回格式类
class R:
@staticmethod
def success(data=None, msg='success', code=200): # 成功响应
return Response({ # 返回格式化Response
'code': code, # 状态码
'msg': msg, # 提示信息
'data': data # 返回数据
}, status=200) # HTTP 200状态

@staticmethod
def fail(msg='error', code=400, data=None): # 失败响应
return Response({ # 返回格式化Response
'code': code, # 状态码
'msg': msg, # 错误信息
'data': data # 错误数据
}, status=400) # HTTP 400状态

@staticmethod
def paginate(paginator): # 分页数据封装
return {
'count': paginator.paginator.count, # 总条数
'page': paginator.number, # 当前页
'page_size': paginator.paginator.per_page, # 每页数量
'results': list(paginator) # 当前页数据列表
}

services.py — 业务逻辑层

from .models import Article, Income, Advertisement, Category, ArticleBlock  # 导入模型
from django.db.models import Q # 导入Q表达式用于复杂查询

# 文章业务逻辑服务
class ArticleService:
@staticmethod
def get_filtered_articles(params): # 获取筛选后的文章列表
queryset = Article.objects.select_related('category') # 查询文章并关联分类

if params.get('category'): # 分类筛选
queryset = queryset.filter(category_id=params['category'])

if params.get('search'): # 搜索关键词筛选
search = params['search'] # 获取search参数
queryset = queryset.filter( # 多字段模糊搜索
Q(title__icontains=search) | # 标题包含关键词
Q(description__icontains=search) # 描述包含关键词
)

tag = params.get('tag') # 获取tag参数
if tag == 'free': # 免费筛选
queryset = queryset.filter(is_free=True)
elif tag == 'vip_free': # VIP免费筛选
queryset = queryset.filter(is_vip_free=True)
elif tag == 'm_series': # 支持M系列筛选
queryset = queryset.filter(support_m_series=True)

return queryset.order_by('-created_at') # 按创建时间倒序

# 收入业务逻辑服务
class IncomeService:
@staticmethod
def get_user_incomes(user, income_type=None): # 获取用户收入记录
queryset = Income.objects.filter(user=user) # 筛选当前用户收入
if income_type: # 按收入类型过滤
queryset = queryset.filter(income_type=income_type)
return queryset

# 广告业务逻辑服务
class AdvertisementService:
@staticmethod
def list_ads(): # 获取广告列表
return Advertisement.objects.all()

# 分类业务逻辑服务
class CategoryService:
@staticmethod
def list_categories(): # 获取启用分类
return Category.objects.filter(is_active=True).order_by('order', 'name')

# 文章区块业务逻辑服务
class ArticleBlockService:
@staticmethod
def list_blocks(article_id=None): # 获取文章区块
queryset = ArticleBlock.objects.all() # 查询所有区块
if article_id: # 按文章ID过滤
queryset = queryset.filter(article_id=article_id)
return queryset.order_by('order') # 按顺序排序

serializers.py — 序列化器

from rest_framework import serializers  # 导入DRF序列化器
from .models import Article, Income, Advertisement, Category, ArticleBlock # 导入模型

# 文章列表序列化器
class ArticleListSerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.name', read_only=True) # 显示分类名称

class Meta:
model = Article # 绑定模型
fields = ['id', 'title', 'category_name', 'created_at'] # 序列化字段

# 文章详情序列化器
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article # 绑定模型
fields = '__all__' # 全部字段

# 收入序列化器
class IncomeSerializer(serializers.ModelSerializer):
class Meta:
model = Income # 绑定模型
fields = '__all__' # 全部字段

# 广告序列化器
class AdvertisementSerializer(serializers.ModelSerializer):
class Meta:
model = Advertisement # 绑定模型
fields = '__all__'

# 分类序列化器
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category # 绑定模型
fields = '__all__'

# 文章区块序列化器
class ArticleBlockSerializer(serializers.ModelSerializer):
class Meta:
model = ArticleBlock # 绑定模型
fields = '__all__'

views.py — 接口层(ViewSet)

from rest_framework import viewsets, permissions  # 导入DRF视图集与权限
from django.core.paginator import Paginator # Django分页器
from .serializers import ( # 导入序列化器
ArticleSerializer, ArticleListSerializer, IncomeSerializer,
AdvertisementSerializer, CategorySerializer, ArticleBlockSerializer
)
from .services import ( # 导入服务层
ArticleService, IncomeService,
AdvertisementService, CategoryService, ArticleBlockService
)
from .response import R # 导入统一返回格式
from django.shortcuts import get_object_or_404 # 获取对象或返回404

# 文章接口视图集
class ArticleViewSet(viewsets.ViewSet):
permission_classes = [permissions.AllowAny] # 所有人可访问

def list(self, request): # 文章列表接口
queryset = ArticleService.get_filtered_articles(request.query_params) # 调用服务获取数据
page = int(request.GET.get('page', 1)) # 获取页码
size = int(request.GET.get('page_size', 10)) # 获取每页数量
paginator = Paginator(queryset, size) # 创建分页器
page_obj = paginator.get_page(page) # 获取当前页对象

serializer = ArticleListSerializer(page_obj, many=True) # 序列化数据
data = R.paginate(page_obj) # 格式化分页数据
data['results'] = serializer.data # 填充结果集
return R.success(data) # 返回成功响应

def retrieve(self, request, pk=None): # 文章详情接口
article = get_object_or_404(Article, pk=pk) # 获取文章或404
serializer = ArticleSerializer(article) # 序列化文章
return R.success(serializer.data) # 返回成功响应

# 收入接口视图集
class IncomeViewSet(viewsets.ViewSet):
permission_classes = [permissions.IsAuthenticated] # 需要登录

def list(self, request): # 收入列表接口
queryset = IncomeService.get_user_incomes(request.user, request.GET.get('income_type')) # 获取收入数据
serializer = IncomeSerializer(queryset, many=True) # 序列化收入
return R.success(serializer.data) # 返回成功响应

# 广告接口视图集
class AdvertisementViewSet(viewsets.ViewSet):
permission_classes = [permissions.AllowAny] # 所有人可访问

def list(self, request): # 广告列表接口
queryset = AdvertisementService.list_ads() # 获取广告数据
serializer = AdvertisementSerializer(queryset, many=True) # 序列化广告
return R.success(serializer.data) # 返回成功响应

# 分类接口视图集
class CategoryViewSet(viewsets.ViewSet):
permission_classes = [permissions.AllowAny] # 所有人可访问

def list(self, request): # 分类列表接口
queryset = CategoryService.list_categories() # 获取分类数据
serializer = CategorySerializer(queryset, many=True) # 序列化分类
return R.success(serializer.data) # 返回成功响应

# 文章区块接口视图集
class ArticleBlockViewSet(viewsets.ViewSet):
permission_classes = [permissions.AllowAny] # 所有人可访问

def list(self, request): # 文章区块列表接口
queryset = ArticleBlockService.list_blocks(request.GET.get('article')) # 获取区块数据
serializer = ArticleBlockSerializer(queryset, many=True) # 序列化区块
return R.success(serializer.data) # 返回成功响应

urls.py — URL路由注册

from rest_framework.routers import DefaultRouter  # 导入DRF路由器
from .views import ( # 导入所有视图集
ArticleViewSet, IncomeViewSet,
AdvertisementViewSet, CategoryViewSet, ArticleBlockViewSet
)

router = DefaultRouter() # 创建路由器实例
router.register(r'articles', ArticleViewSet, basename='articles') # 文章接口
router.register(r'incomes', IncomeViewSet, basename='incomes') # 收入接口
router.register(r'advertisements', AdvertisementViewSet, basename='advertisements') # 广告接口
router.register(r'categories', CategoryViewSet, basename='categories') # 分类接口
router.register(r'article-blocks', ArticleBlockViewSet, basename='article-blocks') # 文章区块接口

urlpatterns = router.urls # URL列表

API返回示例

成功返回:

{
"code": 200, # 状态码
"msg": "success", # 提示信息
"data": { # 返回数据
"count": 100, # 总条数
"page": 1, # 当前页码
"page_size": 10, # 每页数量
"results": [ # 数据列表
{"id": 1, "title": "标题1", "category_name": "分类A", "created_at": "2025-08-01T00:00:00Z"},
{"id": 2, "title": "标题2", "category_name": "分类B", "created_at": "2025-08-02T00:00:00Z"}
]
}
}

失败返回:

{
"code": 400, # 状态码
"msg": "Invalid Parameter", # 错误信息
"data": null # 返回数据
}

这个项目架构保证:

  1. ViewSet层非常薄,只负责接口请求与响应。
  2. 业务逻辑Service层独立,复杂逻辑全在services.py,代码易维护、可复用。
  3. 统一Response返回格式,所有接口code/msg/data标准化,便于前后端对接。
  4. 分页逻辑封装好,不用每个接口重复写分页代码。
  5. URL路由全部由DRF Router自动生成,新增接口只需注册即可。

这是DRF在中大型项目里最推荐的架构方式,简单接口写法不变,复杂业务拆到Service,ViewSet永远不会变屎山。