本文是我们利用Django开发智能文档管理系统smartdoc的终章。对于智能文档管理,最不可或缺的功能就是搜索。在Django实战教程: 开发企业级应用智能文档管理系统smartdoc(1)里,我们已经搭好了基础框架,并具体实现了以下几个功能性页面(见标黄部分)。在第2部分教程里,我们讲解了不同用户的权限控制。本文将详细讲解开发最后一个功能性页面。
-
产品列表,产品详情,产品创建和产品修改 - 4个页面
-
类别列表,类别详情,类别创建和类别修改 - 4个页面
-
文档列表,文档详情,文档创建和文件修改 - 4个页面
-
文档一般搜索和Ajax搜索 - 1个页面
项目需求
对于任何IT项目开发,能快速准确描述项目需求是一项技能。本教程中smartdoc对于文档搜索的需求如下, 我们稍后来看看如何实现。
-
用户可以根据标题,产品名字和代码来搜索相关文档
-
文档列表或其它页面上带有搜索框(见图1),用户输入关键词并按提交后,跳转到文档搜索页面,显示结果。
-
在文档搜索页面,用户输入关键词(不用按提交按钮), 实时显示搜索结果 - Ajax搜索(见图2)。
实时文档搜索页面
urls.py
我们需要设计两个urls, 一个负责接收用户输入的关键词q,在前端返回搜索结果。一个负责在后台接收实时变化的关键词q,并给前端提供json格式数据。
urlpatterns = [ # 文档搜索 path('document/search/', views.document_search, name='document_search'), # Ajax返回json数据 path('ajax/search/', views.doc_ajax_search, name='doc_ajax_search'), ]
views.py
我们需要编写两个视图方法对应两个urls。因为我们需要使用的搜索条件很多,所以我们调用了Q方法。document_search方法负责接收用户提交的关键词q,查询数据库并返回HTTP页面和搜索结果列表。doc_ajax_search实时接收用户数据的关键词q,并给前端返回一个包含搜索结果json格式的字典。
from .models import Product, Category, Document from django.db.models import Q from django.http import JsonResponse, HttpResponse from django.views.decorators.csrf import csrf_exempt import json import datetime def document_search(request): q = request.GET.get('q', None) if q: document_list = Document.objects.filter(Q(title__icontains=q) | Q(product__name__icontains=q) | Q(product__code__icontains=q)) context = {'document_list': document_list} return render(request, 'smartdoc/document_search.html', context) return render(request, 'smartdoc/document_search.html',) def doc_ajax_search(request): q = request.GET.get('q', None) if q: document_list = Document.objects.filter(Q(title__icontains=q) | Q(product__name__icontains=q) | Q(product__code__icontains=q)) data = [] for document in document_list: data.append({"title": document.title, "product_name": document.product.name, "category_name": document.category.name, "format": document.doc_file.url.split('.')[-1].upper(), "size": "{:.1f}KB".format(document.doc_file.size/1024), "version": document.version_no, "date": document.mod_date, "product_id": document.product.id, "id": document.id, "url": document.doc_file.url, }) json_data = json.dumps(data, cls=MyEncoder) return HttpResponse(json_data) class MyEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, datetime.datetime): return obj.strftime('%Y-%m-%d') elif isinstance(obj, datetime.date): return obj.strftime('%Y-%m-%d') return json.JSONEncoder.default(self, obj)
由于Datetime类型数据无法被json序列化,所以我们必需定义MyEcoder,把日期转成我们想要的字符串格式。除了日期和时间外,还有很多Python类型数据不能直接转成json格式,比如单个的对象(object), 二进制(bytes), 方法(method), QuerySet(查询集), ValueQuerySet和集合(set)。Django自带的serializers也能解决一些数据Json序列化问题,但更强大的显然是Django Rest Framework,我们后面会专题介绍。
template
# smartdoc/document_search.html
模板逻辑如下所示:
-
当有{{ document_list }}时,直接显示搜索结果。
-
每当输入框元素#q里的值有变化时,将该值通过ajax发送到/smartdoc/ajax/search/。
-
后台/smartdoc/ajax/search/收到ajax请求后,返回json字典格式数据。
-
前端如果成功收到json数据,对其遍历,并显示在#id=result的元素里。
{% extends "smartdoc/base.html" %} {% block content %} <h3>搜索文档</h3> <form action=" " role="search" method="get"> {% csrf_token %} <div class="input-group col-md-12"> <input type="text" name="q" id="q" class="form-control" placeholder="搜索产品名称,代码或文档标题"> <span class="input-group-btn"> <button class="btn btn-default form-control" type="submit" value="submit"> <span class="glyphicon glyphicon-search"></span> </button> </span> </div> </form> <table class="table table-striped" id="result"> {% if document_list %} <thead> <tr> <th>标题</th> <th>产品</th> <th>类别</th> <th>格式</th> <th>大小</th> <th>修改日期</th> <th>版本</th> <th>查看</th> <th>下载</th> </tr> </thead> <tbody> {% for document in document_list %} <tr> <td> {{ document.title }} </td> <td> {{ document.product.name }} </td> <td> {{ document.category.name }} </td> <td> {{ document.get_format }} </td> <td> {{ document.doc_file.size | filesizeformat }} </td> <td> {{ document.mod_date | date:"Y-m-d" }} </td> <td> {{ document.version_no }} </td> <td> <a href="{% url 'smartdoc:document_detail' document.product.id document.id %}"><span class="glyphicon glyphicon-eye-open"></span></a> </td> <td> <a href="{{ document.doc_file.url }}"><span class="glyphicon glyphicon-download"></span></a> </td> </tr> {% endfor %} </tbody> {% endif %} </table> {% endblock %} {% block js %} <script> $("#q").bind('input propertychange', function() { var q = $(this).val(); $.ajax({ url: '/smartdoc/ajax/search/', data: { 'q': q, }, type: 'GET', dataType: 'json', success: function (data) { var content= '<thead><tr>' + '<th>标题</th>' + '<th>产品</th>' + '<th>类别</th>' + '<th>格式</th>' + '<th>大小</th>' + '<th>修改日期</th>' + '<th>版本</th>' + '<th>查看</th>' + '<th>下载</th>' + '</tr></thead><tbody>'; $.each(data, function(i, item) { content = content + '<tr><td>' + item['title'] + '</td><td>' + item['product_name'] + '</td><td>' + item['category_name'] + '</td><td>' + item['format'] + '</td><td>' + item['size'] + '</td><td>' + item['date'] + '</td><td>' + item['version'] + '</td><td>' + "<a href='/smartdoc/product/" + item['product_id'] + "/document/" + item['id'] + "'> 查看</a>" + '</td><td>' + "<a href='" + item['url'] + "'> 下载</a>" + '</td></tr>' }); content = content + "</tbody>" $('#result').html(content) }, }); }); </script> {% endblock %}
Ajax技术的关键在于后台是否能正确地返回json格式的数据,这和其它Web API是一个道理。我们是可以直接在浏览器中访问ajax对应的urls,看其能否提供json格式数据的,如下所示。里面的乱码是unicode格式的汉字,可以在前端正确显示的。
小结
本系列教程详细介绍了如何利用Django开发一个企业级应用smartdoc智能文档管理系统,并重点分析了如何控制和管理用户权限,以及实现Ajax快速搜索。全部代码如下,仅用于学习目的,希望对大家有所帮助。
-
https://github.com/shiyunbo/django-smartdoc
大江狗
2018.8.26