现在很多网站都会有这样的组合搜索功能,其实质是几个模型之间组合对数据库进行查询,并将结果显示到页面上。
每一行都是一个模型,模型之间有着连表关系(一对多、多对多等)

from django.db import modelsclass Direction(models.Model):"""方向:自动化、测试、运维、前端"""name = models.CharField(verbose_name='名称', max_length=32)classification = models.ManyToManyField('Classification')class Meta:db_table = 'Direction'verbose_name_plural = '方向(视频方向)' # 模型的复数形式,若不指定则在后面加一个 sdef __str__(self):return self.nameclass Classification(models.Model):"""分类:Python、Linux、JavaScript、OpenStack、Node.js、C"""name = models.CharField(verbose_name='名称', max_length=32)class Meta:db_table = 'Classification'verbose_name_plural = '分类(视频分类)'def __str__(self):return self.nameclass Level(models.Model):"""等级:初级、中级、高级、骨灰级"""title = models.CharField(max_length=32)class Meta:verbose_name_plural = '难度级别'def __str__(self):return self.titleclass Video(models.Model):status_choice = ((1, '下线'),(2, '上线'),)status = models.IntegerField(verbose_name='状态', choices=status_choice, default=1)level = models.ForeignKey('Level', on_delete=models.CASCADE)classification = models.ForeignKey('Classification', null=True, blank=True, on_delete=models.CASCADE)weight = models.IntegerField(verbose_name='权重(从大到小排列)', default=0)title = models.CharField(verbose_name='标题', max_length=32)summary = models.CharField(verbose_name='简介', max_length=100)# 也可以用 ImgField 或 FileField 存储图片,而不是 CharField,但是要指定存储路径# img = models.ImgField(verbose_name='图片', upload_to='app/static/app/video')img = models.CharField(verbose_name='图片', max_length=200)href = models.CharField(verbose_name='视频地址', max_length=256)create_data = models.DateTimeField(auto_now_add=True)class Meta:db_table = 'Video'verbose_name_plural = '视频'def __str__(self):return self.title
urlconf 配置采用的是二级路由,即应用本身创建 urls.py:
# project/urls.py
from django.contrib import admin
from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),path('app/', include('app.urls')),
]# app/urls.py
from django.urls import path
from app import viewsurlpatterns = [path('video////', views.video, name='video'),path('video2////', views.video2, name='video2'),
]
一对多关系中主要是视频信息与分类、等级之间的关系操作。
函数接收三个额外参数,分别为:分类 classification id、等级 level id 以及状态 status id。根据这三个参数构造查询条件(filter() 函数本身是支持字典形式查询的)。
Tips
condition 构造的查询条件是字典形式,其键 key应与数据表中相应字段对应,如: classification_id 为模型 Video与 Classification 的外键字段名。
from django.shortcuts import render, HttpResponse
from app.models import Level, Classification, Direction, Videodef video(request, *args, **kwargs):print(args, kwargs) # () {'classification_id': 1, 'level_id': 2}# 构造查询条件condition = {# 'classification_id': 0,# 'level_id': 0}# 当 kwargs = {'classification_id': 0, 'level_id': 0} 时,condition = {},# Video.objects.filter(**condition) 能把所有的都查出来# 当 kwargs = {'classification_id': 1, 'level_id': 1} 时,condition = {'classification_id': 1, 'level_id': 1}# filter() 支持字典格式# 将其转换为整数for k, v in kwargs.items():temp = int(v)kwargs[k] = tempif temp:condition[k] = tempclass_list = Classification.objects.all()level_list = Level.objects.all()# 查询数据video_list = Video.objects.filter(**condition)"""status_choice = ((1, '下线'),(2, '上线'),)"""# status_list:[{'id': 1, 'name': '下线'}, {'id': 2, 'name': '上线'}]status_list = list(map(lambda x: {'id': x[0], 'name': x[1]}, Video.status_choice))return render(request,'app/video.html',{'kwargs': kwargs,'class_list': class_list,'level_list': level_list,'video_list': video_list,'status_list': status_list})
{% load static %}
视频
筛选
{% if kwargs.classification_id == 0 %}{ kwargs.level_id }}/{{ kwargs.status }}">全部{% else %}{ kwargs.level_id }}/{{ kwargs.status }}">全部{% endif %}{% for class in class_list %}{% if class.id == kwargs.classification_id %}{ class.id }}/{{ kwargs.level_id }}/{{ kwargs.status }}" class="active">{{ class.name }}{% else %}{ class.id }}/{{ kwargs.level_id }}/{{ kwargs.status }}">{{ class.name }}{% endif %}{% endfor %}{% if kwargs.level_id == 0 %}{ kwargs.classification_id }}/0/{{ kwargs.status }}" class="active">全部{% else %}{ kwargs.classification_id }}/0/{{ kwargs.status }}">全部{% endif %}{% for level in level_list %}{% if level.id == kwargs.level_id %}{ kwargs.classification_id }}/{{ level.id }}/{{ kwargs.status }}" class="active">{{ level.title }}{% else %}{ kwargs.classification_id }}/{{ level.id }}/{{ kwargs.status }}">{{ level.title }}{% endif %}{% endfor %}{% if kwargs.status == 0 %}{ kwargs.classification_id }}/{{ kwargs.level_id }}/0" class="active">全部{% else %}{ kwargs.classification_id }}/{{ kwargs.level_id }}/0">全部{% endif %}{% for status in status_list %}{% if status.id == kwargs.status %}{ kwargs.classification_id }}/{{ kwargs.level_id }}/{{ status.id }}" class="active">{{ status.name }}{% else %}{ kwargs.classification_id }}/{{ kwargs.level_id }}/{{ status.id }}">{{ status.name }}{% endif %}{% endfor %}结果
{% for video in video_list %}{{ video.title }}
{ video.href }}">{% endfor %}

多对多关系中主要是方向与分类之间的关系操作。
from django.shortcuts import render, HttpResponse
from app.models import Level, Classification, Direction, Videodef video2(request, *args, **kwargs):"""多对多:"方向-分类方向:自动化、测试、运维、前端分类:Python、Linux、JavaScript、OpenStack、Node.js、C等级:初级、中级、高级、骨灰级"""print(kwargs) # {'direction_id': 1, 'classification_id': 4, 'level_id': 2}direction_id = kwargs.get('direction_id')classification_id = kwargs.get('classification_id')level_id = kwargs.get('level_id')condition = {} # 条件 构造查询字典# 方向:全部列举direction_list = Direction.objects.all()# 若方向为 0,则分类显示全部if direction_id == 0:# 若分类也为 0,分类显示全部class_list = Classification.objects.all()if classification_id == 0:passelse:condition['classification_id'] = classification_id# 若方向不为 0,则根据方向查询分类else:direction_obj = Direction.objects.filter(id=direction_id)[0]class_list = direction_obj.classification.all() # 根据多对多名字+方向的ID,获取相应分类列表# < QuerySet[ < Classification: Python >, < Classification: Linux >] ># 根据方向 id 获取分类的 id 列表vlist = direction_obj.classification.all().values_list('id') # < QuerySet[(1,), (5,)] ># 若哪个方向下没有分类if not vlist:classification_id_list = []else:# 转换为元组classification_id_list = list(zip(*vlist))[0] # (1, 5)# 若分类为 0,则显示相应分类if classification_id == 0:condition['classification_id__in'] = classification_id_listelse:if classification_id in classification_id_list:condition['classification_id'] = classification_idelse:condition['classification_id__in'] = classification_id_listif level_id == 0:passelse:condition['level_id'] = level_id# 等级:全部列举level_list = Level.objects.all()video_list = Video.objects.filter(**condition)return render(request,'app/video2.html',{'direction_list': direction_list,'level_list': level_list,'kwargs': kwargs,'video_list': video_list,'class_list': class_list})
多对多和一对多
筛选
{% if kwargs.direction_id == 0 %}全部{% else %}全部{% endif %}{% for direction in direction_list %}{% if direction.id == kwargs.direction_id %}{{ direction.name }}{% else %}{{ direction.name }}{% endif %}{% endfor %}{% if kwargs.classification_id == 0 %}全部{% else %}全部{% endif %}{% for class in class_list %}{% if class.id == kwargs.classification_id %}{{ class.name }}{% else %}{{ class.name }}{% endif %}{% endfor %}{% if kwargs.level_id == 0 %}全部{% else %}全部{% endif %}{% for level in level_list %}{% if level.id == kwargs.level_id %}{{ level.title }}{% else %}{{ level.title }}{% endif %}{% endfor %}结果
{% for video in video_list %}{{ video.title }}
{% endfor %}

参考博客