Session与Ajax

来源:互联网 发布:打蛋器价格淘宝 编辑:程序博客网 时间:2024/05/23 02:07

Django完全支持也匿名会话,简单说就是使用跨网页之间可以进行通讯,比如显示用户名,用户是否已经发表评论。session框架让你存储和获取访问者的数据信息,这些信息保存在服务器上(默认是数据库中),以 cookies 的方式发送和获取一个包含 session ID的值,并不是用cookies传递数据本身。

启用session

编辑settings.py中的一些配置

MIDDLEWARE_CLASSES 确保其中包含以下内容

1
'django.contrib.sessions.middleware.SessionMiddleware',

INSTALLED_APPS 是包含

1
'django.contrib.sessions',

这些是默认启用的。如果你不用的话,也可以关掉这个以节省一点服务器的开销。

提示:您也可以配置使用比如 cache 来存储 session

在视图中使用 session

request.session 可以在视图中任何地方使用,它类似于python中的字典

session 默认有效时间为两周,可以在 settings.py 中修改默认值:参见这里

1
2
3
4
5
6
# 创建或修改 session:
request.session[key] = value
# 获取 session:
request.session.get(key,default=None)
# 删除 session
del request.session[key] # 不存在时报错

session 例子

比如写一个不让用户评论两次的应用:

1
2
3
4
5
6
7
8
9
from django.http import HttpResponse
 
def post_comment(request, new_comment):
    if request.session.get('has_commented'False):
        return HttpResponse("You've already commented.")
    = comments.Comment(comment=new_comment)
    c.save()
    request.session['has_commented'= True
    return HttpResponse('Thanks for your comment!')


一个简化的登陆认证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def login(request):
    = Member.objects.get(username=request.POST['username'])
    if m.password == request.POST['password']:
        request.session['member_id'= m.id
        return HttpResponse("You're logged in.")
    else:
        return HttpResponse("Your username and password didn't match.")
         
         
def logout(request):
    try:
        del request.session['member_id']
    except KeyError:
        pass
    return HttpResponse("You're logged out.")


当登陆时验证用户名和密码,并保存用户id在 session 中,这样就可以在视图中用 request.session['member_id']来检查用户是否登陆,当退出的时候,删除掉它。

有时候我们想把一个 list 或 dict等 JSON对象 传到网页的 javascript,用 JS 进行处理,比如用 js 将数据可视化显示到网页上。

请注意:如果不需要处理,直接显示到网页上,用Django模板就可以了,请看前面的教程。

这里讲述两种方法

一,页面加载完成后,在页面上操作,在页面上通过 ajax 方法得到新的数据再向服务器发送一次请求并显示在网页上,这种情况适用于页面不刷新的情况下,动态加载一些内容。比如用户输入一个值或者点击某个地方,动态地把相应内容显示在网页上。

这种请问详见 Django Ajax 一节的内容。

二,直接在视图函数(views.py中的函数)中将 JSON对象 和网页其它内容一起传递到Django模板一次性地渲染,还是同一次请求

请看下面的示例:

views.py

1
2
3
4
5
6
7
from __future__ import unicode_literals
from django.shortcuts import render
 
 
def home(request):
    List = ['自强学堂''渲染Json到模板']
    return render(request, 'home.html', {'List'List})

home.html 中的一部分

1
2
3
4
<script type="text/javascript">
    var List = {{ List }};
    alert(List);
</script>

需要注意的是,我们如果直接这么做,传递到 js 的时候,网页的内容会被转义,得到的格式会报错

访问时会得到 Uncaught SyntaxError: Unexpected token ILLEGAL


需要注意两点:

1. 视图函数中的字典或列表要用 json.dumps()处理。

2. 在模板上要加 safe 过滤器

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding: utf-8 -*-
 
from __future__ import unicode_literals
 
import json
from django.shortcuts import render
 
def home(request):
    List = ['自强学堂''渲染Json到模板']
    Dict = {'site''自强学堂''author''涂伟忠'}
    return render(request, 'home.html', {
            'List': json.dumps(List),
            'Dict': json.dumps(Dict)
        })

home.html 只给出了 js 核心部分:

1
2
3
4
//列表
var List = {{ List|safe }};
//字典
var Dict = {{ Dict|safe }};

如果你对 js 比较熟悉,到此为止,后面的不用看了。

如果不太熟悉,可以参考下面的更详细的代码。


html 完全代码及完整代码下载(最后面):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html>
<head>
<title>欢迎光临 自强学堂!</title>
<script src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<div id="list"> 学习 </div>
<div id='dict'></div>
<script type="text/javascript">
    //列表
    var List = {{ List|safe }};
 
    //下面的代码把List的每一部分放到头部和尾部
    $('#list').prepend(List[0]);
    $('#list').append(List[1]);
 
    console.log('--- 遍历 List 方法 1 ---')
    for(i in List){
        console.log(i);// i为索引
    }
 
    console.log('--- 遍历 List 方法 2 ---')
    for (var i = List.length - 1; i >= 0; i--) {
        // 鼠标右键,审核元素,选择 console 可以看到输入的值。
        console.log(List[i]);
    };
 
    console.log('--- 同时遍历索引和内容,使用 jQuery.each() 方法 ---')
    $.each(List, function(index, item){
        console.log(index);
        console.log(item);
    });
 
 
    // 字典
    var Dict = {{ Dict|safe }};
    console.log("--- 两种字典的取值方式  ---")
    console.log(Dict['site']);
    console.log(Dict.author);
     
    console.log("---  遍历字典  ---");
    for(i in Dict) {
        console.log(i + Dict[i]);//注意,此处 i 为键值
    }
</script>
</body>
</html>

完整代码下载:zqxt_json.zip (基于Django 1.8,注意settings.py文件和低版本可能不兼容,其它部分相同)



有时候我们需要在不刷新的情况下载入一些内容,在网页的基本知识中我们介绍了 ajax 技术。

在本文中讲解如何用 Django 来实现 不刷新网页的情况下加载一些内容。

由于用 jQuery 实现 ajax 比较简单,所以我们用 jQuery库来实现,想用原生的 javascript 的同学可以参考:ajax 教程,下面也有例子提供下载。

本节有多个实例提供下载,通过看代码可以更快的学习。

第一节,源代码下载:zqxt_ajax_1.zip

这里用 Django 表单 第一节 中的一个例子,我们要实现的是在不刷新的情况下显示计算结果到页面上。

修改 index.html 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html>
<body>
<p>请输入两个数字</p>
<form action="/add/" method="get">
    a: <input type="text" id="a" name="a"> <br>
    b: <input type="text" id="b" name="b"> <br>
    <p>result: <span id='result'></span></p>
    <button type="button" id='sum'>提交</button>
</form>
 
<script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
    $(document).ready(function(){
      $("#sum").click(function(){
        var a = $("#a").val();
        var b = $("#b").val();
 
        $.get("/add/",{'a':a,'b':b}, function(ret){
            $('#result').html(ret)
        })
      });
    });
</script>
</body>
</html>

在原来的基础上,在一些元素上加了 id, 以便于获取值和绑定数据,然后我们用了jQuery.get() 方法,并用 $(selector).html() 方法将结果显示在页面上,如下图:

备注:关于请求头和 request.is_ajax() 方法使用

views.py 中可以用  request.is_ajax() 方法判断是否是 ajax 请求,需要添加一个 HTTP 请求头:

原生javascript:

1
xmlhttp.setRequestHeader("X-Requested-With""XMLHttpRequest");

用 jQuery:

1
用 $.ajax 方法代替 $.get,因为 $.get 在 IE 中不会发送 ajax header

服务器端会将请求头的值全部大写,中划线改成下划线,并在非标准的头前面加上 HTTP_,这个过程可以认为相当于以下Python代码:

1
2
3
4
5
6
7
8
9
STANDARD_HEADERS = ['REFER''HOST', ...] # just for example
 
def handle_header(value):
    value = value.replace('-''_').upper()
 
    if value in STANDARD_HEADERS:
        return value
 
    return 'HTTP_' + value

判断ajax方法,以及原生的 javascript实现ajax的示例下载:zqxt_views_ajax.zip


第二节,源代码下载:zqxt_ajax_list_dict.zip

更复杂的例子,传递一个数组或字典到网页,由JS处理,再显示出来。

views.py

1
2
3
4
5
6
7
8
9
10
from django.http import HttpResponse
import json
 
def ajax_list(request):
    = range(100)
    return HttpResponse(json.dumps(a), content_type='application/json')
 
def ajax_dict(request):
    name_dict = {'twz''Love python and Django''zqxt''I am teaching Django'}
    return HttpResponse(json.dumps(name_dict), content_type='application/json')

Django 1.7 及以后的版本有更简单的方法(使用 JsonResponse(官方文档)):

1
2
3
4
5
6
7
8
9
from django.http import JsonResponse
 
def ajax_list(request):
    = range(100)
    return JsonResponse(a, safe=False)
 
def ajax_dict(request):
    name_dict = {'twz''Love python and Django''zqxt''I am teaching Django'}
    return JsonResponse(name_dict)

在 django 1.6 及以前的旧版本中可以自己写一个 JsonResponse 方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from django.http import HttpResponse
 
import json
 
class JsonResponse(HttpResponse):
    def __init__(self,
            content={},
            mimetype=None,
            status=None,
            content_type='application/json'):
 
        super(JsonResponse, self).__init__(
            json.dumps(content),
            mimetype=mimetype,
            status=status,
            content_type=content_type)

写好后,我们在 urls.py 中添加以下两行:

1
2
    url(r'^ajax_list/$''tools.views.ajax_list', name='ajax-list'),
    url(r'^ajax_dict/$''tools.views.ajax_dict', name='ajax-dict'),

打开开发服务器 python manage.py runserver

我们访问对应的网址会看到输出值:

下一步就是在无刷新的情况下把内容加载到网页了,我们修改一下首页的模板 index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE html>
<html>
<body>
<p>请输入两个数字</p>
<form action="/add/" method="get">
    a: <input type="text" id="a" name="a"> <br>
    b: <input type="text" id="b" name="b"> <br>
    <p>result: <span id='result'></span></p>
    <button type="button" id='sum'>提交</button>
</form>
 
 
<div id="dict">Ajax 加载字典</div>
<p id="dict_result"></p>
 
<div id="list">Ajax 加载列表</div>
<p id="list_result"></p>
 
 
<script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
    $(document).ready(function(){
      // 求和 a + b
      $("#sum").click(function(){
        var a = $("#a").val();
        var b = $("#b").val();
 
        $.get("/add/",{'a':a,'b':b}, function(ret){
            $('#result').html(ret);
        })
      });
 
      // 列表 list
      $('#list').click(function(){
          $.getJSON('/ajax_list/',function(ret){
            //返回值 ret 在这里是一个列表
            for (var i = ret.length - 1; i >= 0; i--) {
              // 把 ret 的每一项显示在网页上
              $('#list_result').append(' ' + ret[i])
            };
          })
      })
 
      // 字典 dict
      $('#dict').click(function(){
          $.getJSON('/ajax_dict/',function(ret){
              //返回值 ret 在这里是一个字典
              $('#dict_result').append(ret.twz + '<br>');
              // 也可以用 ret['twz']
          })
      })
    });
</script>
</body>
</html>

技能提升:getJSON中的写的对应网址,用 urls.py 中的 name 来获取是一个更好的方法!

标签:{% url 'name' %}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script>
    $(document).ready(function(){
      // 求和 a + b
      $("#sum").click(function(){
        var a = $("#a").val();
        var b = $("#b").val();
 
        $.get("{% url 'add' %}",{'a':a,'b':b}, function(ret){
            $('#result').html(ret);
        })
      });
 
      // 列表 list
      $('#list').click(function(){
          $.getJSON("{% url 'ajax-list' %}",function(ret){
            //返回值 ret 在这里是一个列表
            for (var i = ret.length - 1; i >= 0; i--) {
              // 把 ret 的每一项显示在网页上
              $('#list_result').append(' ' + ret[i])
            };
          })
      })
 
      // 字典 dict
      $('#dict').click(function(){
          $.getJSON("{% url 'ajax-dict' %}",function(ret){
              //返回值 ret 在这里是一个字典
              $('#dict_result').append(ret.twz + '<br>');
              // 也可以用 ret['twz']
          })
      })
    });
</script>

这样做最大的好处就是在修改 urls.py 中的网址后,不用改模板中对应的网址。


补充:如果是一个复杂的 列表 或 字典,因为比如如下信息:

1
2
3
4
5
person_info_dict = [
    {"name":"xiaoming""age":20},
    {"name":"tuweizhong""age":24},
    {"name":"xiaoli""age":33},
]

这样我们遍历列表的时候,每次遍历得到一个字典,再用字典的方法去处理,当然有更简单的遍历方法:

用 $.each() 方法代替 for 循环,html 代码(jQuery)

1
2
3
4
5
$.getJSON('ajax-url-to-json'function(ret) {
    $.each(ret, function(i,item){
        // i 为索引,item为遍历值
    });
});

补充:如果 ret 是一个字典,$.each 的参数有所不同,详见:http://api.jquery.com/jquery.each/

1
2
3
4
5
$.getJSON('ajax-get-a-dict'function(ret) {
    $.each(ret, function(key, value){
        // key 为字典的 key,value 为对应的值
    });
});


最后,附上一个返回图片并显示的ajax实例

django_ajax_pic.png

代码下载:zqxt_ajax_pic.zip




CSRF(Cross-site request forgery跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

Django 中自带了 防止CSRF攻击的功能,但是一些新手不知道如何使用,给自己编程带来了麻烦。常常会出现下面django csrf token missing or incorrect的错误。

GET 请求不需要 CSRF 认证,POST 请求需要正确认证才能得到正确的返回结果。一般在POST表单中加入 {% csrf_token %}

1
2
3
4
5
<form method="POST" action="/post-url/">
    {% csrf_token %}
     
    <input name='zqxt' value="自强学堂学习Django技术">
</form>


如果使用Ajax调用的时候,就要麻烦一些。需要注意以下几点:

  1. 在视图中使用 render (而不要使用 render_to_response)

  2. 使用 jQuery 的 ajax 或者 post 之前 加入这个 js 代码:http://www.ziqiangxuetang.com/media/django/csrf.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
jQuery(document).ajaxSend(function(event, xhr, settings) {
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    function sameOrigin(url) {
        // url could be relative or scheme relative or absolute
        var host = document.location.host; // host + port
        var protocol = document.location.protocol;
        var sr_origin = '//' + host;
        var origin = protocol + sr_origin;
        // Allow absolute or scheme relative URLs to same origin
        return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
            (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
            // or any other URL that isn't scheme relative or absolute i.e relative.
            !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
 
    if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
});

或者 更为优雅简洁的代码(不能写在 .js 中,要直接写在模板文件中):

1
2
3
$.ajaxSetup({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});

这样之后,就可以像原来一样的使用 jQuery.ajax() 和 jQuery.post()了

最后,附上一个 Django Ajax CSRF 实例:exam.zip



原创粉丝点击