Django REST Framework多页复用HTML加载对应分类数据
Django DRF 多分类页面复用单模板+单库实现方案
问题根源
现有实现未给商品绑定分类属性,视图层全量拉取商品数据,同时为每个分类单独编写重复视图和模板,最终导致数据无法隔离、代码冗余度高。
分步实现
1. 给商品模型新增分类字段
首先修改models.py中的Product模型,新增分类标识字段用于后续数据筛选,不需要新建数据库表:
from django.db import models class Product(models.Model): # 保留原有字段 image = models.ImageField(upload_to='products/') description = models.TextField() price = models.DecimalField(max_digits=10, decimal_places=2) buy = models.CharField(max_length=200) # 新增分类slug字段,值和URL路径对齐,比如home、foods、drinks等,加索引提升查询效率 category = models.SlugField(max_length=50, db_index=True)
修改完成后依次执行python manage.py makemigrations、python manage.py migrate完成表结构更新。
2. 改造视图层,单视图处理全部分类页面
删除原有重复的home、foods视图函数,只编写一个通用分类视图,根据URL传入的分类参数筛选对应数据,统一传给同一个模板。
修改views.py:
from django.shortcuts import render, get_object_or_404 from .models import Product from rest_framework.viewsets import ModelViewSet from .serializers import ProductSerializer from django_filters.rest_framework import DjangoFilterBackend # 可维护的分类配置,后续新增分类直接在这里追加即可,无需修改视图、模板、路由 CATEGORY_MAP = { 'home': '首页推荐', 'foods': '食品专区', # 后续扩展到20个分类时,只需要在这里追加键值对,key对应URL路径,value对应页面显示名称 } def product_category_view(request, category): # 校验分类合法性,非法分类直接返回404 if category not in CATEGORY_MAP: raise get_object_or_404 # 仅拉取当前分类下的商品,不再全量查询 product_list = Product.objects.filter(category=category) context = { 'p': product_list, # 保持原有模板变量名不变,减少模板修改成本 'current_category': category, 'current_category_name': CATEGORY_MAP[category] } return render(request, 'product_list.html', context) class ProductViewSet(ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer # 新增过滤配置,支持接口按category参数筛选对应分类数据 filter_backends = [DjangoFilterBackend] filterset_fields = ['category']
注:如果未安装django-filter,先执行
pip install django-filter,再把django_filters添加到settings.py的INSTALLED_APPS列表中即可。
3. 配置URL路由,无需为每个分类单独写路由
修改app下的urls.py,用动态参数匹配所有分类页面:
from django.urls import path, include from rest_framework.routers import DefaultRouter from . import views router = DefaultRouter() router.register(r'products', views.ProductViewSet) urlpatterns = [ # 首页默认指向home分类 path('', views.product_category_view, {'category': 'home'}, name='home'), # 动态匹配所有分类页面,比如访问 /products/foods/ 即可进入食品专区 path('products/<slug:category>/', views.product_category_view, name='product_category'), # DRF接口路由 path('api/', include(router.urls)), ]
4. 合并模板,单模板复用全部分类页面
删除原有重复的home.html、foods.html,新建统一的product_list.html模板,原有卡片遍历逻辑完全保留,仅把页面标题等动态内容替换为传入的分类变量即可:
<!DOCTYPE html> <html> <head> <title>{{ current_category_name }}</title> </head> <body> <h1>{{ current_category_name }}</h1> <div class="product-list"> {% for item in p %} <div class="product-card"> <img src="{{ item.image.url }}" alt="{{ item.description }}"> <p>{{ item.description }}</p> <p>¥{{ item.price }}</p> <button>{{ item.buy }}</button> </div> {% empty %} <!-- 分类下无数据时显示空提示,不会再渲染其他分类的卡片 --> <p>当前分类暂无商品</p> {% endfor %} </div> </body> </html>
扩展说明
- 新增分类时,仅需在
CATEGORY_MAP中追加对应的分类键值对,不需要新增视图、模板、路由,也不需要新建数据库 - 新增商品时,提交的JSON数据中带上
category字段指定商品所属分类,商品就会自动在对应分类页面展示 - 前端异步加载数据时,接口请求带上
?category=分类标识参数即可拿到对应分类的商品数据
内容的提问来源于stack exchange,提问作者kan
相关产品推荐
相关产品推荐

