赞
踩
在着手开发一个SPA项目时,由于才疏学浅,被一个问题蹂躏了将近三天:如何把Django后台Views的值传递给前端的jQuery(JS),再由前端处理后显示与HTML中或者仅仅作为参数去使用,同时这个过程要是Ajax方式的,也就是不刷新页面。
官方文档,网上的教程(如“自强学堂”),技术博客,搜一下,满都是。这个问题似乎很好解决。但是,如果知识功底不扎实,也没有人指导,还是会折腾好久的。我要把解决过程和我掌握基本知识写下来,以免以后的朋友再踩这个坑。
基本环境:
python:3.6.3
Django:1.11.6
这个问题的解决有以下两种入手方式:
1.采用Django自带的Template模板系统
在学习Django的时候,大家都至少接触过这个系统,它是Django内置的。就如同静态文件加载(static)一样,采用了这个系统的模板,就必须在Django的server下打开网页,否则毫无疑问会报错。
在这里只做基本回顾:
结构:
{{ }} 双大括号包括起来的是变量
{% %} 模式是块,可以用if和for之类的语句进行遍历
| 括号中的值末尾加“|”,可以使用筛选器,筛选结果
用法:
①在.py文件中 from django.template import Template
便可以在文件的任意字符串中使用模板
②直接在html文件的几乎任意位置使用,在页面被context(一会说)渲染的时候,便会被所渲染的值替代。
思路:把你想要的值(无论是数组、整形、字符串、字典还是别的什么混合结构),用 {{ 名称}} 的格式返回给JS代码中的某个变量,然后就可以自由自在的运用它了。
示例:
1.使用ajax方法向url "mysite/foobar/"发送请求
2.当访问url "mysite/foobar/" 的时候,调用Views里的函数 foobar,采用某个函数,返回我们要的字典 data={'foo':'bar'} 到前端
3.在前端的html页面中的<script>标签中,使用var name = {{ data|safe }}完成接收
urls.py
- import views
- urlpatterns = [
- url(r'^mysite/foobar', views.foobar)
- ]
到了views中,我们需要选择一个用于返回的函数,Django中提供了以下选择:
1.django.http 中的常规httpresponse对象
代表性的有:HttpResponse 和JsonResponse,顾名思义,后者返回Json格式的字符串而前者返回任意字符串。二者都有配套的多种方法和参数可用,不再赘述。
2.django.shortcuts 中的封装好的快捷函数
不用代表,似乎只有render 和 render_to_response两种,后者与前者的最大区别(非唯一区别),是不需要request参数。在根本上render_to_response内部是render在运作,而render的内部,最终又是HttpResponse。
由于我们要使用django的模板,就必须有渲染函数,所以,只能选择render_to_response 或者render。
由于功能的完善和严谨性,官方推荐大家使用render,且render的简化版,那个长的,将可能在未来移除
- from django.shortcuts import render
-
- def foobar(request):
- return render(request,'foobar.html',{'foo':'bar'})
最后一步便是接收了,这也是最困扰我导致折腾很久无果的一个步骤。可以看到,在之前的实例中,我使用了 |safe 过滤器,如果不用这个过滤器,则接收到的数据很可能是乱码,没有正确的编码。但是在发送时,还有更多的问题,如:是否使用json.dumps()转换想要发送的字典的json字符串(就像无数个例子和博客中所写的那样),答案是:不一定。
事实上,render的第三个参数,也叫作context,它的接收类型本身就是一个字典,所以转换成json显得有点多余。虽然这可能带来了一些潜在的好处,但是在前端,势必还需要去转换,实属得不偿失(才疏学浅,希望大神指正。)
更加常见的情况是,很多人在海量的博客和教程中晕头转向,代码报错不断,而且还会遇到最严重的问题:html的宽容加上前后端交互时错误信息的难以查看。
所以我推荐的做法是:字典就直接用字典的形式发送,不是字典的内容放在字典中用{‘名称’:‘内容’}的形式发送,报错,则改为{‘名称’:json.dumps(‘内容’)},再在前端使用对应的函数去转换即可。
foobar.html
- ...........
- <script>
- var name = { foo|safe }
- </script>
-
- <body>
- .............
这样就“行了”。
然后运行的时候,name 就等价于 undefined了。
对,这里涉及到了一个核心问题:django的模板渲染方式
我当时就掉进了这个坑。教大家一个小技巧(大神请无视),调试前端时不要只看控制台,可以尝试打开一些浏览器的调试台,里面会有源代码的实时情况。对于调试Ajax来说,这简直是必须的。打开后你就会发现,运行之前,源代码长这样:
- ...........
- <script>
- var name =
- </script>
-
- <body>
- .............
对,你没看错,那里是空白。这是因为浏览器不是django家开的,所以{{ }}或者其他模板输入的这种模式会被视为非法模式,而在解析时直接忽略。此时读取foo,只能是“undefined”,其实没报错都算不错了。而当你运行了对应的请求之后,源代码会变成这样:
- ...........
- <script>
- var name = bar
- </script>
-
- <body>
- .............
看上去像是好了。
但当你运行的时候,什么都不会发生。就像这句代码不存在那样。
归根结底,其实也很好理解,因为bar此时没有引号括起来,又处在<script>标签中,所以会被认为是一个变量名,而当你没有这个变量名的时候,自然而然会触发一个错误(有的话则会被正常读取,不过……那样可能更尴尬吧?),这一次的错误可比{{ }}触发的格式错误要严重的多,它试图访问不存在的变量,这个错误会导致整段script甚至之后的所有js代码全部被忽略。
但是要解决也很容易:你可以费劲心思给它强行加上一对引号,也可以选择另辟蹊径从此走上easy模式。由于模板系统会用输出后的值去替换模板而不是python源代码,所以把要的值放回到字典中将会十分牢靠。(还记得python中字典可以直接print的吧?)
于是我们同时修改 views和html:
views.py
- from django.shortcuts import render
-
- def foobar(request):
- mydata = {'foo':'bar'}
- return render(request,'foobar.html',{'data':mydata})
foobar.html
- ...........
- <script>
- var name = {{ data|safe }}
- var myname = name.foo
- </script>
-
- <body>
- .............
如此如此,你就取到了你想要的值了。当然,把取值内容写在模板中(模板渲染),或者用$.each()或者for遍历取值等等各种花式秀,那就都是你的事了,我不多说。
2.拒绝使用templates,用纯字符串实现
对于那些专注于前端的工程师来说,用惯了Hbuilder等一些边看边开发的IDE,要把django模板加入到html文件中,估计都会一百个不愿意:这样的话,边看边开发功能就没有之前那么好用了。再者说,能够进行模板渲染的框架很多,如VUE,还有前端的AngularJS,甚至于常用的jQuery本身,都有或强或弱的渲染能力。硬生生加入django的模板,其实有一点此消彼长的意思,也有些后端插手前端的嫌疑。
其实,不用django模板的话,这个功能会更容易实现,也不会遇到什么问题,但是会有一些后顾之忧(一会说)。
实现变得异常简单:
urls.py完全不变
views.py
- from django.http import JsonResponse
-
- mydata = {
- 'foo':'bar'
- }
-
- return JsonResponse(mydata,safe=False)
之后是.html
- ...........
- http请求
- 任意类型
- success:function(data){
- $.parse(data)
- }
- .........
可能有些朋友有些看不太懂这种草书哈哈,其实意思就是都无所谓,无论你用的是get还是post,还是精细化的$.ajax,唯一要做的就是从回调函数中获得json字符串,再用自己的方法去把它转回去,然后读取使用。
当然,使用getJson也是可以的。总之,很便捷。
原因:不需要模板渲染使得这个过程就是简单的数据回执然后接收使用,所以很简单。
那要不要使用Django的“辣鸡”模板?
看了上面心得的朋友不难发现,模板在我的论述中显得很多余。但是事实并非如此。这一整个结构(前端后端),是一个此消彼长的结构。理论上讲,完全使用django能开发出前后两端,完全手撕JS加上一些基本组件,也是可以开发前后两端的。但是,几乎没几个人会选择这么干吧?原因很简单,就是:术业有专攻。
在模板渲染和前后端传值这里,其实是前后端框架的交界地带。交界处,技术重叠的就更多,选择也就更多。所以就有了条条大路通罗马的情况出现。这里我简单罗列一下两种选择的优势和缺点,诸君可以根据需要、兴趣以及能力去选择:
选择使用django模板:
优势:
1.速度优势
由于django是服务器端,在发送页面时,可以同时对源代码进行修改。怎么算也比发送到前端后,渲染,然后再运行解析代码,再渲染。来的快得多吧?
2.方便接入后端功能
同样由于django是后端,所以诸如跨站请求伪造、静态文件配置、甚至url绑定等功能,都有对应的模板实现方法。仅仅因为传值而放弃使用这些功能的简易方法,肯定是有所损失的。
劣势:
1.配置的复杂性
就单纯对比两种实现方法,都能看出来render要麻烦一些。
2.较低的灵活性
尤其是选用render_to_response的时候,这个问题尤为突出。回执过程无法做到精细化控制,而且当你想使用request参数时(比如session),你就必须换个函数(听上去很搞笑是吧),而当你不需要使用request的时候(其实都会在参数中),又会多那么一点点代码。对于技术达人来说,高度的封装无疑像是被捆住了手脚。
3.冲突与错误
无论是{} 还是% ,在不同的语言中,往往都有含义。如果你在前端使用了额外的框架,或者更离谱一点:javascript针对{{ }}这种符号发布了某种更新,冲突随时可能发生。前端重叠使用多个框架尚且会发生冲突,更别说来自后端的django,它在设计的时候估计也没怎么去考虑来自前端同行的兼容问题。同理,前端框架的开发者估计也没心思在兼顾几大巨头的情况下还去想想如何避免django冲突吧?
比冲突更难避免的是错误的使用,就如刚才的例子,万一真的有个变量名叫bar呢?
而且它还非常不适合被输出在这个位置。那么可能你一次看似毫无BUG的顺利更新就会导致一个十分可笑或者不安全的错误。这在网站以及互联网产品的运营中可算是严重失误了吧。模板的不可控性,就会带来这些隐忧。
4.IDE依赖者的噩梦
就如之前所说,加入html的django模板几乎会彻底摧毁“边看边开发模式”(或者你有好的解决方案请联系我),如果你是个开发新手,不断的打开浏览器,刷新,甚至简单的配置token,static,都会要了亲命。“瞎”折腾几次,相信你会积攒一些坏情绪的。
选择放弃django模板:
优势:
1.高的灵活性
这就是简单的json字符串传递,其中的流程以及解析方式,完全由你掌控,数据类型和加密方式,也完全能够自定义化。对于技术达人来说,可以用HttpResponse去打造属于自己的回执函数,减掉不需要的,留下必要的,甚至增加需要的,达到完美。
2.简易配置
就如理由这般简易。
3.低依赖性
比如项目的设计者(可能是你,但万一不是呢?)考虑换个后台,换个路由处理程序等等,身为前端的你,是什么都不用做还是再去把模板推倒了换成原生。那一刻后悔或者欣喜,就都只是成果了。
4.大量的个性化替代方案
前端框架可以说是无穷无尽,能做模板的比比皆是,万一实现模板的同时,别的小而美框架还能实现更牛的功能,那,何乐而不为呢?让那些用模板的嫉妒去吧。
劣势:
就一条:难啊。
灵活运用低封装的工具(什么?你说你用socket....!?),需要一定的技术水平,用不好势必是要翻车的。所以技术不过关的话,还是先用现成的完成任务比较好。性能的差距或许并没有你想象的那么多,或许有一天,你需要扩展了,大神熬夜去改代码,你却发现,你以前用的这个封装好的,早就有这个功能了。哈哈,笑而不语。
总结:自由选择。努力学习提高技术才是硬道理。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。