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

每一行都是一个模型,模型之间有着连表关系(一对多、多对多等)

模型设计

  • 总共四个模型:分别为方向、分类、等级以及存储结果信息
  • 一个方向下可以有多个分类,一个分类也可以是多个方向(多对多)
  • 一个分类可以有多个视频(一对多)
  • 一个等级下也可以有多个视频(一对多)
  1. from django.db import models
  2. class Direction(models.Model):
  3. """方向:自动化、测试、运维、前端"""
  4. name = models.CharField(verbose_name='名称', max_length=32)
  5. classification = models.ManyToManyField('Classification')
  6. class Meta:
  7. db_table = 'Direction'
  8. verbose_name_plural = '方向(视频方向)' # 模型的复数形式,若不指定则在后面加一个 s
  9. def __str__(self):
  10. return self.name
  11. class Classification(models.Model):
  12. """分类:Python、Linux、JavaScript、OpenStack、Node.js、C"""
  13. name = models.CharField(verbose_name='名称', max_length=32)
  14. class Meta:
  15. db_table = 'Classification'
  16. verbose_name_plural = '分类(视频分类)'
  17. def __str__(self):
  18. return self.name
  19. class Level(models.Model):
  20. """等级:初级、中级、高级、骨灰级"""
  21. title = models.CharField(max_length=32)
  22. class Meta:
  23. verbose_name_plural = '难度级别'
  24. def __str__(self):
  25. return self.title
  26. class Video(models.Model):
  27. status_choice = (
  28. (1, '下线'),
  29. (2, '上线'),
  30. )
  31. status = models.IntegerField(verbose_name='状态', choices=status_choice, default=1)
  32. level = models.ForeignKey('Level', on_delete=models.CASCADE)
  33. classification = models.ForeignKey('Classification', null=True, blank=True, on_delete=models.CASCADE)
  34. weight = models.IntegerField(verbose_name='权重(从大到小排列)', default=0)
  35. title = models.CharField(verbose_name='标题', max_length=32)
  36. summary = models.CharField(verbose_name='简介', max_length=100)
  37. # 也可以用 ImgField 或 FileField 存储图片,而不是 CharField,但是要指定存储路径
  38. # img = models.ImgField(verbose_name='图片', upload_to='app/static/app/video')
  39. img = models.CharField(verbose_name='图片', max_length=200)
  40. href = models.CharField(verbose_name='视频地址', max_length=256)
  41. create_data = models.DateTimeField(auto_now_add=True)
  42. class Meta:
  43. db_table = 'Video'
  44. verbose_name_plural = '视频'
  45. def __str__(self):
  46. return self.title

URlconf 配置

urlconf 配置采用的是二级路由,即应用本身创建

urls.py

:

  1. # project/urls.py
  2. from django.contrib import admin
  3. from django.urls import path, include
  4. urlpatterns = [
  5. path('admin/', admin.site.urls),
  6. path('app/', include('app.urls')),
  7. ]
  8. # app/urls.py
  9. from django.urls import path
  10. from app import views
  11. urlpatterns = [
  12. path('video/<int:classification_id>/<int:level_id>/<int:status>/', views.video, name='video'),
  13. path('video2/<int:direction_id>/<int:classification_id>/<int:level_id>/', views.video2, name='video2'),
  14. ]

一对多

一对多关系中主要是视频信息与分类、等级之间的关系操作。

视图函数

函数接收三个额外参数,分别为:分类

classification

id、等级

level

id 以及状态

status

id。根据这三个参数构造查询条件(

filter()

函数本身是支持字典形式查询的)。


Tips


condition

构造的查询条件是字典形式,其键

key

应与数据表中相应字段对应,如:

classification_id

为模型

Video

与

Classification

的外键字段名。

  1. from django.shortcuts import render, HttpResponse
  2. from app.models import Level, Classification, Direction, Video
  3. def video(request, *args, **kwargs):
  4. print(args, kwargs) # () {'classification_id': 1, 'level_id': 2}
  5. # 构造查询条件
  6. condition = {
  7. # 'classification_id': 0,
  8. # 'level_id': 0
  9. }
  10. # 当 kwargs = {'classification_id': 0, 'level_id': 0} 时,condition = {},
  11. # Video.objects.filter(**condition) 能把所有的都查出来
  12. # 当 kwargs = {'classification_id': 1, 'level_id': 1} 时,condition = {'classification_id': 1, 'level_id': 1}
  13. # filter() 支持字典格式
  14. # 将其转换为整数
  15. for k, v in kwargs.items():
  16. temp = int(v)
  17. kwargs[k] = temp
  18. if temp:
  19. condition[k] = temp
  20. class_list = Classification.objects.all()
  21. level_list = Level.objects.all()
  22. # 查询数据
  23. video_list = Video.objects.filter(**condition)
  24. """
  25. status_choice = (
  26. (1, '下线'),
  27. (2, '上线'),
  28. )
  29. """
  30. # status_list:[{'id': 1, 'name': '下线'}, {'id': 2, 'name': '上线'}]
  31. status_list = list(map(lambda x: {'id': x[0], 'name': x[1]}, Video.status_choice))
  32. return render(
  33. request,
  34. 'app/video.html',
  35. {
  36. 'kwargs': kwargs,
  37. 'class_list': class_list,
  38. 'level_list': level_list,
  39. 'video_list': video_list,
  40. 'status_list': status_list
  41. }
  42. )

模板

  1. {% load static %}
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>视频</title>
  7. <style type="text/css">
  8. .box1 a{
  9. display: inline-block;
  10. padding: 5px 8px;
  11. border: 1px solid #dddddd;
  12. }
  13. .box1 a.active{
  14. background-color: coral;
  15. color: white;
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <div class="box1">
  21. <h1>筛选</h1>
  22. <!--分类-->
  23. <div>
  24. {% if kwargs.classification_id == 0 %}
  25. <a class="active" href="/app/video/0/{{ kwargs.level_id }}/{{ kwargs.status }}">全部</a>
  26. {% else %}
  27. <a href="/app/video/0/{{ kwargs.level_id }}/{{ kwargs.status }}">全部</a>
  28. {% endif %}
  29. {% for class in class_list %}
  30. {% if class.id == kwargs.classification_id %}
  31. <a href="/app/video/{{ class.id }}/{{ kwargs.level_id }}/{{ kwargs.status }}" class="active">{{ class.name }}</a>
  32. {% else %}
  33. <a href="/app/video/{{ class.id }}/{{ kwargs.level_id }}/{{ kwargs.status }}">{{ class.name }}</a>
  34. {% endif %}
  35. {% endfor %}
  36. </div>
  37. <!--等级-->
  38. <div>
  39. {% if kwargs.level_id == 0 %}
  40. <a href="/app/video/{{ kwargs.classification_id }}/0/{{ kwargs.status }}" class="active">全部</a>
  41. {% else %}
  42. <a href="/app/video/{{ kwargs.classification_id }}/0/{{ kwargs.status }}">全部</a>
  43. {% endif %}
  44. {% for level in level_list %}
  45. {% if level.id == kwargs.level_id %}
  46. <a href="/app/video/{{ kwargs.classification_id }}/{{ level.id }}/{{ kwargs.status }}" class="active">{{ level.title }}</a>
  47. {% else %}
  48. <a href="/app/video/{{ kwargs.classification_id }}/{{ level.id }}/{{ kwargs.status }}">{{ level.title }}</a>
  49. {% endif %}
  50. {% endfor %}
  51. </div>
  52. <!--状态-->
  53. <div>
  54. {% if kwargs.status == 0 %}
  55. <a href="/app/video/{{ kwargs.classification_id }}/{{ kwargs.level_id }}/0" class="active">全部</a>
  56. {% else %}
  57. <a href="/app/video/{{ kwargs.classification_id }}/{{ kwargs.level_id }}/0">全部</a>
  58. {% endif %}
  59. {% for status in status_list %}
  60. {% if status.id == kwargs.status %}
  61. <a href="/app/video/{{ kwargs.classification_id }}/{{ kwargs.level_id }}/{{ status.id }}" class="active">{{ status.name }}</a>
  62. {% else %}
  63. <a href="/app/video/{{ kwargs.classification_id }}/{{ kwargs.level_id }}/{{ status.id }}">{{ status.name }}</a>
  64. {% endif %}
  65. {% endfor %}
  66. </div>
  67. </div>
  68. <div>
  69. <h1>结果</h1>
  70. <div>
  71. {% for video in video_list %}
  72. <p>{{ video.title }}</p>
  73. <img src="{{ video.href }}">
  74. {% endfor %}
  75. </div>
  76. </div>
  77. </body>
  78. </html>

多对多

多对多关系中主要是方向与分类之间的关系操作。

视图函数

  1. from django.shortcuts import render, HttpResponse
  2. from app.models import Level, Classification, Direction, Video
  3. def video2(request, *args, **kwargs):
  4. """
  5. 多对多:"方向-分类
  6. 方向:自动化、测试、运维、前端
  7. 分类:Python、Linux、JavaScript、OpenStack、Node.js、C
  8. 等级:初级、中级、高级、骨灰级
  9. """
  10. print(kwargs) # {'direction_id': 1, 'classification_id': 4, 'level_id': 2}
  11. direction_id = kwargs.get('direction_id')
  12. classification_id = kwargs.get('classification_id')
  13. level_id = kwargs.get('level_id')
  14. condition = {} # 条件 构造查询字典
  15. # 方向:全部列举
  16. direction_list = Direction.objects.all()
  17. # 若方向为 0,则分类显示全部
  18. if direction_id == 0:
  19. # 若分类也为 0,分类显示全部
  20. class_list = Classification.objects.all()
  21. if classification_id == 0:
  22. pass
  23. else:
  24. condition['classification_id'] = classification_id
  25. # 若方向不为 0,则根据方向查询分类
  26. else:
  27. direction_obj = Direction.objects.filter(id=direction_id)[0]
  28. class_list = direction_obj.classification.all() # 根据多对多名字+方向的ID,获取相应分类列表
  29. # < QuerySet[ < Classification: Python >, < Classification: Linux >] >
  30. # 根据方向 id 获取分类的 id 列表
  31. vlist = direction_obj.classification.all().values_list('id') # < QuerySet[(1,), (5,)] >
  32. # 若哪个方向下没有分类
  33. if not vlist:
  34. classification_id_list = []
  35. else:
  36. # 转换为元组
  37. classification_id_list = list(zip(*vlist))[0] # (1, 5)
  38. # 若分类为 0,则显示相应分类
  39. if classification_id == 0:
  40. condition['classification_id__in'] = classification_id_list
  41. else:
  42. if classification_id in classification_id_list:
  43. condition['classification_id'] = classification_id
  44. else:
  45. condition['classification_id__in'] = classification_id_list
  46. if level_id == 0:
  47. pass
  48. else:
  49. condition['level_id'] = level_id
  50. # 等级:全部列举
  51. level_list = Level.objects.all()
  52. video_list = Video.objects.filter(**condition)
  53. return render(
  54. request,
  55. 'app/video2.html',
  56. {
  57. 'direction_list': direction_list,
  58. 'level_list': level_list,
  59. 'kwargs': kwargs,
  60. 'video_list': video_list,
  61. 'class_list': class_list
  62. }
  63. )

模板

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>多对多和一对多</title>
  6. <style type="text/css">
  7. .box1 a {
  8. display: inline-block;
  9. padding: 5px 8px;
  10. border: 1px solid #dddddd;
  11. }
  12. .box1 a.active {
  13. background-color: coral;
  14. color: white;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div class="box1">
  20. <h1>筛选</h1>
  21. <!--方向-->
  22. <div>
  23. {% if kwargs.direction_id == 0 %}
  24. <a class="active" href="{% url 'video2' direction_id=0 classification_id=kwargs.classification_id level_id=kwargs.level_id %}">全部</a>
  25. {% else %}
  26. <a href="{% url 'video2' direction_id=0 classification_id=kwargs.classification_id level_id=kwargs.level_id %}">全部</a>
  27. {% endif %}
  28. {% for direction in direction_list %}
  29. {% if direction.id == kwargs.direction_id %}
  30. <a href="{% url 'video2' direction_id=direction.id classification_id=kwargs.classification_id level_id=kwargs.level_id %}" class="active">{{ direction.name }}</a>
  31. {% else %}
  32. <a href="{% url 'video2' direction_id=direction.id classification_id=kwargs.classification_id level_id=kwargs.level_id %}">{{ direction.name }}</a>
  33. {% endif %}
  34. {% endfor %}
  35. </div>
  36. <!--分类-->
  37. <div>
  38. {% if kwargs.classification_id == 0 %}
  39. <a class="active" href="{% url 'video2' direction_id=kwargs.direction_id classification_id=0 level_id=kwargs.level_id %}">全部</a>
  40. {% else %}
  41. <a href="{% url 'video2' direction_id=kwargs.direction_id classification_id=0 level_id=kwargs.level_id %}">全部</a>
  42. {% endif %}
  43. {% for class in class_list %}
  44. {% if class.id == kwargs.classification_id %}
  45. <a class="active" href="{% url 'video2' direction_id=kwargs.direction_id classification_id=class.id level_id=kwargs.level_id %}">{{ class.name }}</a>
  46. {% else %}
  47. <a href="{% url 'video2' direction_id=kwargs.direction_id classification_id=class.id level_id=kwargs.level_id %}">{{ class.name }}</a>
  48. {% endif %}
  49. {% endfor %}
  50. </div>
  51. <!--等级-->
  52. <div>
  53. {% if kwargs.level_id == 0 %}
  54. <a class="active" href="{% url 'video2' direction_id=kwargs.direction_id classification_id=kwargs.classification_id level_id=0 %}">全部</a>
  55. {% else %}
  56. <a href="{% url 'video2' direction_id=kwargs.direction_id classification_id=kwargs.classification_id level_id=0 %}">全部</a>
  57. {% endif %}
  58. {% for level in level_list %}
  59. {% if level.id == kwargs.level_id %}
  60. <a class="active" href="{% url 'video2' direction_id=kwargs.direction_id classification_id=kwargs.classification_id level_id=level.id %}">{{ level.title }}</a>
  61. {% else %}
  62. <a href="{% url 'video2' direction_id=kwargs.direction_id classification_id=kwargs.classification_id level_id=level.id %}">{{ level.title }}</a>
  63. {% endif %}
  64. {% endfor %}
  65. </div>
  66. </div>
  67. <div>
  68. <h1>结果</h1>
  69. {% for video in video_list %}
  70. <p>{{ video.title }}</p>
  71. {% endfor %}
  72. </div>
  73. </body>
  74. </html>


参考博客

  • Django之组合查询
  • django实现瀑布流、组合搜索、阶梯评论、验证码