Django多条件筛选查询
主模型只存在外键一对多关系
模型设计
# 快捷筛选状态
class Status(models.Model):
order_number = models.PositiveIntegerField(unique=True, verbose_name='状态编号')
status_tag = models.CharField(max_length=10, verbose_name='状态名称')
class Meta:
ordering = ['order_number', ]
verbose_name = '事件选择'
verbose_name_plural = verbose_name
def __str__(self):
return self.status_tag
# 项目分类
class Project(models.Model):
project_name = models.CharField(max_length=10, verbose_name='项目名称')
class Meta:
ordering = ['project_name']
verbose_name = '项目分类'
verbose_name_plural = verbose_name
def __str__(self):
return self.project_name
# 事件分类
class Category(models.Model):
category_name = models.CharField(max_length=10, verbose_name='分类名称')
class Meta:
ordering = ['category_name', ]
verbose_name = '事件分类'
verbose_name_plural = verbose_name
def __str__(self):
return self.category_name
# 事件级别
class Level(models.Model):
order_number = models.PositiveIntegerField(unique=True, verbose_name='级别编号')
level_tag = models.CharField(max_length=10, verbose_name='级别名称')
class Meta:
ordering = ['order_number', ]
verbose_name = '事件级别'
verbose_name_plural = verbose_name
def __str__(self):
return self.level_tag
# 事件内容
class EventContent(models.Model):
title = models.CharField(max_length=50, verbose_name='事件标题')
content = models.TextField(verbose_name='事件正文')
image = models.ImageField(upload_to='images/%Y/%m', blank=True, null=True, verbose_name='描述图片')
created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated = models.DateTimeField(auto_now=True, verbose_name='更新时间')
status = models.ForeignKey(Status, on_delete=models.SET_NULL, null=True, blank=True, related_name='event_content', verbose_name='事件状态')
project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True, related_name='event_content', verbose_name='项目分类')
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='event_content', verbose_name='事件分类')
level = models.ForeignKey(Level, on_delete=models.SET_NULL, null=True, blank=True, related_name='event_content', verbose_name='事件级别')
user = models.ForeignKey(User, related_name='event_content', verbose_name='创建人')
start_time = models.DateTimeField(default=timezone.now, verbose_name='事件开始时间')
end_time = models.DateTimeField(default=timezone.now, verbose_name='事件结束时间')
pause_time = models.DateTimeField(default=timezone.now, verbose_name='事件暂停时间')
class Meta:
ordering = ['-created']
verbose_name = '事件内容'
verbose_name_plural = verbose_name
def time_interval(self):
time_diff = (self.end_time-timezone.now())
days = time_diff.days
seconds = time_diff.seconds
minutes = seconds // 60 # 得到这些秒换算的分钟整数
second = seconds % 60 # 得到除去分钟后剩余的秒数
hours = minutes // 60
minute = minutes % 60
if self.status.order_number == 6:
return '事件已关闭!'
if days <= -1:
return '处理已超时!'
return '{}天{}时{}分'.format(days, hours, minute)
def __str__(self):
return self.title
def get_content_as_markdown(self):
"""
当使用Mardown功能时,我们需要先让它转义一下特殊字符,然后再解析出Markdown标签。
这样做之后,输出字符串可以安全的在模板中使用。
:return:
"""
return mark_safe(markdown(self.content, safe_mode='escape'))
路由设计
url(r'^event/$', event, name='event'),
url(r'^event-(?P\d+)-(?P\d+)-(?P\d+)-(?P\d+)-(?P\d+).html$', event, name='event_filter'),
视图设计
该视图只需要查看kwargs
有值的情况
def get_group_url_list(url):
"""
将访问的url存储在列表中,用于前端判断
EVENT_MENU_GROUP : 事件菜单组
OTHER_MENU_GROUP : 其他菜单组
:param url:
:return:
"""
group_url_list = list()
group_url_list.append(url)
return group_url_list
# 显示事件列表
def event(request, **kwargs):
print('视图**kwargs的值:', kwargs)
if not kwargs:
# 原来的事件列表和post筛选
# events = EventContent.objects.all()
queryset = EventContent.objects.all()
if request.method == 'POST':
visit_url = reverse('event')
event_url_list = get_group_url_list(visit_url)
filter_event_form = FilterEventForm(request.POST)
if filter_event_form.is_valid():
print('表单验证通过')
user = filter_event_form.cleaned_data['user']
status = filter_event_form.cleaned_data['status']
project = filter_event_form.cleaned_data['project']
category = filter_event_form.cleaned_data['category']
level = filter_event_form.cleaned_data['level']
queryset = queryset.filter(user=user, status=status, project=project, category=category, level=level)
print(queryset)
else:
visit_url = reverse('event')
event_url_list = get_group_url_list(visit_url)
filter_event_form = FilterEventForm()
page = request.GET.get('page', 1)
paginator = Paginator(queryset, settings.PAGE_NUM) # paginator是分页对象
try:
events = paginator.page(page)
except PageNotAnInteger:
events = paginator.page(1)
except EmptyPage:
events = paginator.page(paginator.num_pages)
return render(request, 'event.html',
{
'events': events,
'EVENT_MENU_GROUP': event_url_list,
'filter_event_form': filter_event_form,
'old_filter': True
})
else:
"""
多条件事件筛选
event-(?P\d+)-(?P\d+)-(?P\d+)-(?P\d+)-(?P\d+).html
{'user_id': '0', 'status_id': '0', 'level_id': '0', 'category_id': '0', 'project_id': '0'}
"""
filter_dict = dict()
request_path = request.path
print('请求地址:', request_path)
if kwargs['user_id'] != '0':
filter_dict['user'] = get_object_or_404(User, id=kwargs['user_id'])
if kwargs['status_id'] != '0':
filter_dict['status'] = get_object_or_404(Status, id=kwargs['status_id'])
if kwargs['level_id'] != '0':
filter_dict['level'] = get_object_or_404(Level, id=kwargs['level_id'])
if kwargs['category_id'] != '0':
filter_dict['category'] = get_object_or_404(Category, id=kwargs['category_id'])
if kwargs['project_id'] != '0':
filter_dict['project'] = get_object_or_404(Project, id=kwargs['project_id'])
user_list = User.objects.all().values('id', 'username')
# print(user_list)
status_list = Status.objects.all().values('id', 'status_tag')
# print(status_list)
level_list = Level.objects.all().values('id', 'level_tag')
category_list = Category.objects.all().values('id', 'category_name')
project_list = Project.objects.all().values('id', 'project_name')
url_id_list = kwargs.values() # url中所有id:[0, 0, 0, 0, 0 ]
visit_url = reverse('event_filter', args=url_id_list)
event_url_list = get_group_url_list(visit_url)
queryset = EventContent.objects.filter(**filter_dict)
page = request.GET.get('page', 1)
paginator = Paginator(queryset, settings.PAGE_NUM) # paginator是分页对象
try:
events = paginator.page(page)
except PageNotAnInteger:
events = paginator.page(1)
except EmptyPage:
events = paginator.page(paginator.num_pages)
return render(request, 'event.html',
{
'events': events,
'EVENT_MENU_GROUP': event_url_list,
'user_list': user_list,
'status_list': status_list,
'level_list': level_list,
'category_list': category_list,
'project_list': project_list,
})
模板设计
事件列表
{% if old_filter %}
{% else %}
用户
状态
级别
分类
项目
{% endif %}
链接生成模板标签
使用模板标签,在应用下创建templatetags
的python包,然后创建active.py
文件,需要在模板中通过{% load active %}
引入模板标签。
from django.utils.safestring import mark_safe
from django import template
register = template.Library()
@register.simple_tag
def active_all(request_path, index):
url_part_list = request_path.split('-')
# print(url_part_list)
# ['/event', '0', '0', '0', '0', '0.html']
# 第五组带.html,需要分开判断
if url_part_list[index] == '0' or url_part_list[index] == '0.html':
temp = '''
全部
'''
else:
temp = '''
全部
'''
if index != 5:
url_part_list[index] = '0'
else:
url_part_list[index] = '0.html'
href = '-'.join(url_part_list)
return mark_safe(temp.format(href=href))
@register.simple_tag
def active(request_path, item, index):
url_part_list = request_path.split('-')
# 下面判断中,前面表示 event-0-1-5-1-,后面表示 3.html
if url_part_list[index] == str(item['id']) or url_part_list[index] == str(item['id']) + '.html':
temp = '''
{name}
'''
else:
temp = '''
{name}
'''
if index == 5:
# 第五组有后缀.html,需单独处理
url_part_list[index] = str(item['id']) + '.html'
else:
url_part_list[index] = str(item['id'])
href = '-'.join(url_part_list)
if index == 1:
"""
event-1-0-0-0-0.html
event-2-0-0-0-0.html
event-3-0-0-0-0.html
"""
return mark_safe(temp.format(href=href, name=item['username']))
if index == 2:
return mark_safe(temp.format(href=href, name=item['status_tag']))
if index == 3:
return mark_safe(temp.format(href=href, name=item['level_tag']))
if index == 4:
return mark_safe(temp.format(href=href, name=item['category_name']))
if index == 5:
return mark_safe(temp.format(href=href, name=item['project_name']))
两级分类筛选
模型设计
from django.db import models
from django.utils.timezone import now
class GoodsTag(models.Model):
name = models.CharField(max_length=64, verbose_name='标签名称')
def __str__(self):
return self.name
class Meta:
ordering = ['name', ]
verbose_name = '商品标签' # 后台显示模型名称
verbose_name_plural = verbose_name
# 智能家居、手机、电视、电脑
class FirstCategory(models.Model):
name = models.CharField(max_length=64, verbose_name='分类名称')
def __str__(self):
return self.name
class Meta:
ordering = ['name', ]
verbose_name = '一级分类'
verbose_name_plural = verbose_name
# 小米6、小米8、红米10
class SubCategory(models.Model):
name = models.CharField(max_length=64, verbose_name='分类名称')
first_category = models.ForeignKey(FirstCategory, related_name='sub_categories', verbose_name='上级分类')
def __str__(self):
return self.name
class Meta:
ordering = ['name', ]
verbose_name = '二级分类'
verbose_name_plural = verbose_name
class GoodsInfo(models.Model):
STATUS_CHOICES = (
(1, '上架'),
(2, '下架'),
)
title = models.CharField(max_length=100, verbose_name='标题')
content = models.TextField(blank=True, null=True, verbose_name='正文')
image = models.FileField(upload_to='images/goods/%Y/%m', blank=True, null=True, verbose_name='图片')
status = models.IntegerField(choices=STATUS_CHOICES, default=1, verbose_name='状态')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
publish_time = models.DateTimeField(blank=True, null=True, default=now, verbose_name='发布时间')
updated_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
category = models.ForeignKey(SubCategory, on_delete=models.CASCADE, related_name='goods_info', verbose_name='所属分类')
tags = models.ManyToManyField(GoodsTag, blank=True, verbose_name='标签集合')
def __str__(self):
return self.title
class Meta:
verbose_name = '商品信息'
verbose_name_plural = verbose_name
主路由
urlpatterns = [
url(r'^test/', include('multiple_filter.urls', namespace='test')),
]
应用路由
访问 http://127.0.0.1:8000/test/goods.html 可跳转到 http://127.0.0.1:8000/test/goods-0-0-0-0.html
urlpatterns = [
url(r'^goods.html$', goods, name='goods'),
url(r'^goods-(?P\d+)-(?P\d+)-(?P\d+)-(?P\d+).html', goods, name='goods_filter'),
]
视图
from .models import GoodsTag, FirstCategory, SubCategory, GoodsInfo
from django.shortcuts import get_object_or_404
def goods(request, **kwargs):
if not kwargs:
return redirect('test:goods_filter', first_category_id='0', sub_category_id='0', tags_id='0', status_id='0')
else:
request_path = request.path
print('\n当前请求路径:', request_path, '\n')
print('kwargs:', kwargs) # {'first_category_id': '0', 'sub_category_id': '0', 'tags_id': '0', 'status_id': '0'}
goods_tag_list = GoodsTag.objects.all().values('id', 'name')
first_category_list = FirstCategory.objects.all().values('id', 'name')
sub_category_list = SubCategory.objects.all().values('id', 'name')
status_list = list(map(lambda x: {'id': x[0], 'status': x[1]}, GoodsInfo.STATUS_CHOICES))
filter_dict = dict()
if kwargs['first_category_id'] == '0':
# goods-0-x-x-x.html
if kwargs['sub_category_id'] != '0':
# goods-0-1-x-x.html
sub_category = get_object_or_404(SubCategory, id=kwargs['sub_category_id'])
# 选择二级分类后,由于多对一关系,一级分类也会跟着变化
first_category_list = [{'id': sub_category.first_category.id, 'name': sub_category.first_category.name}]
filter_dict['category'] = sub_category
else:
# 一级分类不为0,需要进行筛选
# goods-1-x-x-x.html
first_category = get_object_or_404(FirstCategory, id=kwargs['first_category_id'])
sub_category_list = first_category.sub_categories.values('id', 'name') # 选择一级分类后获取二级分类的列表
if kwargs['sub_category_id'] != '0':
sub_category = get_object_or_404(SubCategory, id=kwargs['sub_category_id'], first_category=first_category)
# 选择二级分类后,由于多对一关系,一级分类也会跟着变化
first_category_list = [{'id': sub_category.first_category.id, 'name': sub_category.first_category.name}]
filter_dict['category'] = sub_category
if kwargs['tags_id'] != '0':
filter_dict['tags'] = kwargs['tags_id']
if kwargs['status_id'] != '0':
filter_dict['status'] = int(kwargs['status_id'])
goods_list = GoodsInfo.objects.filter(**filter_dict)
return render(request, 'goods.html',
{
'first_category_list': first_category_list,
'sub_category_list': sub_category_list,
'goods_tag_list': goods_tag_list,
'status_list': status_list,
'goods_list': goods_list
})
模板
{% load goods_active %}
多条件筛选
{% active_all request.path 1 %}
{% for first_category in first_category_list %}
{% active request.path first_category 1 %}
{% endfor %}
{% active_all request.path 2 %}
{% for sub_category in sub_category_list %}
{% active request.path sub_category 2 %}
{% endfor %}
{% active_all request.path 3 %}
{% for goods_tag in goods_tag_list %}
{% active request.path goods_tag 3 %}
{% endfor %}
{% active_all request.path 4 %}
{% for status in status_list %}
{% active request.path status 4 %}
{% endfor %}
{% for goods in goods_list %}
【{{ goods.title }}】{{ goods.content }}
{% endfor %}
链接生成模板标签
应用下创建templatetags包,创建 goods_active.py 文件,用来放置模板标签
from django.utils.safestring import mark_safe
from django import template
register = template.Library()
@register.simple_tag
def active_all(current_url, index):
"""
获取当前url,进行值修改拼接
:param current_url: http://127.0.0.1:8000/test/goods-0-0-0-0.html
:param index:
:return:
"""
a_href_active = """
【全部】
"""
a_href_unactive = """
全部
"""
url_part_list = current_url.split('-')
if index == len(url_part_list)-1: # 最后一个带.html要特殊处理
if url_part_list[index] == '0.html':
a_href = a_href_active
else:
a_href = a_href_unactive
url_part_list[index] = '0.html'
else:
if url_part_list[index] == '0':
a_href = a_href_active
else:
a_href = a_href_unactive
url_part_list[index] = '0'
href = '-'.join(url_part_list)
a_href = a_href.format(href=href)
return mark_safe(a_href)
@register.simple_tag
def active(current_url, item, index):
"""
获取当前url,进行值修改拼接
:param current_url: http://127.0.0.1:8000/test/goods-0-0-0-0.html
:param index:
:return:
"""
a_href_active = """
【{name}】
"""
a_href_unactive = """
{name}
"""
url_part_list = current_url.split('-')
if index == len(url_part_list)-1: # 最后一个带.html要特殊处理
if url_part_list[index] == str(item['id']) + '.html':
a_href = a_href_active
else:
a_href = a_href_unactive
url_part_list[index] = str(item['id']) + '.html'
else:
# print(item['id'], type(item['id'])) # item['id']是int类型
if url_part_list[index] == str(item['id']):
a_href = a_href_active
else:
a_href = a_href_unactive
url_part_list[index] = str(item['id'])
href = '-'.join(url_part_list)
if index in range(1, 4):
a_href = a_href.format(href=href, name=item['name'])
if index == len(url_part_list)-1:
a_href = a_href.format(href=href, name=item['status'])
return mark_safe(a_href)
多对多模型进行筛选
模型
# 课程分类
class Category(models.Model):
weight = models.IntegerField(verbose_name='权重(按从大到小排列)', default=0)
name = models.CharField(max_length=32, verbose_name='分类名称')
class Meta:
verbose_name = '分类方向'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
# 编程语言,一个课程分类里可能有多种编程语言,一种编程语言可能存在不同的课程分类
class Code(models.Model):
weight = models.IntegerField(default=0, verbose_name='权重(按从大到小排列)')
name = models.CharField(max_length=32, verbose_name='编程语言')
category = models.ManyToManyField(Category, related_name='codes', verbose_name='课程分类')
class Meta:
verbose_name = '编程语言'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
# 课程详情
class Course(models.Model):
STATUS_CHOICE = (
(0, '下线'),
(1, '上线')
)
LEVEL_CHOICE = (
(1, '初级'),
(2, '中级'),
(3, '高级')
)
status = models.IntegerField(choices=STATUS_CHOICE, default=1, verbose_name='状态')
level = models.IntegerField(choices=LEVEL_CHOICE, default=1, verbose_name='难度级别')
category = models.ForeignKey(Category, null=True, blank=True, related_name='courses', verbose_name='课程分类')
weight = models.IntegerField(default=0, verbose_name='权重(按从大到小排列)')
title = models.CharField(max_length=32, verbose_name='标题')
summary = models.CharField(max_length=100, verbose_name='简介')
image = models.ImageField(upload_to='images/course/%Y/%m', verbose_name='图片')
video_url = models.CharField(max_length=256, verbose_name='视频地址')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
verbose_name = '课程详情'
verbose_name_plural = verbose_name
def __str__(self):
return self.title
路由
urlpatterns = [
# 访问形式http://127.0.0.1:8000/test/course-0-0-0.html,
# 第一个0代表课程分类,第二个0代表编程语言,第三个0代表课程级别
# 0代表全部,然后递增,当选择课程分类中的第一项,第一个0就会变成1
url(r'^course-(?P\d+)-(?P\d+)-(?P\d+).html', course, name='course'),
]
视图
def course(request, *args, **kwargs):
print(args, kwargs) # () {'code_id': '0', 'category_id': '0', 'level_id': '0'}
request_path = request.path # http://127.0.0.1:8000/test/course-0-0-0.html
# 筛选字典
filter_dict = dict()
code_list = Code.objects.all().values('id', 'name')
category_list = Category.objects.all().values('id', 'name')
level_list = list(map(lambda x: {'id': x[0], 'name': x[1]}, Course.LEVEL_CHOICE))
if kwargs['code_id'] == '0':
if kwargs['category_id'] != '0':
category_list = Category.objects.filter(id=kwargs['category_id']).values('id', 'name')
category = get_object_or_404(Category, id=kwargs['category_id'])
# 分类不是全部,得到这个分类对应的所有编程语言
code_list = category.codes.values('id', 'name')
# 筛选这一分类
filter_dict['category'] = category
else:
# 如果编程语言不为0,则获取对应的编程语言
code = get_object_or_404(Code, id=kwargs['code_id'])
# 得到编程语言对应的所有分类
categories = code.category.all()
category_list = categories.values('id', 'name')
# 筛选课程在这些分类的结果
filter_dict['category__in'] = categories
if kwargs['category_id'] != '0':
# 如果分类不为0,对分类进行筛选,得到该编程语言和该分类下的结果
category = get_object_or_404(categories, id=kwargs['category_id'])
code_list = category.codes.values('id', 'name')
filter_dict['category'] = category
if kwargs['level_id'] != '0':
filter_dict['level'] = int(kwargs['level_id'])
filter_dict['status'] = 1
course_list = Course.objects.filter(**filter_dict)
return render(request, 'course.html',
{
'category_list': category_list,
'code_list': code_list,
'level_list': level_list,
'course_list': course_list,
})
模板
{% load course_active %}
多条件筛选多对多模型
选择:
编程语言:
{% active_all request.path 1 %}
{% for code in code_list %}
{% active request.path code 1 %}
{% endfor %}
课程分类:
{% active_all request.path 2 %}
{% for category in category_list %}
{% active request.path category 2 %}
{% endfor %}
课程信息:
{% active_all request.path 3 %}
{% for level in level_list %}
{% active request.path level 3 %}
{% endfor %}
视频:
{% for course in course_list %}
《{{ course.title }}》{{ course.summary }}
{% endfor %}
链接生成模板标签
应用下创建templatetags包,创建 course_active.py 文件,用来放置模板标签
from django.utils.safestring import mark_safe
from django import template
register = template.Library()
@register.simple_tag
def active_all(current_url, index):
"""
获取当前url, course-1-1-2.html
:param current_url:
:param index:
:return:
"""
url_part_list = current_url.split('-')
if index == 3:
if url_part_list[index] == '0.html':
temp = '【全部】'
else:
temp = '全部'
url_part_list[index] = '0.html'
else:
if url_part_list[index] == '0':
temp = '【全部】'
else:
temp = '全部'
url_part_list[index] = '0'
url_str = '-'.join(url_part_list)
temp = temp % (url_str, )
return mark_safe(temp)
@register.simple_tag
def active(current_url, item, index):
"""
course-0-0-1.html
:param current_url:
:param item:
:param index:
:return:
"""
# print('\n当前访问地址:', current_url, item, index, type(index))
url_part_list = current_url.split('-')
# print(url_part_list) # ['/test/course', '0', '0', '0.html']
if index == 3:
if str(item['id']) == url_part_list[3].split('.')[0]: # 如果当前标签被选中
temp = '【%s】'
else:
temp = '%s'
url_part_list[index] = str(item['id']) + '.html' # 拼接对应位置的url
else:
if str(item['id']) == url_part_list[index]:
temp = '【%s】'
else:
temp = '%s'
url_part_list[index] = str(item['id'])
url_str = '-'.join(url_part_list) # 拼接整体url
# print(url_str)
temp = temp % (url_str, item['name']) # 生成对应的a标签
return mark_safe(temp)