基于Django的后台支撑与基于JS、HTML前台之间的交互探讨

记录一个关键问题,导出的models.py可能跟编译器有关或它默认的就是utf16,使用manage.py进行运行可能不会发生错误,但是使用uwsgi进行运行会报错!!!所以导出后的models.py要将编码转化为utf8的格式。

这个错误发生于uwsgi的启动阶段,直接进入页面会在页面显示如下错误。

--------------------------------------------------------------------------------------

django使用的设计模式是MTV(models/temples/views)模式

目前我工作所使用的django使用方式并没有使用model,目前处于摸索阶段,不过先写出数据库反向输入model的方法:

python manage.py inspectdb > models.py

先进入manage.py所在的目录,然后根据你要操作的app进行输出,我这边manage.py与models.py在同一目录,所以只需要写名字即可。

目前我工作使用的模式是前台用js和html支撑,然后后台由python支撑,起初项目启动时只对MTV一知半解,后面发现解耦的关键性作用,可是呢,问题是目前的代码情况来说,并不构成解耦,光js返回数据与前台之间就可能存在一定的关联,很难完整解耦。

因此突然感觉可能是models没有使用的问题,所以在此稍加探索。

----------------------------------------------------------------------------------------

django的大体结构,django的主要设计思想就是解耦的过程,让每个模块各司其职,只依靠接口进行交互:

django
    |--urls
    |--views
    |--templates
    |--static

urls记录了路由:

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'check_board$', views.check_board),
    url(r'^notice$', views.notice),
]

  值得一提的是,路由记录的不是一对一的页面对应,一个url对应的可能是一个功能函数,而非页面,具体会不会呈现页面,由views内对应函数的返回值决定。

views代表视图,能够渲染html的数据:

@csrf_exempt
def check_board(request):
    new_list=[]
    result='111'
    if request.is_ajax():
        if request.method == 'POST':
            info = json.loads(request.body.decode()).get('check_board')
            if info:
                sql = 'select board_id from fpga_info'
                old_list = select_pc_from_table(sql)
                save_data = info["value_dict"]
                print('checkkkkkkkkkkkkkkkk',save_data)
                for i in range(len(save_data)):
                    if (save_data[i] is None):
                        pass
                    else:
                        new_list.append(save_data[i]['board_id'])
                if new_list!=[]:
                    diff_a_b=set(old_list).difference(new_list)
                    diff_b_a=set(new_list).difference(old_list)
                    union_df=diff_a_b.union(diff_b_a)
                    result=json.dumps(list(union_df))
                    print(result)
    return HttpResponse(result, content_type="text/javascript")

  以此函数为例,其作用是通过ajax与前台js交互,其只负责递交result给js,具体如何处理,并不关心。

templates是模板,里面是html页面,与C++模板意义上类似,一个html的页面能够装纳views传输的数据。

static是用于装纳css格式及js文件的,css主要用于影响前台页面的展现效果,js主要用于前台的交互,设计良好能够减少前后台交互,增强体验。

---------------------------------------分割线------------------------------------------

model篇:

初步去实现了一下model的使用方式,在此稍作记录:

models增加:

NightResult.objects.create(**save_data[i])

models删除:

save_data={'id'=111,'name'='222'}
NightResult.object.update(**save_data)

models修改:

T=NightResult.objects.filter(id=save_data[i]['id'])
del save_data[i]['id']
T.update(**save_data[i])

models查找:

temp = NightResult.objects.filter(test_time=date)

其中NightResult为models.py实际的class,即表名(注意从数据库中导入到models文件后,表名是有区别的),filter类似于查找,test_time为数据表中的列名。

models字典式查询方式:

dic={'test_time':'20210125','id__contains':'168'}
T = NightResult.objects.filter(**dic)

其中,__contains代表id中包含168的项目,即模糊查询,以下将简单记录几个有代表性的。

models后缀表意:

__gt 大于> 
__gte 大于等于>=

仅以此实例,其余类似:

Student.objects.filter(age__lt=10) // 查询年龄小于10岁的学生
Student.objects.filter(age__lte=10) // 查询年龄小于等于10岁的学生

__lt 小于<
__lte 小于等于<=

__exact 精确等于 like 'aaa'
__iexact 精确等于 忽略大小写 ilike 'aaa'
__contains 包含 like '%aaa%'
__icontains 包含,忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains。

__in 查询包含在某一范围,请不要以为这是x<y<z的类型,其原理类似于x in [y,z]

__range 此后缀表示在某一范围,及 x<y<z

datas['test_time__range'] = [dic['date1'], dic['date2']]
T = NightResult.objects.filter(**datas)

__isnull 判空

__startswith 以…开头
__istartswith 以…开头 忽略大小写
__endswith 以…结尾
__iendswith 以…结尾,忽略大小写
__range 在…范围内

__year 日期字段的年份
__month 日期字段的月份
__day 日期字段的日

Book.objects.filter(create_time__year=2019, create_time__month=4).all()

models返回列名:

T = NightResult.objects.filter(result='pass')
filed = T[0]._meta.fields
params = [f for f in filed]
lis = []
for i in params:
    lis.append(i.name)

首先获取查询结果,选择第一项对其fields进行遍历,即可获取列名

models排序:

UserData.objects.filter(**dic).order_by('hour') #正序
UserData.objects.filter(**dic).order_by('-hour')#降序

models单表聚合:

页面进行检索时,可能需要数据库提供一定量的已有选项,所以出现了与SQL中group_by类似的用法:

T = NightResult.objects.values('name_id').distinct()

暂时没有应用多表的聚合问题,暂不记录。

下一步去了解ajax的局部刷新来应用于搜索功能。

对于model查询结果的格式整理:

values_list('pc_ctrl_ip')=[(,)] values_list('pc_ctrl_ip','status')=[('','')]

models的查询方式是ORM模型提供的,其具有一定的封装性,如果追求效率,用sql原生语句会更快一些,可是看网上的一些言论说,如果追求优化,那么一开始就不会选择django作为开发工具。汗= =。

后来我根据实际操作,以我们目前的sql查询语句(单纯的查询某月的测试结果为例),实际测试结果如下:

 嗯....说真的,我也不知道为什么,sql查询就这么一句难道还有优化的可能吗?我没有追溯这个问题产生的原因,基于原代码系统,既然当前models对此系统有益,那自然采纳。

这里发现了是为什么,这始于django的一中class,queryset,就是查询集,从数据库里获取到对象数据集合.但是有个特点就是它不是直接查询数据的,而是只是定义了一个查询集,在你调用的时候才查询数据库,并且查询同一个查询集后还会自己生成一个缓存,在你以后再次查询同样的查询集会使用缓存的数据,这样节省了查询数据库的资源,有点类似迭代器,因此上面产生这个差异的原因极可能是因为缓存的问题

后期打算写一篇关于queryset的文章用于记录。

----------------------------------------------------------------------------------------------------------

handlebars.js与pagination.js

之前探讨了models的使用方式,这次的更新迭代涉及到了前端的一些东西----局部刷新,因为之前使用的是后端映射的方式进行页面刷新,所以这次修改内容较为繁多,要查找的东西自然也多一些,其中还涉及到一些性能问题,可是其实我觉得项目并没有进行商业化而仅仅是对内便捷式使用,所以仍然使用的django,一些帖子说django的性能并不好,这个待证实。

更新方式主要涉及两个方面,第一使用了“模板语言” handlebars,第二原分页模式不再使用,于是探讨pagination的js可用的js包,奈何能力有限,可见范围内的pagination包全部有大大小小的问题,可是奈何我js能力确实不太强,源码实在懒得看,然后在脚儿网找到了博主自己写的pagination包。

原生思路:pagination直接通过ajax从后台调用数据调用handlebars模板使网页局部刷新

现实:可能是我能力问题,脚儿网的分页、翻页等功能一应俱全,就是没有数据调用。

故四处奔走寻找方式,奔走过程中,突然萌生一个问题,数据量大的情况下,数据到底交给前台处理还是后台处理?起初我只有一股脑丢给前台或者后台的思路,可是一个博主说,一般分页情况下,是前台需求一页的数据,然后通过ajax调用一页的数据。

好家伙!原来是我愚笨了,然后发现脚儿网的包其实并没有问题,只是需要我们自己去写调用数据的函数而已。

思路齐全后,开始一步步的走通路---handlebars作用于表格√、pagination封装下的handlebars作用于表格√、表格实现ajax式局部刷新√、与后端models查询数据产生联动并正确显示√

所有功能基本完善,还没来得及应用先来记录一波,怕自己以后遗漏什么。

handlebars

具体包的下载地址在这个超链接中,通过git即可下载,具体语法可以在该官网查询获得。

很多论坛说handlebars更快一些,因为它会预编译,可是具体快不快其实我并不清楚,我的主要目的还是实现功能,并且利于维护。

只在这里做简单的说明:

<script id="xx-template" type="text/x-handlebars-template">
        {% verbatim %}
{{#each info}}
    <tr class="tr_c_line" align='center' id="tr_c_id" style="background:#F5F5F5" cellspacing="0" cellpadding="0" border="1" data-sortlist="[[0,0],[2,0]]">
        <td class="td">
            <div class="board_div" name='c_edit' contenteditable="false">
                <select id="board_id{{fields.id}}" style="60px;text-align-last:center;" name="board_id">
                    <option selected> {{fields.board_id}} </option>
                    {{#each ../list.board_list}}
                    <option>{{this}}</option>
                    {{/each}}
                </select>
            </div>
        </td>
    </tr>
{{/each}}
    {% endverbatim %}
</script>

each可做遍历作用,其中{% verbatim %}{% endverbatim %}在django的联调模式中必须添加,不然会产生冲突而发生错误,vue不是太清楚。

script中的id适用于后面调用模板所做的准备,因为我们需要确认模板,具体数据如何调用,待我放出data之后,就可以了然,值得一提的是 ../的意思是访问父属性。

data:

var dataSource={
        info:[
      {
        fields:
        {           id:
'123', board_id:'s1', }
      }, {
        fields: {   id:
'124',   board_id:'s3', }
      } ] list:
    {
      board_list : [
'm1','m2','m3'] } }

可能存在错误,我临时修改的数据。结合以下代码食用更佳:

<tbody id="tbody">
            </tbody>
var tpl   =  $("#xx-template").html();
var template = Handlebars.compile(tpl);
var html = template(dataSource);
$("#tbody").html(html);

可以看出dataSource实际上在页面上并没有用到,我们直取的info和list,这是个坑,网上很多代码是直取的dataSource,是有问题的。另外pagination和handlebars都给了我很新奇的一种思路,就是数据存储位置和模板位置未必要一致。这也体现出html灵活的地方。

综上,主要3个坑点,1、注意django与handlebars的适用性。2、父属性的如何访问。3、dataSource如何访问的。

补充一个handlebars的华点:

一般我们拿来js包都是直接使用并没有注意它有什么,这里补充一个:自定义函数

<script>
Handlebars.registerHelper("inc", function(value, options) { return parseInt(value) + 1; });
</script>

它的功能:

<td >
     <div name="numid" contenteditable="false">{{inc @index}}</div>
</td>

背景:@index可以访问当前each序号,可是它是从0开始,我们需求是它需要+1.可是直接+1是错误操作,于是我们通过inc来进行一个自加操作。

pagination

安装包可根据超链接内的git下载。使用方式按照git给的使用方式即可。git内有example是非常好的实例。

附上我如何与handlebars联动的。

    <div id="aaa" align='center'></div>
<script>
item=123
i=111
var pagenav1 = xPagination(pagediv, {
    max:7,
    curr:1,
    size:15,
    items:item,
    jump:true,
    showSize:[15,30,i],//回传的数组需要去影响这个showSize与items
    onpagination: function  (page, size) {
        changepage(page,size)
    }
})
function changepage(page,size){
        $.ajax({
        url: '/test1' ,
        data: JSON.stringify({'111':'1'}),
        // data: body,
        contentType: "application/json;charset=utf-8",
        traditional: true,
        dataType: 'json',
        type: 'POST',
        async:true,
        success: function (data) {
            console.log(data)
            var dataSource={'info':[]}
            dataSource['list']=data.list
            dataSource.info=JSON.parse(data.info)
            console.log('kkkkkkkkkkkkk',dataSource)
            var tpl   =  $("#xx-template").html();
            var template = Handlebars.compile(tpl);
            var html = template(dataSource);
            $("#tbody").html(html);
         },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            alert(XMLHttpRequest.status);
            alert(XMLHttpRequest.readyState);
            alert(textStatus);
        },
    });
}
</script>

这段代码已经可以说明大部分问题,这里我还没有涉及到翻页,翻页页码和每页显示多少条的size问题可置于后台处理。

这次更新可能至此。目前并无什么新需求,后面遇到什么问题可能也是在原博文更新。

----------------------------------------------------------------------------------------

既昨天的思路我开始对目前的代码进行泛化处理,结果第一步就踩坑了,pagination的items我本身是写死的,可是在应用过程中使用的是后台传入的数据大小,是不能够产生作用的

那我们怎么去应用改变这个items呢?首先补充一个之前我不知道的东西。

JS执行顺序

首先我们这里的js为HTML文档嵌入式的应用,而HTML是按照文档流顺序执行的,因此JS作为HTML文档的一部分也是按照文档流顺序执行,而这个顺序是<script>决定,这个顺序不会因为外部JS文件而延期执行。为什么这么说呢,因为<script>内部会产生不一样的预编译顺序。以下实例皆出自此博文:https://www.jb51.net/article/44123.htm

1.预编译与执行顺序

<scripttype="text/javascript">
function Hello() { alert(
"Hello"); } Hello(); var Hello = function() { alert("Hello World"); } Hello();
</script>

运行之后会发现它输出了两次hello world。而并不是hello和hello world。

这是因为JS并非完全顺序解释执行,而是在解释之前对JS进行一次预编译,在预编译过程中,会把定义式的函数优先执行,也会把所有的var变量创建,默认值为undefined,以提高程序的执行效率。

由此,预编译后的代码:

<scripttype="text/javascript">        
varHello = function() {           
alert("Hello");        
}       
Hello = function() { 
           alert("Hello World");  
      }      
Hello();     
Hello();  
</script>

为什么会输出两次hello world可见一斑。

如何得到我们预想的输出呢?

<scripttype="text/javascript">      
functionHello() {        
alert("Hello");       
}        
Hello();  
</script>   
<scripttype="text/javascript">   
functionHello() {          
alert("Hello World");     
}       
Hello();  
</script>

通过<script>将程序分成两段,那样js就不会将其放到一起进行处理。

另外,变量赋值是在执行期而不是预编译期。

alert(a);                            // 返回值undefined
var a =1;
alert(a);                            // 返回值1

而函数定义和调用稍有不同。

f();                                 // 调用函数,返回值1
function f(){
    alert(1);
}
f();                                 // 调用函数,返回语法错误
var f = function(){
    alert(1);
}

其差异性在于,前者是定义,而后者是赋值,赋值只会产生在调用阶段。

<script type="text/javascript">
/*在预编译过程中func是window环境下的活动对象中的一个属性,值是一个函数,覆盖了undefined值*/
alert(func); //function func(){alert("hello!")}
var func = "this is a variable"
function func(){
alert("hello!")
}
/*在执行过程中遇到了var重新赋值为"this is a variable"*/
alert(func);  //this is a variable
</script>
<script type="text/javascript"> 
var name = "feng"; function func()
{ 
/*首先,在func环境内先把name赋值为undefined,然后在执行过程中先寻找func环境下的活动对象的name属性,此时之前已经预编译值为undefined,所以输出是undefined,而不是feng*/ 
alert(name);  //undefined var name = "JSF"; 
alert(name);  //JSF 
}
func(); 
alert(name); 
//feng
</script>

从上面也不难看出,JS在同一<script>下会产生定义和赋值上的问题,但是在不同的<script>中则不会产生这种问题,由此可见JS是按代码块执行

2.借助时间改变JS预编译执行顺序

<script>
// JavaScript代码块1
window.onload = function(){        // 页面初始化事件处理函数
    alert(a);
    f();
}
</script>
<script>
// JavaScript代码块2
var a =1;
function f(){
    alert(1);
}
</script>

一般页面初始化完毕之后才允许执行JS代码,避免网速对JS执行的影响,也避免了HTML文档流对JS执行的限制。

如果页面存在多个windows.onload事件处理函数,那么只有最后一个才有效,可以将所有脚本或调用函数都放在同一个onload时间中进行处理。

window.onload = function(){
    f1();
    f2();
    f3();
}

简化之后这些讲解还是有点多。不过还是值得的。到这问题其实已经解决了。

两个<script>第一个定义items第二个对pagination进行初始化,但是问题在于,需要查两次。

第一个代码块获取items需要后台查一次,第二次初始化需要查第二次。

第二次为什么产生呢?因为pagination的onpagination函数一加载页面会自动调用,即使是第一页,而且我们通过pagination调用handlebars避免不了查第二次。

那如果查的第一次就存下数据呢?

这违背了我们思路的初衷,首先,你不知道页面的size,也就是当前页面能存储多少条数据,那么你就需要把所有数据留在页面,但是,我们的思路是看一页取一页。

查过官方的pagination,也是事先规定了pagination的items和页数之类,限于水平目前还未找到优化的可能。

------------------------------------------------------------------------------------------------

就昨天的实践内容,打算实装的时候发现一个问题,我搜索,局部刷新,页面不刷新那么我第一次后台查询就不会调用,当然我们可以搜索的时候自动调用这一个。

可是当时我发现我当时实践过程中的一个华点。我修改了pagenav1的options中items的属性,修改成功了为什么没生效?其实生效了,不过它需要翻页刷新状态,这可能是这个JS包设计上的缺陷,不过对于我们目前的内部使用来说是可以使用的,后期如果能力允许可以进行手动修改。

另外,虽然翻页能够刷新页码数,可是条数其实是不变的。

 如图,两个300很突兀,2*15显然是30才对,这部分可以在onpagination里面对页面标签进行修改,问题不大。

-----------------------------------------------------------------------------------------------

JS的碎片整理:

文本框: innerHTML
select: val()
上下table列无法对齐的情况:设置table style: style="table-layout: fixed;",这样表格的内容将不会影响td的长度

原文地址:https://www.cnblogs.com/threeidiots/p/14139306.html