本文是我们利用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