• Django中Q搜索的简单应用


    本节涉及:

    1.Q搜索在前后端的设计

    2.Django中Queryset对象的序列化(由后端扔给前端的数据必然会经过序列化)

    3.前端动态地构造表格以便显示(动态创建DOM对象)

    思路:

    用户通过前端查询数据库内容时,可添加多个搜索框,一个搜索框内可以输入多个条件。同一搜索框内的条件是或OR关系,不同搜索框间是与AND关系。如搜索图书,每条图书信息包括名称、页数、印刷日期、类型,在一个搜索框内可选择搜索书名,以中文逗号分隔即可以书名同时搜索多本图书,同一搜索框内就是OR关系。又可以再添加输入框,这时就可以再添加类型、价格等信息,缩小搜索范围,这些搜索框之间就是AND关系。条件传递给后端后,后端拿到结果,处理后再抛给前端,由前端在页面展示。

    代码

    数据库信息

    class BookType(models.Model):
        caption = models.CharField(max_length=32)
    
    
    class Book(models.Model):
        name = models.CharField(max_length=32)
        pages = models.IntegerField()
        price = models.DecimalField(max_digits=10, decimal_places=2)
        pubdate = models.DateField()
        # 外键
        book_type = models.ForeignKey(BookType, on_delete=models.CASCADE)
    
        def __str__(self):
            return "Book Object: %s %sp %s元" % (self.name, self.pages, self.price)
    数据库信息

    HTML代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <script src="/static/js/jquery-2.1.4.min.js"></script>
        <title>Index页面</title>
    </head>
    <body>
        <!--搜索框-->
        <div class="condition">
            <div class="item ">
                <!--点击后会添加一个搜索框-->
                <div class='icon' onclick="AddCondition(this);">+</div>
                <div>
                    <!--选择不同的搜索条件时,会触发方法-->
                    <select onchange="ChangeName(this);">
                        <option value="name">书名</option>
                        <option value="book_type__caption">类型</option>
                        <option value="price">价格</option>
                    </select>
                </div>
                <div class="left"><input type="text" name="name"/></div>
            </div>
        </div>
        <div>
            <!--点击触发搜索,向后端传递-->
            <input type="button" onclick="Search();" value="搜索">
        </div>
        <!--展示区-->
        <div class="container">
        </div>
    </body>
    index.html

    JS代码

    <script>
        function AddCondition(ths){
            // dom对象转换为jquery对象
            var new_tag = $(ths).parent().clone();
            // 新添加的搜索框的按钮改为减号,定义删除方法
            new_tag.find('.icon').text('-');
            new_tag.find('.icon').attr('onclick','RemoveCondition(this);');
            $(ths).parent().append(new_tag);
        }
    
        // 新添加的搜索框还可删除
        function RemoveCondition(ths){
            $(ths).parent().remove();
        }
    
        //
        function ChangeName(tag){
            // 获取搜索条件value属性的值
            var v = $(tag).val();
            // 定义input的name属性
            // 这里主要是为了随着用户选择不同的搜索条件
            // 也向后端传递不同的搜索条件,Q搜索中会用到
            $(tag).parent().next().find('input').attr('name', v);
        }
    
        function Search(){
            // 获取所有用户输入的内容并提交
            // 将要给后端传递的字典
            var post_data_dict = {};
            // 循环所有的input
            $('.condition input').each(
                function(){
                   // 操作jquery对象
                   // 获得搜索条件
                   var attr_name = $(this).attr('name');
                   // 获取用户输入
                   var value_list = $(this).val().split(',');
                   post_data_dict[attr_name] = value_list;
                }
            );
            // 将要传递的字典序列化
            var post_data_str = JSON.stringify(post_data_dict);
            $.ajax({
                url: '/index/',
                type: 'POST',
                data: {'post_data': post_data_str},
                // 此参数使得后端传来的json会被解析为js对象
                dataType: "json",
                success: function(ret){
                    if(ret.status){
                        // 清空展示柜,避免重复显示
                        $('.container').empty();
                        // {'status':true, 'content': [{},{}]}
                        $.each(ret.content, function(useless_key, value_dict){
                            // 有多少条数据就有多少个表
                            // 形式为一表一行n列
                            var table = document.createElement('table');
                            // 每个表有一行数据
                            var tr = document.createElement('tr');
                            // {'name':'xx', 'pages':540}
                            $.each(value_dict, function(key, val){
                                // 书籍信息的每一项内容对应一列 td
                                var td = document.createElement('td');
                                // 为td标签加上class属性,值为其键
                                td.setAttribute('class', key);
                                // 为td标签加上文本, 即其值
                                td.innerText = val;
                                td.setAttribute('width', 100);
                                // 每次创建一个td标签都添加到tr上
                                tr.appendChild(td);
                            });
                            // 将tr标签加入table中
                            table.appendChild(tr);
                            table.setAttribute('border', 1);
                            // 将table加入展示柜中
                            $('.container').append(table);
                        });
                    }else{
                        alert(ret.message);
                    }
                }
            })
        }
    </script>
    </html>
    JS代码

    后端代码(views.py)

    from django.shortcuts import render
    from django.shortcuts import HttpResponse
    from app01 import models
    # Create your views here.
    # 业务处理逻辑
    import json
    from decimal import Decimal
    from datetime import date
    
    
    class DecimalDatetimeEncoder(json.JSONEncoder):
    
        def default(self, o):
            if isinstance(o, Decimal):
                return str(o)
            elif isinstance(o, date):
                return o.strftime('%Y-%m-%d')
            return json.JSONEncoder.default(self, o)
    
    
    def index(request):
        # 定义要给前端传递的字典
        post_ret_dict = {'status': True, 'content': None}
        if request.method == 'POST':
            try:
                # 获取前端抛来的字符串
                post_data_str = request.POST.get('post_data', None)
                # 反序列化
                post_data_dict = json.loads(post_data_str)
                # post_data_dict: {'name': ['nameA', 'nameB'],'price':[20,30,40] }
                from django.db.models import Q
                # 构造Q搜索
                condition = Q()
                for k, v in post_data_dict.items():
                    # 同一搜索框内(同一条件)的输入是OR关系(主关系)
                    q = Q()
                    q.connector = 'OR'
                    for item in v:
                        # 这里的k就是前端传来的name属性的值,也就是搜索条件(子关系)
                        q.children.append((k, item))
                    condition.add(q, 'AND')
                # 将Q搜索直接作为filter的条件传入,并再用values方法取到想要的值
                # 这里用book_type__caption双下划线的形式找到外键表的caption域的值
                # 返回值仍是Queryset对象,可通过list转换为列表
                list_ret = list(models.Book.objects.filter(condition).values('name', 'pages', 'price', 'pubdate', 'book_type__caption'))  # QuerySet
                post_ret_dict['content'] = list_ret
            except Exception as e:
                post_ret_dict['status'] = False
            # 最后再序列化要给前端的内容
            # 注意价格是Decimal类型,印刷日期是Datetime类型,这两类都不是python内置类型,无法直接用json.dumps方法序列化
            # 这里可以借助第二个参数,构造一个自定义的解析类,自行处理
            post_ret_str = json.dumps(post_ret_dict, cls=DecimalDatetimeEncoder)
            return HttpResponse(post_ret_str)
            
            # django提供的序列化方法,但是无法获得外键表的对应值,不能在使用values方法后序列化
            # from django.core.serializers import serialize
            # ret = models.Book.objects.filter(condition)  # QuerySet
            # str_ret = serialize('json', ret)
            # print(str_ret)
            # return HttpResponse(str_ret)
        return render(request, "index.html")
    views.py
  • 相关阅读:
    AGC 015 E
    CF 1041 F. Ray in the tube
    AGC 005 D
    CF 348 D. Turtles
    2069: [POI2004]ZAW
    AGC 007 D
    zhengruioi 470 区间
    2653: middle
    Django 源码安装及使用
    Django MTV模型思想
  • 原文地址:https://www.cnblogs.com/yifeixu/p/8877734.html
Copyright © 2020-2023  润新知