django rest_framework实战,前端原生js

本人是python业余选手,公司的系统是我业余时间开发的,一直在维护升级。
前段时间更新了一个业务人员下单的功能

在这里插入图片描述

上方的是本地开发的页面和数据,实际业务中,订单会很多,每天会增加几十条订单记录,那订单的筛选必不可少,而且是多条件查询,这是刚需。

先对需求做一个整理:

  • 多条件交叉查询
  • 异步请求

程序采用的是django 2.1.8,跑在本地的windows sever2008上,使用apache2.4部署,前端使用的是django前端模板语言,那么以下几点是可以明确的

  • django rest_framework
  • 前端使用原生js写请求和数据渲染(jq还未学习,不熟悉)

js原生异步请求思路

在这里插入图片描述

数据渲染思路

异步请求有响应后,不管有无数据,都需要清除原来页面的数据以及分页

if(有数据){
	清除原有数据
	清除原有分页
	创建元素 //和原来一模一样的
	渲染数据 //使用responseText()方法把后端响应的json数据转换成js对象
}else(无数据){
	清除原有数据
	清除原有分页
	提示:暂无数据
}

前端流程的处理思路

前端:

  • 抽象ajax模块,处理请求和响应(按理说,响应部分也需要抽象出来,这里偷懒了,直接写在了ajax模板中)
  • 抽象数据过滤模块,使用addEventListner()监听多条件点击事件,拼接url,发起异步请求
  • 抽象响应后的分页addEventListner()点击监听模块,如果存在page参数,则拼接到url中,发起异步请求
  • 分页监听模块,过滤数据后进行调用(如果数据存在)

核心代码展示

  • 获取元素,声明变量
// 获取元素,页面上的4个过滤条件
var orderStatus = document.querySelector("#order-status-items").children
var expenseStatus = document.querySelector("#expense-status-items").children
var sales = document.querySelector("#sales-items").children
var designer = document.querySelector("#designer-items").children

// 获取当前的url,后面做url拼接
var host = document.location

// 请求后的分页,分页是在ajax请求后再生成的,所以要在数据过滤模块外进行保存该变量
var pages

// 定义4个过滤条件的url默认值,xx-xx-all代表“全部xx”,如果前端选择的是其他过滤条件,则声明为实际的过滤条件,这个值由前端元素中声明
var orderStatusCode = "order-status-all";
var expenseStatusCode = "expense-status-all";
var salesCode = "sales-status-all";
var designerCode = "designer-status-all";


//异步请求和响应处理模块
var JsonRequest = function(orderStatusCode, expenseStatusCode, salesCode, designerCode, page) {
	var osc = orderStatusCode
	var esc = expenseStatusCode
	var sc = salesCode
	var dc = designerCode
	// 1.创建XMLHttprequest对象
	var xhr = new XMLHttpRequest();
	// 2.调用open方法打开URL
	// 如果分页参数page存在,即在url后面拼接&page=x,不存在便只拼接过滤的url
	if (page) {
		xhr.open("get", "filter/?osc=" + osc + "&esc=" + esc + "&sc=" + sc + "&dc=" + dc + "&page=" + page);
	} else {
		xhr.open("get", "filter/?osc=" + osc + "&esc=" + esc + "&sc=" + sc + "&dc=" + dc);
	}
	// 3.发送异步请求
	xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	xhr.send()
	// 4.侦听请求状态码
	xhr.onreadystatechange = function() {
		if (xhr.readyState == 4 && xhr.status == 200) {
			// 获取返回的结果,使用json解析
			var res = JSON.parse(xhr.responseText)
			var page_obj = res["data"]

			//清除当前页面的数据
			var tbody = document.getElementById("order-body");
			if (tbody) {
				tbody.parentNode.removeChild(tbody);
			}
			
			// 判断能否筛选到数据 res["flag"] == true
			if(res["flag"] == true){
				// 有数据则:
				// 1.清除当前页面的数据和分页
				// 2.写入请求到的数据和分页
				// 3.启动分页点击侦听,请求分页数据
				
				//写入过滤后的数据
				// 创建table的子元素tbody,并设置id属性为order-body
				var tbody = document.createElement("tbody");
				tbody.setAttribute("id", "order-body");
				var table = document.querySelector("table");
				table.appendChild(tbody);
				
				// 清除“哥,暂无结果”的提示,如果存在的话
				var h4 = document.querySelector("#nomsg");
				if(h4){
					h4.parentNode.removeChild(h4);
				}
				
				// 创建tbody的子元素tr、子孙元素th和td,创建的规范符合原有的table规范
				// 创建tr,数据有几行就创建几个tr,使用循环处理
				for (i = 0; i < page_obj.length; i++) {
					console.log("遍历获取的数据:", page_obj[i]);
					var tr = document.createElement("tr");
				
					// 创建th,即序号
					var th = document.createElement("th");
					th.setAttribute("scope", "row");
					th.innerHTML = i + 1;
					tr.appendChild(th);
				
					// 创建td
					var tdTitle = document.createElement("td");
					tdTitle.innerHTML = "<a href=" + host.origin + "/orders/detail/" + page_obj[i]["id"] + ">" + page_obj[i]["title"] +
						"</a>";
					tr.appendChild(tdTitle);
				
					var tdCompany = document.createElement("td");
					tdCompany.innerHTML = page_obj[i]["company"];
					tr.appendChild(tdCompany);
				
					var tdCreateTime = document.createElement("td");
					var dateTime = formatDate(page_obj[i]["created_time"]);
					tdCreateTime.innerHTML = dateTime;
					tr.appendChild(tdCreateTime);
				
					var tdEndTime = document.createElement("td");
					var endTime = formatDate(page_obj[i]["end_time"]);
					tdEndTime.innerHTML = endTime;
					tr.appendChild(tdEndTime);
				
					var tdOrderStatus = document.createElement("td");
					tdOrderStatus.innerHTML = page_obj[i]["order_status"];
					tr.appendChild(tdOrderStatus);
				
					var tdExpenseStatus = document.createElement("td");
					tdExpenseStatus.innerHTML = page_obj[i]["expense_status"];
					tr.appendChild(tdExpenseStatus);
				
					var tdDesigner = document.createElement("td");
					tdDesigner.innerHTML = page_obj[i]["designer"];
					tr.appendChild(tdDesigner);
				
					var tdSales = document.createElement("td");
					tdSales.innerHTML = page_obj[i]["sales"];
					tr.appendChild(tdSales);
				
					// 把tr添加到tbody中
					tbody.appendChild(tr)
				}
				
				// 清除分页页面:原有的分页及请求后产生的分页,请求后产生的分页,它们使用一致的id,以便简化代码
				var defaultPaginator = document.querySelector("#dj-pagination");
				// console.log(defaultPaginator);
				if (defaultPaginator) {
					defaultPaginator.parentNode.removeChild(defaultPaginator);
				}
				// 写入分页页面
				var Ulpaginator = document.createElement("ul");
				// 写入id,实现每次点击请求时都会清除该页面,然后再重写
				Ulpaginator.setAttribute("id", "dj-pagination");
				// 写入一个自定义属性,以此来区别该ul是异步请求后生成的
				Ulpaginator.setAttribute("mark", "true");
				// 写入样式,调用bootstrap默认样式
				Ulpaginator.classList.add("pagination", "mt-3")
				var container = document.querySelector("#use-for-ajax");
				container.appendChild(Ulpaginator);
				
				// 写入li,后端传过来的res["page_nums"]是分页后的页码总数,可以此来创建分页
				var pageNum = res["page_nums"]
				for (var i = 0; i < pageNum; i++) {
					var pageLi = document.createElement("li");
					pageLi.innerHTML = i + 1;
					pageLi.classList.add("page-item", "page-link");
					pageLi.style.cursor = "pointer";
					// console.log(pageLi);
					Ulpaginator.appendChild(pageLi);
				}
				
				// 异步请求清除原有分页数据后,才能获取新的分页,并保存在pages中(上面有声明该变量)
				pages = document.querySelector("#dj-pagination").children;
				
				// 启动分页点击侦听器
				pageListner(pages);

			}else{
				// 无数据,则:
				// 输出无数据的提示
				// 依然要清除原有的分页和原有的页面数据

				// 清除原有的分页页面
				var defaultPaginator = document.querySelector("#dj-pagination");
				// console.log(defaultPaginator);
				if (defaultPaginator) {
					defaultPaginator.parentNode.removeChild(defaultPaginator);
				}
				
				// 提示
				var h4 = document.querySelector("#nomsg");
				if(h4){
					return;
				}else{
					var h4 = document.createElement("h4");
					h4.innerText = "哥,暂无数据";
					h4.setAttribute("id", "nomsg");
					var container = document.querySelector("#use-for-ajax");
					container.appendChild(h4);
				}
			}
		}
	}
}


//条件过滤点击侦听函数
var vanListner = function(nav) {
	for (i = 0; i < nav.length; i++) {
		nav[i].addEventListener("click", function() {
			for (i = 0; i < nav.length; i++) {
				nav[i].className = "";
			}
			this.className = "order_status_active";

			// 获取父元素的id,以此区分当前元素的id值归属于哪部分				
			// 订单code
			if (this.parentNode.getAttribute("id") == "order-status-items") {
				tarGetId = this.getAttribute("id");
				orderStatusCode = tarGetId;
				// 发送异步请求
				JsonRequest(orderStatusCode, expenseStatusCode, salesCode, designerCode)
			}

			// 费用code
			if (this.parentNode.getAttribute("id") == "expense-status-items") {
				tarGetId = this.getAttribute("id");
				expenseStatusCode = tarGetId;
				// 发送异步请求
				JsonRequest(orderStatusCode, expenseStatusCode, salesCode, designerCode)
			}

			// 下单人code
			if (this.parentNode.getAttribute("id") == "sales-items") {
				tarGetId = this.getAttribute("id");
				salesCode = tarGetId;
				// 发送异步请求
				JsonRequest(orderStatusCode, expenseStatusCode, salesCode, designerCode)
			}

			// 设计code
			if (this.parentNode.getAttribute("id") == "designer-items") {
				tarGetId = this.getAttribute("id");
				designerCode = tarGetId;
				// 发送异步请求
				JsonRequest(orderStatusCode, expenseStatusCode, salesCode, designerCode)
			}
		})
	}
}


// 分页点击侦听模块,该模块需要在异步请求模块中的过滤有数据后立即调用
var pageListner = function(pages) {
	// 分页侦听
	// 点击后,获取异步请求后的分页数据
	for (var i = 0; i < pages.length; i++) {
		pages[i].addEventListener("click", function() {
			// 去掉激活样式
			for (var i = 0; i < pages.length; i++) {
				pages[i].classList.remove("active");
			}
			this.classList.add("active");
			// 获取当前请求参数,发送异步请求
			JsonRequest(orderStatusCode, expenseStatusCode, salesCode, designerCode, this.innerText);
		})
	}
}

以上代码写在一个叫js/order_filter.js的文件中,在前端页面中引入,并调用过滤条件侦听器

<script src="{% static 'js/order_filter.js' %}" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 调用函数,导航点击激活、拼接url和发送异步请求
vanListner(orderStatus);
vanListner(expenseStatus);	
vanListner(sales);
vanListner(designer);
</script>

django后端处理逻辑

django中有个叫order的app,用来处理订单。

  • 在order下创建一个序列化器文件: serializer.py 这个文件的命名不能使用serializers,不然会和serializers 模块冲突
    在这里插入图片描述
    serializer.py中创建序列化器
from rest_framework import serializers
from . models import Order

class OrderSerializer(serializers.ModelSerializer):

    order_status = serializers.CharField(source='get_order_status_display')
    company = serializers.CharField(source='company.company_name')
    expense_status = serializers.CharField(source='get_expense_status_display')
    sales = serializers.CharField(source='sales.username')
    designer = serializers.CharField(source='designer.username')

    class Meta:
        model = Order
        fields = ['id', 'title', 'order_status', 'expense_status', 'sales', 'designer', 'company','items', 'created_time', 'end_time']

对于Forenkeychoices 的字段,需要在序列化器中做友好可视化,不然前端只能看到id或choices的选项。

views.py中处理响应

from order.models import Order
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.pagination import PageNumberPagination
from order import serializer


# 重写PageNumberPagination方法,获取页码总数
class DRFPagination(PageNumberPagination):
    def paginate_queryset(self, queryset, request, view=None):
        """
        Paginate a queryset if required, either returning a
        page object, or `None` if pagination is not configured for this view.
        """
        page_size = self.get_page_size(request)
        if not page_size:
            return None

        paginator = self.django_paginator_class(queryset, page_size)
        page_number = request.query_params.get(self.page_query_param, 1)
        if page_number in self.last_page_strings:
            page_number = paginator.num_pages

        try:
            self.page = paginator.page(page_number)
        except InvalidPage as exc:
            msg = self.invalid_page_message.format(
                page_number=page_number, message=str(exc)
            )
            raise NotFound(msg)

        if paginator.num_pages > 1 and self.template is not None:
            # The browsable API should display pagination controls.
            self.display_page_controls = True

        self.request = request
        return list(self.page), paginator.num_pages


class OrderSerializerView(APIView):
    def post(self, request, *args, **kwargs):
        print(request.data)
        obj = code_handler(osc=request.data['osc'], esc=request.data['esc'], sc=request.data['sc'], dc=request.data['dc'])

        #创建分页对象
        page_numb_paginator = PageNumberPagination()
        page_obj = page_numb_paginator.paginate_queryset(queryset=obj, request=request, view=self)
        json_obj = serializer.OrderSerializer(instance=page_obj, many=True)
        return Response(json_obj.data)
    
    def get(self, request, *args, **kwargs):
        osc = request.GET.get('osc')
        esc = request.GET.get('esc')
        sc = request.GET.get('sc')
        dc = request.GET.get('dc')
        obj = code_handler(osc=osc, esc=esc, sc=sc, dc=dc)
        # page_numb_paginator = PageNumberPagination() 原生调用,后来继承重写,增加了page_num的返回
        page_numb_paginator = DRFPagination()
        page_obj, num = page_numb_paginator.paginate_queryset(queryset=obj, request=request, view=self)
        json_obj = serializer.OrderSerializer(instance=page_obj, many=True)

        msg = ''
        flag = True
        drf_data = {}
        
        drf_data['page_nums'] = num
        drf_data['data'] = json_obj.data
        print(page_obj)
        print(len(page_obj))
        if len(page_obj) < 1:
            msg = '暂无结果,请重新选择'
            flag = False

        if len(page_obj) >= 1:
            msg = '获取成功'          
        drf_data['msg'] = msg
        drf_data['flag'] = flag
        return Response(drf_data)

核心代码就这么多,第一次写原生js请求和渲染json数据,过程比较吃力,各种学习才能完整理解其中的原理

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值