Django之Form、跨站请求以及cookie、session

Form表单

常规html页面的form表单验证

常规页面中,如果想实现对表单中用户输入信息的数据验证,需要配合Ajax来实现.

使用前我们先来熟悉下函数参数:request,其中包含的意义:

request.path               除去了域名和端口的访问路径,
request.get_host       域名+端口信息
request.get_full_path()   所有路径,包含传递的参数
requets.is_secure()     是否使用https进行链接

Django中的form库:每一个邦定Form实体都有一个errors属性,它为你提供了一个字段与错误消息相映射的字典表。可以这么定义:

>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f.errors
{'message': [u'This field is required.']}

实现验证的必要条件:

  1. 在views.py中定义一个继承了父类forms.Form的子类
  2. 在页面中需要设置name属性
  3. 后台验证时使用LoginForm(request.POST)获取表单确认结果
  4. 获取结果后,使用LoginForm(request.POST).is_valid()判断是否有误(True为无错误,False为有错误)
  5. 用户页面提交时,需要将输入内容拼接为字典形式,并且属性name的值要与views.py 中定义的一致

下面我们看下代码吧:

views.py代码:

class LoginForm(forms.Form):
    user = forms.CharField(required=True, error_messages={'required': '用户名不能为空.'})
    pwd = forms.CharField(required=True,
                          min_length=6,
                          max_length=10,
                          error_messages={'required': '密码不能为空.', 'min_length': "至少6位"})

    num = forms.IntegerField(error_messages={'required': '数字不能空.', 'invalid': '必须输入数字'})

    phone = forms.CharField(validators=[mobile_validate, ], )
    
    
    
    
def login1(request):
    if request.method == 'POST':
        result = {'status': False, 'message': None}
        obj = LoginForm(request.POST)
        ret = obj.is_valid()
        if ret:
            print(obj.clean())
            result['status'] = True
        else:
            from django.forms.utils import ErrorDict
            print(type(obj.errors), obj.errors.as_json())
            # print(obj.errors)
            error_str = obj.errors.as_json()
            result['message'] = json.loads(error_str)
        return HttpResponse(json.dumps(result))
    return render(request, 'login1.html')    

login1.html页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .error_msg{
            color: red;
        }
    </style>
</head>
<body>
    <div>
        <div>
            <input type="text" name="user" />
        </div>
        <div>
            <input type="password" name="pwd" />
        </div>
        <input type="button" value="提交" onclick="DoSubmit();" />
    </div>
    <script src="/static/jquery-1.12.4.js"></script>
    <script>


        function DoSubmit(){
            $('.error_msg').remove();
            var input_dict = {};
            $('input').each(function(){
                var v = $(this).val();
                var n = $(this).attr('name');
                input_dict[n] = v;
            });
            console.log(input_dict);
            $.ajax({
                url: '/login1/',
                type: 'POST',
                data: input_dict,
                dataType:'json',
                success: function (result) {
                    if(result.status){
                        location.href = '/index/'
                    }else {
                        $.each(result.message,function (k,v) {
                            console.log(k,v[0].message);
                            var tag = document.createElement('span');
                            tag.className = 'error_msg';
                            tag.innerText = v[0].message;
                            $('input[name="'+k +'"]').after(tag);
                            
                        })

                    }


                }
            })
        }
    </script>
</body>
</html>

怎么样,比较复杂吧,为什么复杂,我们要自己创建便签,自己生成HTML页面,很麻烦,那么我们就来看下django自带的form库!

Django自带form进行验证

权威django讲解form的地址:http://www.kancloud.cn/thinkphp/django-book/39587

Django中的form一般自带两个功能

  • 自动生产HTML
  • 验证用户输入

详细参数地址

views.py设置:

def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手机号码格式错误')


class LoginForm(forms.Form):
    user = forms.CharField(required=True, error_messages={'required': '用户名不能为空.'})
    pwd = forms.CharField(required=True,
                          min_length=6,
                          max_length=10,
                          error_messages={'required': '密码不能为空.', 'min_length': "至少6位"})

    num = forms.IntegerField(error_messages={'required': '数字不能空.', 'invalid': '必须输入数字'})

    phone = forms.CharField(validators=[mobile_validate, ], )
    topic = forms.ChoiceField(choices=TOPIC_CHOICES,label='选择评分')
    
###页面处理

def login(request):
    if request.POST:
        objPost = LoginForm(request.POST)
        ret = objPost.is_valid()
        if ret:
            print(objPost.clean())
        else:
            from django.forms.utils import ErrorDict
            # print(type(obj.errors),obj.errors.as_json())
            # obj1.errors
            pass
        return render(request, 'login.html', {'obj1': objPost})
    else:
        objGet = LoginForm()
        return render(request, 'login.html', {'obj1': objGet})    
    

其中error_messages定义了出错时显示在页面的错误消息

此处的topic中的choices需要在models.py中定义一个数组.

TOPIC_CHOICES = (
        ('leve1', '差评'),
        ('leve2', '中评'),
        ('leve3', '好评'),
)

html的页面中就很简单了,我们来看下:

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .error_msg{
            color: red;
        }
    </style>
</head>
<body>
    <form action="/login/" method="POST">
        <div>
            {{ obj1.user }}
            {%  if obj1.errors.user %}
                <span class="error_msg">{{ obj1.errors.user.0 }}</span>
            {% endif %}
        </div>
        <div>
            {{ obj1.pwd }}
            {%  if obj1.errors.pwd %}
                <span class="error_msg">{{ obj1.errors.pwd.0 }}</span>
            {% endif %}
        </div>
        <div>
            {{ obj1.num }}
            {%  if obj1.errors.num %}
                <span class="error_msg">{{ obj1.errors.num.0 }}</span>
            {% endif %}
        </div>
        <div>
            {{ obj1.phone }}
            {%  if obj1.errors.phone %}
                <span class="error_msg">{{ obj1.errors.phone.0 }}</span>
            {% endif %}
        </div>
        <input type="submit" value="提交"/>

    </form>

</body>
</html>

代码很少就能完美解决,真方便...

自定义form验证机制

关于自定义Form验证机制,可以看一下这个:http://www.cnblogs.com/ccorz/p/5868380.html

跨站请求csrf

概述

django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。

  1. 全局:文件位置setting.py,文件中间件位置:django.middleware.csrf.CsrfViewMiddleware
  2. 局部:
    • @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。

    • @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

    • 注意:以上需要导入模块:from django.views.decorators.csrf import csrf_exempt,csrf_protect

应用

使用时,views.py中无需设置任何东西,常规return render(xxx)即可,主要是在HTML页面中.

普通表单

普通form中添加在form中设置{% csrf_token %}即可.如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>csrf</title>
</head>
<body>
    <form action="/csrf/" method="POST">
        {% csrf_token %}
        <input type="text" name="v"/>
        <input type="submit" value="提交"/>
    </form>

</body>
</html>

Ajax

对于传统的form,可以通过表单的方式将token再次发送到服务端,而对于ajax的话,使用如下方式。

需要在script部分写入如下内容:

var csrftoken = $.cookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

我们可以将以上代码写入一个js文件,然后在使用时导入script.

html如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>csrf</title>
</head>
<body>
    <input type="button" value="ajax提交" onclick="doajax();" />

    <script src="/static/jquery-1.12.4.js"></script>
    <script src="/static/jquery.cookie.js"></script>
    <script src="/static/csrf.js"></script>
    <script>
{#        var csrftoken = $.cookie('csrftoken');#}
{##}
{#        function csrfSafeMethod(method) {#}
{#            // these HTTP methods do not require CSRF protection#}
{#            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));#}
{#        }#}
{#        $.ajaxSetup({#}
{#            beforeSend: function(xhr, settings) {#}
{#                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {#}
{#                    xhr.setRequestHeader("X-CSRFToken", csrftoken);#}
{#                }#}
{#            }#}
{#        });#}

        function doajax() {
            $.ajax({
                url:'/csrf/',
                type:'POST',
                data:{'k1':'v1'},
                success:function (data) {
                    console.log(data)
                    
                }
            })


            
        }


    </script>
</body>
</html>

cookie

获取cookie

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    参数:
        default: 默认值
           salt: 加密盐
        max_age: 后台控制过期时间

设置Cookie

rep = HttpResponse(...) 或 rep = render(request, ...)
 
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
    参数:
        key,              键
        value='',         值
        max_age=None,     超时时间
        expires=None,     超时时间(IE requires expires, so set it if hasn't been already.)
        path='/',         Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
        domain=None,      Cookie生效的域名
        secure=False,     https传输
        httponly=False    只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

前端操作cookie

由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。

<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });

session

session一般是与cookie配合一起使用的,否则没有cookie的session没有什么实际意义.

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie

数据库session

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
 
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
     
    SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
    SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
    SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
    SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
    SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
    SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
    SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
 
 
 
b. 使用
 
    def index(request):
        # 获取、设置、删除Session中数据
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('k1',123) # 存在则不设置
        del request.session['k1']
 
        # 所有 键、值、键值对
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()
 
 
        # 用户session的随机字符串
        request.session.session_key
 
        # 将所有Session失效日期小于当前日期的数据删除
        request.session.clear_expired()
 
        # 检查 用户session的随机字符串 在数据库中是否
        request.session.exists("session_key")
 
        # 删除当前用户的所有Session数据
        request.session.delete("session_key")
 
        ...

缓存session

配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
 
 
    SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
    SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
    SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
    SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
    SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次请求都保存Session,默认修改之后才保存

文件session

配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
 
 
    SESSION_COOKIE_NAME = "sessionid"                          # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    SESSION_COOKIE_PATH = "/"                                  # Session的cookie保存的路径
    SESSION_COOKIE_DOMAIN = None                                # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                               # 是否Https传输cookie
    SESSION_COOKIE_HTTPONLY = True                              # 是否Session的cookie只支持http传输
    SESSION_COOKIE_AGE = 1209600                                # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                     # 是否关闭浏览器使得Session过期
    SESSION_SAVE_EVERY_REQUEST = False                          # 是否每次请求都保存Session,默认修改之后才保存

缓存+数据库session

数据库用于做持久化,缓存用于提高效率
 
配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

加密cookie session

配置 settings.py
     
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

使用方式都一样.

扩展:session用户验证

def session_login(request):
    if request.method == 'POST':
        u = request.POST.get('user', None)
        p = request.POST.get('pwd', None)
        if p == '123' and u in USER_LIST:
            request.session['user'] = u
            return redirect('/session_index/')
    return render(request, 'session_login.html')

session 实例:用户登录退出

分析需求,一共三个页面,登录、登录后提示成功页面、以及注销页面.

views.py设置:

USER_LIST = ['alex', 'hu', 'eric', 'cc']

#设置装饰器
def check_login(func):
    def inner(request, *args, **kwargs):
        user = request.session.get('user')
        if not user:
            return redirect('/session_login')
        return func(request, *args, **kwargs)

    return inner


#设置登录页面后台处理
def session_login(request):
    if request.method == 'POST':
        u = request.POST.get('user', None)
        p = request.POST.get('pwd', None)
        if p == '123' and u in USER_LIST:
            request.session['user'] = u
            return redirect('/session_index/')
    return render(request, 'session_login.html')


#登录后的提示页面
@check_login
def session_index(request):
    user = request.session.get('user', None)
    return render(request, 'session_index.html', {'user': user})

#注销后台处理
@check_login
def session_logout(request):
    del request.session['user']
    return redirect('/session_login/')

登录页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/session_login/"  method="POST">
        <input type="text" name="user">
        <input type="text" name="pwd">
        <input type="submit" value="提交" />
    </form>
</body>
</html>

登录后的跳转首页:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>欢迎:{{ user }}登录</h1>

    <a href="/session_logout/">注销</a>
</body>
</html>
posted @ 2016-09-13 18:20  ccorz  阅读(1185)  评论(0编辑  收藏  举报