Django实现一对多与多对多标签筛选功能详解
本文详解Django中基于URL参数的标签筛选实现。涵盖一对多场景下的课程类型、难度及隐藏状态筛选,以及多对多场景下通过课程方向联动筛选类型的逻辑。提供完整的数据库模型、URL配置、视图处理及前端模板代码,展示如何利用正则路由传递参数并动态生成筛选条件。
本节主要阐述如何构建基于一对多关系的数据筛选功能。
具体需求为针对课程数据,支持按课程类型、难度级别以及是否隐藏这三个维度进行筛选。
在数据结构上,每个视频文件关联一个特定的课程类型、一个难度级别,并标记是否隐藏。
数据库模型设计如下:
# 视频分类表格
class VideoType(models.Model):
Video_Type = models.CharField(max_length=50)
class Meta:
verbose_name_plural = '视频分类'
def __str__(self):
return self.Video_Type
# 视频难度表格
class VideoDif(models.Model):
Video_dif = models.CharField(max_length=50)
class Meta:
verbose_name_plural = '视频难度'
def __str__(self):
return self.Video_dif
# 视频:ID、视频图片、视频名称、视频简介、视频地址、视频分类、视频难度、权重、是否显示
class Video(models.Model):
Video_img = models.CharField(max_length=100)
Video_title = models.CharField(max_length=100)
Video_text = models.TextField()
Video_type_id = models.ForeignKey('VideoType', on_delete=models.CASCADE,)
Video_dif_id = models.ForeignKey('VideoDif', on_delete=models.CASCADE,)
Video_qz = models.IntegerField(default=0)
display_choice = (
(1, '显示'),
(2, '隐藏'),
)
display = models.IntegerField(verbose_name='状态', choices=display_choice, default=1)
class Meta:
verbose_name_plural = '视频'
URL路由配置如下:
from django.urls import re_path
urlpatterns = [
path('admin/', admin.site.urls),
path('video/', views.video),
# 通过正则表达式添加三个字段,从前台获取当前选择项
re_path('video-(?P<Video_type_id>(\d+))-(?P<Video_dif_id>(\d+))-(?P<display>(\d+))', views.video),
后端视图处理逻辑如下:
def video(request,*args,**kwargs):
# 给后台筛选数据库使用
condition = {}
# kwargs是从前台URL获取的键值对,如果第一次访问,针对字典做一个初始化
if not kwargs:
kwargs ={
'Video_type_id':0,
'Video_dif_id':0,
'display':0,
}
# 依次取出kwargs字典中传来的值
for k, v in kwargs.items():
# 首先将传来的值变为数字类型
temp = int(v)
kwargs[k] = temp
# 如果kwargs中有值,循环将值赋予condition列表
if temp:
condition[k] = temp
# 从数据库中获取视频类型的列表
VideoType_list = models.VideoType.objects.all()
# 从数据库中获取视频难度的列表
VideoDif_list = models.VideoDif.objects.all()
# 从数据库中视频列表中,获取是否显示的字段的内容,是一个元组形式的:((1, '显示'), (2, '隐藏'))
# map后形成一个map对象:{'id':1,'name':'显示'}
# 最后list转换为列表:[{'id': 1, 'name': '显示'}, {'id': 2, 'name': '隐藏'}]
display_list = list(map(lambda x:{'id':x[0],'name':x[1]},models.Video.display_choice))
# 根据condition列表筛选数据库中的视频列表
video_list = models.Video.objects.filter(**condition)
return render(
request,
'video1.html',
{
'VideoType_list': VideoType_list,
'VideoDif_list': VideoDif_list,
'kwargs': kwargs,
'video_list': video_list,
'display_list': display_list,
}
)
前端展示模板如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.condition a{
display: inline-block;;
padding: 5px 8px;
border: 1px solid #dddddd;
}
.condition a.active{
background-color: red;
color: white;
}
</style>
</head>
<body>
<div class="condition">
<h1>筛选</h1>
<div>
{% if kwargs.Video_type_id == 0%}
<a href="/video-0-{{ kwargs.Video_dif_id }}-{{ kwargs.display }}" rel="external nofollow" rel="external nofollow" class="active">全部</a>
{% else %}
<a href="/video-0-{{ kwargs.Video_dif_id }}-{{ kwargs.display }}" rel="external nofollow" rel="external nofollow" >全部</a>
{% endif %}
{% for i in VideoType_list %}
{% if i.id == kwargs.Video_type_id %}
<a href="/video-{{ i.id }}-{{ kwargs.Video_dif_id }}-{{ kwargs.display }}" rel="external nofollow" rel="external nofollow" class="active">{{ i.Video_Type }}</a>
{% else %}
<a href="/video-{{ i.id }}-{{ kwargs.Video_dif_id }}-{{ kwargs.display }}" rel="external nofollow" rel="external nofollow" >{{ i.Video_Type }}</a>
{% endif %}
{% endfor %}
</div>
<div>
{% if kwargs.Video_dif_id == 0%}
<a href="/video-{{ kwargs.Video_type_id }}-0-{{ kwargs.display }}" rel="external nofollow" rel="external nofollow" class="active">全部</a>
{% else %}
<a href="/video-{{ kwargs.Video_type_id }}-0-{{ kwargs.display }}" rel="external nofollow" rel="external nofollow" >全部</a>
{% endif %}
{% for i in VideoDif_list %}
{% if i.id == kwargs.Video_dif_id %}
<a href="/video-{{ kwargs.Video_type_id }}-{{ i.id }}-{{ kwargs.display }}" rel="external nofollow" rel="external nofollow" class="active">{{ i.Video_dif }}</a>
{% else %}
<a href="/video-{{ kwargs.Video_type_id }}-{{ i.id }}-{{ kwargs.display }}" rel="external nofollow" rel="external nofollow" >{{ i.Video_dif }}</a>
{% endif %}
{% endfor %}
</div>
<div>
{% if kwargs.display == 0 %}
<a class="active" href="/video-{{ kwargs.Video_type_id }}-{{ kwargs.Video_dif_id }}-0" rel="external nofollow" rel="external nofollow" >全部</a>
{% else %}
<a href="/video-{{ kwargs.Video_type_id }}-{{ kwargs.Video_dif_id }}-0" rel="external nofollow" rel="external nofollow" >全部</a>
{% endif %}
{% for item in display_list %}
{% if item.id == kwargs.display %}
<a class="active" href="/video-{{ kwargs.Video_type_id }}-{{ kwargs.Video_dif_id }}-{{ item.id }}" rel="external nofollow" rel="external nofollow" >{{ item.name }}</a>
{% else %}
<a href="/video-{{ kwargs.Video_type_id }}-{{ kwargs.Video_dif_id }}-{{ item.id }}" rel="external nofollow" rel="external nofollow" >{{ item.name }}</a>
{% endif %}
{% endfor %}
</div>
</div>
<div>
<h1>结果</h1>
<div>
{% for row in video_list %}
<p>{{ row.Video_title }}</p>
{% endfor %}
</div>
</div>
</body>
</html>
前端通过切换active标签状态来高亮当前选中项,并利用a标签中携带的参数值来控制后端的筛选逻辑。
接下来介绍基于多对多关系的筛选功能实现。
此场景下,需支持按课程方向、课程类型以及难度级别三个维度进行筛选。
其中,每个课程方向包含多个课程类型;当用户选择某个课程方向时,系统应自动筛选出该方向涵盖的所有课程类型。
每个视频文件依然关联一个课程类型和一个难度级别。
数据库设计在一对多基础上,新增了一个用于表示多对多关系的课程方向表:
# 方向分类:ID、名称(与视频—分类做多对多关系)
class VideoGroup(models.Model):
Video_group = models.CharField(max_length=50)
group_type = models.ManyToManyField('VideoType')
class Meta:
verbose_name_plural = '方向分类'
def __str__(self):
return self.Video_group
# 视频分类表格
class VideoType(models.Model):
Video_Type = models.CharField(max_length=50)
class Meta:
verbose_name_plural = '视频分类'
def __str__(self):
return self.Video_Type
# 视频难度表格
class VideoDif(models.Model):
Video_dif = models.CharField(max_length=50)
class Meta:
verbose_name_plural = '视频难度'
def __str__(self):
return self.Video_dif
# 视频:ID、视频图片、视频名称、视频简介、视频地址、视频分类、视频难度、权重、是否显示
class Video(models.Model):
Video_img = models.CharField(max_length=100)
Video_title = models.CharField(max_length=100)
Video_text = models.TextField()
Video_type_id = models.ForeignKey('VideoType', on_delete=models.CASCADE,)
Video_dif_id = models.ForeignKey('VideoDif', on_delete=models.CASCADE,)
Video_qz = models.IntegerField(default=0)
display_choice = (
(1, '显示'),
(2, '隐藏'),
)
display = models.IntegerField(verbose_name='状态', choices=display_choice, default=1)
class Meta:
verbose_name_plural = '视频'
对应的URL路由配置如下:
urlpatterns = [
path('admin/', admin.site.urls),
path('video2/', views.video2),
re_path('video2-(?P<Video_group_id>(\d+))-(?P<Video_type_id>(\d+))-(?P<Video_dif_id>(\d+))', views.video2),
]
后端视图处理逻辑如下:
def video2(request, *args, **kwargs):
condition = {}
# 思路 -- 构造查询字典
"""
如果:获取Video_group_id=0 代表方向是全部,不会对以后的筛选造成影响
*列出所有的type
如果:Video_type_id=0
pass
否则:
condition【'Video_type_id'】= Video_type_id
否则:*列出当前方向下的type
如果:Video_type_id=0
获取当前方向下的type的所有的id【1,2,3,4】
condition【'Video_type_id__in'】= 【1,2,3,4】
否则:
需要查看当前的type是否在当前的方向列表中,如果在:
condition【'Video_type_id'】= Video_type_id
如果不在:
condition【'Video_type_id__in'】= 【1,2,3,4】
"""
if not kwargs:
kwargs = {
'Video_type_id':0,
'Video_dif_id':0,
'Video_group_id':0,
}
for k, v in kwargs.items():
temp = int(v)
kwargs[k] = temp
# 首先从kwargs中取出相应的id
group_id = kwargs.get('Video_group_id')
type_id = kwargs.get('Video_type_id')
dif_id = kwargs.get('Video_dif_id')
# 从数据库中取出所有的group列表,因为所有方向在页面上都要显示
group_list = models.VideoGroup.objects.all()
# 判断group值是否为0
if group_id == 0:
# 如果为0,则列出所有type的列表
VideoType_list = models.VideoType.objects.all()
# 如果type的列表也为0,筛选中就不用作特殊操作
if type_id == 0:
pass
# 如果type的列表不为0,筛选列表中增加type的id
else:
condition['Video_type_id'] = type_id
# 如果group值不为0
else:
# 首先根据group的id筛选出分类表格中的内容,形成一个对象
group_obj = models.VideoGroup.objects.filter(id=group_id).first()
# 再根据group筛选出的对象,用多对多表格字段,筛选出所有的type的列表,等待返回给前台使用
VideoType_list = group_obj.group_type.all()
# 获取筛选后的type的id值,得到一个QuerySet [(1,),(3,),(4,)]的对象
vlist = group_obj.group_type.all().values_list('id')
# 如果筛选后的type的值为空,也就是没有找到对应的type类型
if not vlist:
# 设置一个空列表
type_ids = []
# 如果筛选后的type值有内容
else:
# 将vlist进行一个zip,获得一个zip的对象,再转化为列表,得到一个【(1,3,4)】,取第一个值,得到(1,3,4)
type_ids = list(zip(*vlist))[0] # (1,3,4)
# 判断如果前台传来的type为0的话
if type_id == 0:
# 后台筛选的时候,查询按照方向筛选出来的type_ids进行查询
# __in指的是用列表方式查询多个id
condition['Video_type_id__in'] = type_ids
# 如果前台传来的type不为0的时候,有两种情况
else:
# 如果前台传来的type值在后台筛选的值范围内的时候
if type_id in type_ids:
# 后台筛选的typeid就按照前台传来的type值筛选,也就是前台选了某个课程,如果课程方向发生改变的时候,课程类型还在选择范围内,前台也仍然是选中的状态,我们也就仍然返回选中的课程类型筛选的内容
condition['Video_type_id'] = type_id
# 如果前台传来的type值不在后台筛选的值范围内的时候
else:
# 就按照后台筛选的课程方向向下的所有type类型进行筛选
condition['Video_type_id__in'] = type_ids
kwargs['Video_type_id'] = 0
# 难度这边跟上面的多对多没有关联,与一对多的情况时一样
if dif_id == 0:
pass
else:
condition['Video_dif_id'] = dif_id
VideoDif_list = models.VideoDif.objects.all()
# 最终将符合条件的视频筛选出来
video_list = models.Video.objects.filter(**condition)
return render(
request,
'video2.html',
{
'group_list': group_list,
'VideoType_list': VideoType_list,
'VideoDif_list': VideoDif_list,
'video_list': video_list,
'kwargs': kwargs
}
)
前端展示模板如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.condition a{
display: inline-block;;
padding: 5px 8px;
border: 1px solid #dddddd;
}
.condition a.active{
background-color: red;
color: white;
}
</style>
</head>
<body>
<div class="condition">
<h1>筛选</h1>
<div>
{% if kwargs.Video_group_id == 0%}
<a href="/video2-0-{{ kwargs.Video_type_id }}-{{ kwargs.Video_dif_id }}" rel="external nofollow" rel="external nofollow" class="active">全部</a>
{% else %}
<a href="/video2-0-{{ kwargs.Video_type_id }}-{{ kwargs.Video_dif_id }}" rel="external nofollow" rel="external nofollow" >全部</a>
{% endif %}
{% for item in group_list %}
{% if item.id == kwargs.Video_group_id %}
<a class="active" href="/video2-{{ item.id }}-{{ kwargs.Video_type_id }}-{{ kwargs.Video_dif_id }}" rel="external nofollow" rel="external nofollow" >{{ item.Video_group }}</a>
{% else %}
<a href="/video2-{{ item.id }}-{{ kwargs.Video_type_id }}-{{ kwargs.Video_dif_id }}" rel="external nofollow" rel="external nofollow" >{{ item.Video_group }}</a>
{% endif %}
{% endfor %}
</div>
<div>
{% if kwargs.Video_type_id == 0%}
<a href="/video2-{{ kwargs.Video_group_id }}-0-{{ kwargs.Video_dif_id }}" rel="external nofollow" rel="external nofollow" class="active">全部</a>
{% else %}
<a href="/video2-{{ kwargs.Video_group_id }}-0-{{ kwargs.Video_dif_id }}" rel="external nofollow" rel="external nofollow" >全部</a>
{% endif %}
{% for item in VideoType_list %}
{% if item.id == kwargs.Video_type_id %}
<a class="active" href="/video2-{{ kwargs.Video_group_id }}-{{ item.id }}-{{ kwargs.Video_dif_id }}" rel="external nofollow" rel="external nofollow" >{{ item.Video_Type }}</a>
{% else %}
<a href="/video2-{{ kwargs.Video_group_id }}-{{ item.id }}-{{ kwargs.Video_dif_id }}" rel="external nofollow" rel="external nofollow" >{{ item.Video_Type }}</a>
{% endif %}
{% endfor %}
</div>
<div>
{% if kwargs.Video_dif_id == 0%}
<a href="/video2-{{ kwargs.Video_group_id }}-{{ kwargs.Video_type_id }}-0" rel="external nofollow" rel="external nofollow" class="active">全部</a>
{% else %}
<a href="/video2-{{ kwargs.Video_group_id }}-{{ kwargs.Video_type_id }}-0" rel="external nofollow" rel="external nofollow" >全部</a>
{% endif %}
{% for item in VideoDif_list %}
{% if item.id == kwargs.Video_dif_id %}
<a class="active" href="/video2-{{ kwargs.Video_group_id }}-{{ kwargs.Video_type_id }}-{{ item.id }}" rel="external nofollow" rel="external nofollow" >{{ item.Video_dif }}</a>
{% else %}
<a href="/video2-{{ kwargs.Video_group_id }}-{{ kwargs.Video_type_id }}-{{ item.id }}" rel="external nofollow" rel="external nofollow" >{{ item.Video_dif }}</a>
{% endif %}
{% endfor %}
</div>
</div>
<div>
<h1>结果</h1>
<div>
{% for item in video_list %}
<p>{{ item.Video_title }}</p>
{% endfor %}
</div>
</div>
</body>
</html>
总结
以上介绍了Django中实现一对多及多对多标签筛选的完整代码示例,希望能提供帮助。如有任何疑问,欢迎留言,小编将及时回复。感谢大家对本站的支持!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









