Django 表单(一)

HTML表单是网站交互性的经典方式,本章将记录如何用Django对用户提交的表单数据进行处理。

HTTP 请求

HTTP协议以"请求-回复"的方式工作。客户发送请求时,可以在请求中附加数据。服务器通过解析请求,就可以获得客户传来的数据,并根据URL来提供特定的服务。

  • 一个请求的大概流程:
    image.png

    通过TCP三次握手进行连接,然后开始传数据,客户端发送请求给服务器,服务器作出相应的相应返回给客户端

表单

表单是HTML中用于提交数据给服务器的一个标签,所有的表单元素(input/textarea/button...)都要放在form标签当中.然后有以下参数:

  • formmethod参数用于设置表单的提交方式,默认使用GET.
  • action用于设置表单的提交url,如果不写或者保持空字符串,那么将使用当前的URL.
  • action尽量指定一个url,因为这块会有浏览器兼容问题,有的浏览器填空是不能获取到对应的action

请求类型

  • GET: 请求指定的页面信息,并返回实体主体。它的参数在url中
  • HEAD: 只请求页面的首部。它的参数在body中
  • POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。
  • PUT: 从客户端向服务器传送的数据取代指定的文档的内容。
  • DELETE: 请求服务器删除指定的页面。
  • OPTIONS: 允许客户端查看服务器的性能。
  • TRACE: 请求服务器在响应中的实体主体部分返回所得到的内容。
  • PATCH: 实体中包含一个表,表中说明与该URI所表示的原内容的区别。
  • MOVE: 请求服务器将指定的页面移至另一个网络地址。
  • COPY: 请求服务器将指定的页面拷贝至另一个网络地址。
  • LINK: 请求服务器建立链接关系。
  • UNLINK: 断开链接关系。
  • WRAPPED: 允许客户端发送经过封装的请求。
  • Extension-mothed:在不改动协议的前提下,可增加另外的方法。
    一般常用GET与POST类型

HttpRequest

  • request.scheme:网络请求的协议,一般是http和https* request.body:http请求中传递的原始数据,是一个byte类型的string,如果要处理form表单上传上来的数据,应该使用request.POST,这个处理得比较好。
  • request.path:当前请求的URL路径,但是不包括scheme和域名,比如有http://www.myblog.com/blog/1。那么返回的将是/blog/1。会去除掉http和www.myblog.com
  • request.method:当前这个请求访问方法,一般为GET和POST,例如可以通过以下方式进行判断:
  1. request.GET:一个QueryDict的类字典类型的对象,可以通过访问字典的方式进行访问里面的值,里面的值是通过GET请求传递进来的参数。
  2. request.POST:同request.GET,但是是保存了通过POST请求传递上来的参数。注意,这个POST里面不包含上传的文件信息。
  • request.FILES : 如果要访问上传的文件,请使用request.FILES。
  • request.COOKIES:一个字典类型,包含了所有的cookie信息,key和value都是字符串。
  • request.session:返回一个QueryDict的类字典类型的集合,这个属性要有效,必须添加SessionMiddleware这个中间件。
  • request.META:存储所有请求的头部信息。
  • request.is_secure():如果是HTTPS的,那么返回True,否则返回False
  • request.is_ajax():这个请求是否是通过XMLHttpRequest进行访问的。
  • 他的实现原理就是去request.META中查找HTTP_X_REQUESTED_WITH,如果是XMLHttpRequest,那么将返回True,否则返回False。**一般在第三方库如jQuery中使用ajax方法都设置了这个值,如果自己手动写XMLHttpRequest,那么需要自己添加这个header,is_ajax这个方法才能生效。

HttpResponse

  • HttpResponse.init(content=’’,content_type=None,status=200,reason=None,charset=None)构造方法:

  • content:代表要发送给客户端的内容。

  • content_type:代表发送给客户端的内容类型,默认是“text/html;charset=utf-8”。

  • charset:响应体的编码,如果没有指定,则会使用content_type中设置的charset。

  • set_cookie(key,value=’’,max_age=None,expires=None,path=’/’,domain=None,secure=None,httponly=False)

  • key:cookie的key。

  • value:cookie的value。

  • max_age:cookie最大的过期时间。

  • expires:cookie的过期时间和日期。

  • path:针对哪个目录下有效果。

  • domain:域名,如果你想跨域使用cookie,必须设置这个值,比如domain=”.lawrence.com”,那么www.lawrence.comblogs.lawrence.comcalendars.lawrence.com都可以访问到这个cookie。

  • httponly:如果设置为True,客户端的js代码将不能访问到cookie。

  • delete_cookie(key,path=’/’,domain=None):删除cookie。

  • response子类

  • BadHeaderError

  • FileResponse

  • Http404

  • HttpResponse

  • HttpResponseBadRequest

  • HttpResponseForbidden

  • HttpResponseGone,

  • HttpResponseNotAllowed

  • HttpResponseNotFound

  • HttpResponseNotModified,

  • HttpResponsePermanentRedirect

  • HttpResponseRedirect,

  • HttpResponseServerError

  • JsonResponse

  • StreamingHttpResponse

其中将get,post,cookie,session简单介绍下。

get方法

用于接收用户的请求,调用的是View类中的get方法,其中传的数据在地址栏是可见的。

post方法

常用于和表单form一起用,数据是不可见的,当提交表单数据之后,会调用View类中的post方法。

cookie

在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。
Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。

session

Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

通过视图中对应的请求函数
class TestRequest(View):
    # get方式请求
    def get(self, request):
        #通过request.GET获取GET数据
        request.POST.get("param")

    # post方式请求
    def post(self, request):
        request.POST.get("param")

    ...其它方式类似
使用session与cookie完成登录页面

1 建立一个用户Model, 完成一个注册
2 crsf_token: 防跨域html标签,这个是django自带的app,一但使用form表单则必须使用

<form>
{% crsf_token %}
</form>
添加一个登录视图

通过session+cookie保存登录状态

# 验证用户名密码
if user:
    uuid_str = str(uuid.uuid4())
    httpResponse = HttpResponse(u"登录成功")
       httpResponse.set_cookie("mysession_id", uuid_str)
       request.session[uuid_str] = user.username
       return httpResponse
使用md5加密
import hashlib
password = "123123"
hashlib.md5(password).hexdigest()
uuid

以下这个函数会生成一个唯一字符串

import uuid
uuid.uuid4()

课后练习

  • 配置对应路由URL
#项目urls.py
url(r'^formtest/', include('formApp.urls')

#app应用urls.py
urlpatterns = [
    url(r'^register/$', views.Register.as_view(),name='register'),
    url(r'^login/$', views.Login.as_view(),name = 'login'),
    url(r'^index/$', views.Index.as_view(),name='index'),
    url(r'^logout/$', views.Logout.as_view(),name='logout')
]
  • 建立模型类
class User(models.Model):
    username = models.CharField(u'用户名',max_length= 20)
    password = models.CharField(max_length = 40,verbose_name = u'密码')
    class Meta:
        db_table = 'user'
        managed = True
  • 视图实现相关功能
  • 导入模块
from __future__ import unicode_literals
from django.shortcuts import render,redirect,reverse
from django.views import View
from django.http import HttpResponse
import hashlib
from .models import *
import uuid

def md5_password(password): #使用md5加密
    return hashlib.md5(password).hexdigest()

1 注册功能

# 通过通用视图的话,我们不需要判断请求method方法,执行实现对应method方法即可
class Register(View): 
    def get(self,request):
        # request 其实就是一个HttpRequest,它是django对请求的一个封装
        # view视图其实就是返回一个HttpResponse对象, render函数其实就是django方便我们渲染模版请求的一个封装
        return render(request,'register.html')
    def post(self,request):
        # 在post里面也是可以获取get参数,但是get方法不能获取post参数
        #param = request.GET.get("param")
        # 使用post方法获取前端参数
        user = request.POST.get('username')
        passwd = request.POST.get('password')
        # 密码这里一般不会明文直接存储到数据库,一般会进行加密
        passwd = md5_password(passwd)
        # 验证
        # 比如用户名是否存在,密码是否符合规则等等
        if User.objects.filter(username = user):
            print(User.objects.filter(username = user))
            return HttpResponse('该用户名已被使用,请选择其他账户,谢谢!')

        User.objects.create(username = user,password = passwd)
        return HttpResponse('注册成功&nbsp;&nbsp;&nbsp;&nbsp;前往<a href = "/formtest/login">登录</a>')

对应页面代码:
注册页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
<h2>注册界面</h2>
<form action="" method="post">
    {% csrf_token %}
    用户名:<input type="text" name="username" placeholder="请输入账号"/><br/>
    密  码:<input type="password" name="password"/><br/>
           <input type="submit" value="注册"/>
</form>
</body>
</html>

注册页面效果:
image.png

2 登录

class Login(View): #登录模块
    def get(self,request):
        return render(request,'login.html')
    def post(self,request):
        loginusername = request.POST.get('username')
        password = request.POST.get('password')
        loginpassword = md5_password(password)
        #filter方法是从数据库的取得匹配的结果,返回一个对象列表,如果记录不存在的话,它会返回[]。
        users = User.objects.filter(username = loginusername,password = loginpassword)
        if users:  #如果有记录
            unique_str = str(uuid.uuid4()) #生成唯一的字符串
            rep = redirect(reverse('index'))
            #每次用户第一次访问服务端, 把用户的唯一字符串 session_id加到cookie里面,发送给客户端;
            # 设置cookies,当用户登录成功后,把{sessionID :随机字符串} 组织成键值对,加到cookie里发送给用户
            cookies = rep.set_cookie('my_session_id',unique_str)
            # 服务端session保存
            request.session[unique_str] = loginusername  #使用session保存用户名,这里key可以为对应值,当做字典处理而已;
            return rep
        else:
            message  = u'用户名或者密码错误'
            return render(request,'login.html',locals())  #否则登录失败回到登录界面

class Index(View): #实现登录成功后功能
    def get(self,request):
        # my_session = request.COOKIES['my_session_id'] #获取客户端请求时带的cookies
        my_sessionid = request.COOKIES.get('my_session_id')
        if my_sessionid: #如果存在,则与服务器信息对比
            username = request.session.get(unique_str) #获取客户端的 session信息
            if username:
                return render(request,'index.html',locals())
        return redirect(reverse('login'))

页面代码:
登录界面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login登录页面</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        #topDiv,#bottomDiv{
            background: black;
            height: 40px;
            width: 100%;
            color: white;
            text-align: center;
            line-height: 40px;
        }
        #bottomDiv{
            height: 90px;
            line-height: 90px;
            position: absolute;
            bottom: 0;
        }

        #menu{
            width: 100%;
            height: 60px;
            color: #666;
        }
        #menu ul li{
            list-style: none;
            float: left;
            margin: 20px;
        }
        #loginbox {
            width: 400px;
            height: 200px;
            border: 1px solid red;
            margin: 90px auto;
            text-align: center;
        }
    </style>
</head>
<body>
<div id="topDiv">
    top顶部信息
</div>
<div id="bottomDiv">
    bottom底部资料
</div>
<div id="menu">
    <ul>
        <li>首页</li>
        <li>编程语言</li>
        <li>项目实战</li>
        <li>每日一学</li>
    </ul>
</div>
<div id="loginbox">
    <br/>
    <h2>登录界面</h2>
    <br/>
    <form action="" method="post">
            {% csrf_token %}
        帐&nbsp;号:<input type="text" name="username">
        <br/>
        <br/>
        密&nbsp;码:<input type="password" name="password">
        <br/>
        <br/>
        <input type="submit" value="登录">
        &nbsp;&nbsp;&nbsp;&nbsp;
        <input type="button" value="注册">
    </form>
</div>
</body>
</html>

首页index.html代码

#首页
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
<div style="margin-top:20px;color: black;font-size: 20px;line-height: 20px;text-align: center;";>
    欢迎{{username}}登录成功!<a href="/formtest/logout">退出</a><br/>
</div>
</body>
</html>

登录成功效果:
image.png

3 退出注销功能

class Logout(View): #退出注销功能
    def get(self,request):
        my_sessionid = request.COOKIES.get('my_session_id')  #获取cookie
        #判断浏览器访问时是否有session对象
        if request.session.has_key(my_sessionid):
            del request.session[my_sessionid]  #删除对应session,利用删除键值对的方式;
        return redirect(reverse('login'))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容