Django基础--Django基本命令路由配置系统(URLconf)编写视图Template数据库与ORM

Posted 始怡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django基础--Django基本命令路由配置系统(URLconf)编写视图Template数据库与ORM相关的知识,希望对你有一定的参考价值。

web框架

框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构。

使用框架可以帮你快速开发特定的系统。

简单地说,就是你用别人搭建好的舞台来做表演。

尝试搭建一个简单的web框架:

因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。

这个接口就是WSGI:Web Server Gateway Interface。

#---------------------myweb.py------------------------


from wsgiref.simple_server import  make_server

def foo1(request):
    f=open("alex.html","rb")
    data=f.read()
    f.close()

    return [data]

def foo2(request):
    f=open("egon.html","rb")
    data=f.read()
    f.close()

    return [data]

def reg(request):
    f=open("register.html","rb")
    data=f.read()
    f.close()

    return [data]

def login(request):
    f=open("login.html","rb")
    data=f.read()
    f.close()

    return [data]



def auth(request):
    print("++++++",request)
    user_union,pwd_union=request.get("QUERY_STRING").split("&")
    _,user=user_union.split("=")
    _,pwd=pwd_union.split("=")

    if user=="yuan" and pwd=="123":
        return [b"login successful"]
    else:
        return [b"user or password exists errors"]

def routers():
    URLpattern=(
        ("/login",login),
        ("/auth",auth),
        ("/alex",foo1),
        ("/egon",foo2),
        ("reg",reg)
    )
    return URLpattern

def application(environ,start_response):
    print("environ",environ)
    path=environ.get("PATH_INFO")
    print("path",path)
    start_response("200 ok",[("content-type","text/html")])

    urlpattern=routers()
    func=None
    for item in urlpattern:
        if path==item[0]:
            func=item[1]
            break

    if func:
        return func(environ)
    else:
        return [b"404"]

t=make_server("",8880,application)
t.serve_forever()  # 开始监听t请求:

 

#------------------------login.html--------------------------

<body>
<h1>登录页面</h1>
<form action="http://127.0.0.1:8880/auth">
    <p>姓名<input type="text" name="user"></p>
    <p>密码<input type="password" name="pwd"></p>
    <input type="submit" value="提交">
</form>
</body>

 

#---------------------egon.html--------------------------------


<body>
    <h1>welcome to egon\'s home</h1>
</body>



#----------------------alex.html----------------------------------


<body>
    <h1>welcome to alex\'s home</h1>
</body>

 注意:

application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。

Python内置了一个WSGI服务器,这个模块叫wsgiref    
    
    
application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

        //environ:一个包含所有HTTP请求信息的dict对象;
        
        //start_response:一个发送HTTP响应的函数。

在application()函数中,调用:

start_response(\'200 OK\', [(\'Content-Type\', \'text/html\')])

就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。
start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每
个Header用一个包含两个str的tuple表示。

通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。

然后,函数的返回值b\'<h1>Hello, web!</h1>\'将作为HTTP响应的Body发送给浏览器。

有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,
通过start_response()发送Header,最后返回Body。

 

MVC和MTV模式

MVC

  • 大部分开发语言中都有MVC框架
  • MVC框架的核心思想是:解耦
  • 降低各功能模块之间的耦合性,方便变更,更容易重构代码,最大程度上实现代码的重用
  • m表示model,主要用于对数据库层的封装
  • v表示view,用于向用户展示结果
  • c表示controller,是核心,用于处理请求、获取数据、返回结果

MTV

  • Django是一款python的web开发框架
  • 与MVC有所不同,属于MTV框架
  • m表示model,负责与数据库(ORM)交互
  • v表示view,是核心,负责接收请求、获取数据、返回结果
  • t表示template,负责呈现内容到浏览器

此外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template

 

下面进入正式部分。

主要知识点框架罗列:

  • 环境搭建
  • 定义模型
  • 使用后台管理
  • 路由配置系统(URLconf)

  • 编写视图
  • 定义模板

一 Django基本命令

 安装Django

pip install django

 

(双等号可以指定安装版本,比如个人安装1.11.4版本,就可以使用:pip install django==1.11.4)

说明:使用pip install django命令进行安装时,会自动删除旧版本,再安装新版本

查看Django版本

进入python shell,运行如下代码:

import django
django.get_version()

 

创建项目

django-admin startproject project_name

 

项目结构:

例如创建一个名为test1的项目,结构如下:

说明:

  • manage.py:一个命令行工具,可以使你用多种方式对Django项目进行交互
  • 内层的目录:项目的真正的Python包
  • _init _.py:一个空文件,它告诉Python这个目录应该被看做一个Python包
  • settings.py:项目的配置
  • urls.py:项目的URL声明
  • wsgi.py:项目与WSGI兼容的Web服务器入口

在项目下创建应用,比如blog:

项目与应用关系:
        一个项目有多个应用
        一个应用可以被多个项目拥有

命令:

python manage.py startapp blog

 

 

启动django项目

python manage.py runserver IP PORT
  • 可以不写ip,默认端口为8000

  这样我们的django就启动起来了!当我们访问:http://127.0.0.1:8080/时就可以看到:

 

同步更改数据库表或字段

 

注意:Django 1.7.1 及以上的版本需要用以下命令
    python manage.py makemigrations
    python manage.py migrate

 清空数据库

 

python manage.py flush

 此命令会询问是 yes 还是 no, 选择 yes 会把数据全部清空掉,只留下空表。

 

 Django 项目环境终端

python manage.py shell

 

python manage.py dbshell

 更多命令

 

python manage.py

 查看所有的命令,忘记子名称的时候特别有用。

 

一些常用的配置:

数据库配置

在settings.py文件中,通过DATABASES项进行数据库设置。

Django默认使用SQLite数据库,同时支持mysql等主流数据库。

<1> sqlite

            django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3

在settings里有如下设置:

 


<2> mysql 引擎名称:django.db.backends.mysql

 

mysql驱动程序

       MySQLdb(mysql python)
       mysqlclient
       MySQL
       PyMySQL(纯python的mysql驱动程序)

 

 如果我们想要更改为MySQL数据库,需要修改如下:

 

DATABASES = {

    \'default\': {

        \'ENGINE\': \'django.db.backends.mysql\', 

        \'NAME\': \'books\',    #你的数据库名称

        \'USER\': \'root\',   #你的数据库用户名

        \'PASSWORD\': \'\', #你的数据库密码

        \'HOST\': \'\', #你的数据库主机,留空默认为localhost

        \'PORT\': \'3306\', #你的数据库端口

    }

}

 注意:

NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建

USER和PASSWORD分别是数据库的用户名和密码。

设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。

然后,启动项目,会报错:no module named MySQLdb

这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL

所以,我们只需要找到项目名文件下的__init__,在里面写入:

import pymysql
pymysql.install_as_MySQLdb()

问题解决!

 

static配置

settings文件中static的配置如下:

STATIC文件还可以配置STATICFILES_DIRS,指定额外的静态文件存储位置。

 

#注意1:
        #为了后端的更改不会影响前端的引入,避免造成前端大量修改

        STATIC_URL = \'/static/\'               #引用名
        STATICFILES_DIRS = (
            os.path.join(BASE_DIR,"statics")  #实际名 ,即实际文件夹的名字
        )

        #django对引用名和实际名进行映射,引用时,只能按照引用名来,不能按实际名去找

例如:我们写一个模板文件,一般会需要引入JS文件
我们常写的格式为:
#<script src="/statics/jquery-3.2.1.js"></script>
#--------------错误--------------------------

正确引用方式:

必须用STATIC_URL = \'/static/\':
#<script src="/static/jquery-3.2.1.js"></script>

 

 #注意2(statics文件夹写在不同的app下,静态文件的调用):

        STATIC_URL = \'/static/\'

        STATICFILES_DIRS=(
            (\'hello\',os.path.join(BASE_DIR,"app01","statics")) ,
        )

        #<script src="/static/hello/jquery-3.2.1.js"></script>

 

#注意3:
        STATIC_URL = \'/static/\'
        {% load staticfiles %}
       # <script src={% static "jquery-3.2.1.js" %}></script>

  日志记录部分:

  应用场景:对于每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分:

LOGGING = {
    \'version\': 1,
    \'disable_existing_loggers\': False,
    \'handlers\': {
        \'console\':{
            \'level\':\'DEBUG\',
            \'class\':\'logging.StreamHandler\',
        },
    },
    \'loggers\': {
        \'django.db.backends\': {
            \'handlers\': [\'console\'],
            \'propagate\': True,
            \'level\':\'DEBUG\',
        },
    }
}

 

 二 路由配置系统(URLconf)

 

  • 在settings.py文件中通过ROOT_URLCONF指定根级url的配    
  • urlpatterns是一个url()实例的列表

  例:

 

ROOT_URLCONF = \'BlogSM.urls\'

(创建的项目名称为:BlogSM)

 

urlpatterns = [
url(r\'^admin/\', admin.site.urls),
url(r\'^add/\',views.add),
  url(r\'^$\',views.add),  #(此条一般用作增加用户体验,比如首页展示特定内容)

]

 

  • 一个url()对象包括:
    • 正则表达式
    • 视图函数
    • 名称name
  • 编写URLconf的注意:
    • 若要从url中捕获一个值,需要在它周围设置一对圆括号
    • 不需要添加一个前导的反斜杠,如应该写作\'test/\',而不应该写作\'/test/\'
    • 每个正则表达式前面的r表示字符串不转义
  • 请求的url被看做是一个普通的python字符串,进行匹配时不包括get或post请求的参数及域名
http://www.itcast.cn/python/1/?i=1&p=new,只匹配“/python/1/”部分

示例:

urlpatterns = [
    url(r\'^articles/2003/$\', views.special_case_2003),
    url(r\'^articles/([0-9]{4})/$\', views.year_archive),
    url(r\'^articles/([0-9]{4})/([0-9]{2})/$\', views.month_archive),
    url(r\'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$\', views.article_detail),
]

 

一些请求的例子:

    /articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。
    /articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。
    /articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数
                       views.month_archive(request, \'2005\', \'03\')。

 

 

无名分组(named group)

正则表达式非命名组(通过圆括号),通过位置参数传递给视图

 

有名分组(named group)

正则表达式命名组,通过关键字参数传递给视图

语法:(?P<name>pattern),其中name 是组的名称,pattern 是要匹配的模式。

例如:

urlpatterns = [
    url(r\'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$\', views.month_archive),
  url(r\'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$\', views.article_detail),
]
 ]
   /articles/2005/03/    
    请求将调用views.month_archive(request, year=\'2005\', month=\'03\')函数
    /articles/2003/03/03/ 
    请求将调用函数views.article_detail(request, year=\'2003\', month=\'03\', day=\'03\')。

 URLconf 在什么上查找

URLconf 在请求的URL 上查找,将它当做一个普通的Python 字符串。不包括GET和POST参数以及域名。

例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/。

在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/。

URLconf 不检查请求的方法。换句话讲,所有的请求方法 —— 同一个URL的POST、GET、HEAD等等 —— 都将路由到相同的函数。

 

 捕获的参数永远是字符串

每个捕获的参数都作为一个普通的Python 字符串传递给视图,无论正则表达式使用的是什么匹配方式。例如,下面这行URLconf 中:
url(r\'^articles/(?P<year>[0-9]{4})/$\', views.year_archive),

views.year_archive() 的year 参数将是一个字符串

 

指定视图参数的默认值

有一个方便的小技巧是指定视图参数的默认值。 下面是一个URLconf 和视图的示例:
# URLconf
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r\'^blog/$\', views.page),
    url(r\'^blog/page(?P<num>[0-9]+)/$\', views.page),
]

# View (in blog/views.py)
def page(request, num="1"):

    ...

 在上面的例子中,两个URL模式指向同一个视图views.page —— 但是第一个模式不会从URL 中捕获任何值。如果第一个模式匹配,page() 函数将使用num参数的默认值"1"。如果第二个模式匹配,page() 将使用正则表达式捕获的num 值。

包含其它的URLconfs


from django.conf.urls import include, url

urlpatterns = [
   url(r\'^admin/\', admin.site.urls),
   url(r\'^blog/\', include(\'blog.urls\')),
]

匹配过程:先与主URLconf匹配,成功后再用剩余的部分与应用中的URLconf匹配 


请求http://www.itcast.cn/booktest/1/
在sesstings.py中的配置:
url(r\'^booktest/\', include(\'booktest.urls\', namespace=\'booktest\')),
在booktest应用urls.py中的配置
url(r\'^([0-9]+)/$\', views.detail, name=\'detail\'),
匹配部分是:/booktest/1/
匹配过程:在settings.py中与“booktest/”成功,再用“1/”与booktest应用的urls匹配

  • 使用include可以去除urlconf的冗余
  • 参数:视图会收到来自父URLconf、当前URLconf捕获的所有参数
  • 在include中通过namespace定义命名空间,用于反解析

 

name参数

\'\'\'

urlpatterns = [
    url(r\'^index\',views.index,name=\'INDEX\'),

]
###################

def index(req):
    if req.method==\'POST\':
        username=req.POST.get(\'username\')
        password=req.POST.get(\'password\')
        if username==\'alex\' and password==\'123\':
            return HttpResponse("登陆成功")

    return render(req,\'index.html\')

#####################

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#     <form action="/index/" method="post">#}
     <form action="{% url \'INDEX\' %}" method="post">
         用户名:<input type="text" name="username">
         密码:<input type="password" name="password">
         <input type="submit" value="submit">
     </form>
</body>
</html>


#######################

\'\'\'

 

错误视图

 

Django原生自带几个默认视图用于处理HTTP错误

404 (page not found) 视图

  • defaults.page_not_found(request, template_name=\'404.html\')
  • 默认的404视图将传递一个变量给模板:request_path,它是导致错误的URL
  • 如果Django在检测URLconf中的每个正则表达式后没有找到匹配的内容也将调用404视图
  • 如果在settings中DEBUG设置为True,那么将永远不会调用404视图,而是显示URLconf 并带有一些调试信息
  • 在templates中创建404.html
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
找不到了
<hr/>
{{request_path}}
</body>
</html>

 在settings.py中修改调试

DEBUG = False
ALLOWED_HOSTS = [\'*\', ]

 请求一个不存在的地址

http://127.0.0.1:8000/test/

 500 (server error) 视图

  • defaults.server_error(request, template_name=\'500.html\')
  • 在视图代码中出现运行时错误
  • 默认的500视图不会传递变量给500.html模板
  • 如果在settings中DEBUG设置为True,那么将永远不会调用505视图,而是显示URLconf 并带有一些调试信息

400 (bad request) 视图

    defaults.bad_request(request, template_name=\'400.html\')
    错误来自客户端的操作
    当用户进行的操作在安全方面可疑的时候,例如篡改会话cookie

 

编写视图

\'\'\'
http请求-响应过程中有两个核心对象:

        http请求对象:HttpRequest

        http响应响应:HttpResponse

所在位置:django.http

\'\'\'

 一个视图函数,或者简短来说叫做视图,是一个简单的Python函数,它接受web请求,并且返回web响应。

响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。

无论视图本身包含什么逻辑,都要返回响应。

一个简单的视图

返回当前日期和时间:

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

 每个视图函数都应接收HttpRequest对象作为第一个参数,一般叫做request。

 

render函数

--------------render(request, template_name[, context])

结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

参数:
     request: 用于生成响应的请求对象。

     template_name:要使用的模板的完整名称,可选的参数

     context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。

     content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。

     status:响应的状态码。默认为200。

 

redirect函数

-----------------------------------url.py

 url(r"login",   views.login),
 url(r"yuan_back",   views.yuan_back),

-----------------------------------views.py
def login(req):
    if req.method=="POST":
        if 1:
            # return redirect("/yuan_back/")
            name="yuanhao"

            return render(req,"my backend.html",locals())

    return render(req,"login.html",locals())


def yuan_back(req):

    name="苑昊"

    return render(req,"my backend.html",locals())

-----------------------------------login.html

<form action="/login/" method="post">
    <p>姓名<input type="text" name="username"></p>
    <p>性别<input type="text" name="sex"></p>
    <p>邮箱<input type="text" name="email"></p>
    <p><input type="submit" value="submit"></p>
</form>
-----------------------------------my backend.html
<h1>用户{{ name }}你好</h1>

#总结: render和redirect的区别:
#   1 如果 render的页面需要模板语言渲染,需要的将数据库的数据加载到html,那么所有的这一部分
#     除了写在yuan_back的视图函数中,必须还要写在login中,代码重复,没有解耦.

#   2 the most important: url没有跳转到/yuan_back/,而是还在/login/,所以当刷新后
#     又得重新登录.

视图之其他:

 HttpReqeust对象

  • 服务器接收到http协议的请求后,会根据报文创建HttpRequest对象
  • 视图函数的第一个参数是HttpRequest对象
  • 在django.http模块中定义了HttpRequest对象的API

属性

  • 下面除非特别说明,属性都是只读的
  • path:一个字符串,表示请求的页面的完整路径,不包含域名
  • method:一个字符串,表示请求使用的HTTP方法,常用值包括:\'GET\'、\'POST\'
  • encoding:一个字符串,表示提交的数据的编码方式
    • 如果为None则表示使用浏览器的默认设置,一般为utf-8
    • 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值
  • GET:一个类似于字典的对象,包含get请求方式的所有参数
  • POST:一个类似于字典的对象,包含post请求方式的所有参数
  • FILES:一个类似于字典的对象,包含所有的上传文件
  • COOKIES:一个标准的Python字典,包含所有的cookie,键和值都为字符串
  • session:一个既可读又可写的类似于字典的对象,表示当前的会话,只有当Django 启用会话的支持时才可用,详细内容见“状态保持”

方法

  • is_ajax():如果请求是通过XMLHttpRequest发起的,则返回True

 

QueryDict对象

  • request对象的属性GET、POST都是QueryDict类型的对象
  • 与python字典不同,QueryDict类型的对象用来处理同一个键带有多个值的情况
  • 方法get():根据键获取值
    • 只能获取键的一个值
    • 如果一个键同时拥有多个值,获取最后一个值
    • dict.get(\'键\',default)
      或简写为
      dict[\'键\']
      

       

  • 方法getlist():根据键获取值
    • 将键的值以列表返回,可以获取一个键的多个值
    • dict.getlist(\'键\',default)
      GET属性

 GET属性

  • QueryDict类型的对象
  • 包含get请求方式的所有参数
  • 与url请求地址中的参数对应,位于?后面
  • 参数的格式是键值对,如key1=value1
  • 多个参数之间,使用&连接,如key1=value1&key2=value2
  • 键是开发人员定下来的,值是可变的
  • 示例如下
  • 创建视图getTest1用于定义链接,getTest2用于接收一键一值,getTest3用于接收一键多值
def getTest1(request):
    return render(request,\'booktest/getTest1.html\')
def getTest2(request):
    return render(request,\'booktest/getTest2.html\')
def getTest3(request):
    return render(request,\'booktest/getTest3.html\')
  • 配置url
url(r\'^getTest1/$\', views.getTest1),
url(r\'^getTest2/$\', views.getTest2),
url(r\'^getTest3/$\', views.getTest3),
  • 创建getTest1.html,定义链接
<html>
<head>
    <title>Title</title>
</head>
<body>
链接1:一个键传递一个值
<a href="/getTest2/?a=1&b=2">gettest2</a><br>
链接2:一个键传递多个值
<a href="/getTest3/?a=1&a=2&b=3">gettest3</a>
</body>
</html>
  •  完善视图getTest2的代码
def getTest2(request):
    a=request.GET[\'a\']
    b=request.GET[\'b\']
    context={\'a\':a,\'b\':b}
    return render(request,\'booktest/getTest2.html\',context)
  • 创建getTest2.html,显示接收结果
<html>
<head>
    <title>Title</title>
</head>
<body>
a:{{ a }}<br>
b:{{ b }}
</body>
</html>

 

  • 完善视图getTest3的代码
def getTest3(request):
    a=request.GET.getlist(\'a\')
    b=request.GET[\'b\']
    context={\'a\':a,\'b\':b}
    return render(request,\'booktest/getTest3.html\',context)
  • 创建getTest3.html,显示接收结果
<html>
<head>
    <title>Title</title>
</head>
<body>
a:{% for item in a %}
{{ item }}
{% endfor %}
<br>
b:{{ b }}
</body>
</html>

 

POST属性

  • QueryDict类型的对象
  • 包含post请求方式的所有参数
  • 与form表单中的控件对应
  • 问:表单中哪些控件会被提交?
  • 答:控件要有name属性,则name属性的值为键,value属性的值为键,构成键值对提交
    • 对于checkbox控件,name属性一样为一组,当控件被选中后会被提交,存在一键多值的情况
  • 键是开发人员定下来的,值是可变的
  • 示例如下
  • 定义视图postTest1
def postTest1(request):
    return render(request,\'booktest/postTest1.html\')
  •  配置url
url(r\'^postTest1$\',views.postTest1)

 创建模板postTest1.html

<html>
<head>
    <title>Title</title>
</head>
<body>
<form method="post" action="/postTest2/">
    姓名:<input type="text" name="uname"/><br>
    密码:<input type="password" name="upwd"/><br>
    性别:<input type="radio" name="ugender" value="1"/>男
    <input type="radio" name="ugender" value="0"/>女<br>
    爱好:<input type="checkbox" name="uhobby" value="胸口碎大石"/>胸口碎大石
    <input type="checkbox" name="uhobby" value="跳楼"/>跳楼
    <input type="checkbox" name="uhobby" value="喝酒"/>喝酒
    <input type="checkbox" name="uhobby" value="爬山"/>爬山<br>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
  • 创建视图postTest2接收请求的数据
def postTest2(request):
    uname=request.POST[\'uname\']
    upwd=request.POST[\'upwd\']
    ugender=request.POST[\'ugender\']
    uhobby=request.POST.getlist(\'uhobby\')
    context={\'uname\':uname,\'upwd\':upwd,\'ugender\':ugender,\'uhobby\':uhobby}
    return render(request,\'booktest/postTest2.html\',context)
  • 配置url
url(r\'^postTest2$\',views.postTest2)

 

  • 创建模板postTest2.html
<html>
<head>
    <title>Title</title>
</head>
<body>
{{ uname }}<br>
{{ upwd }}<br>
{{ ugender }}<br>
{{ uhobby }}
</body>
</html>

 

注意:使用表单提交,注释掉settings.py中的中间件crsf

 

HttpResponse对象

 

  • 在django.http模块中定义了HttpResponse对象的API
  • HttpRequest对象由Django自动创建,HttpResponse对象由程序员创建
  • 不调用模板,直接返回数据
from django.http import HttpResponse

def index(request):
    return HttpResponse(\'你好\')

 

属性

  • content:表示返回的内容,字符串类型
  • charset:表示response采用的编码字符集,字符串类型
  • status_code:响应的HTTP响应状态码
  • content-type:指定输出的MIME类型

方法

  • init :使用页内容实例化HttpResponse对象
  • write(content):以文件的方式写
  • flush():以文件的方式输出缓存区
  • set_cookie(key, value=\'\', max_age=None, expires=None):设置Cookie
    • key、value都是字符串类型
    • max_age是一个整数,表示在指定秒数后过期
    • expires是一个datetime或timedelta对象,会话将在这个指定的日期/时间过期,注意datetime和timedelta值只有在使用PickleSerializer时才可序列化
    • max_age与expires二选一
    • 如果不指定过期时间,则两个星期后过期
  • delete_cookie(key):删除指定的key的Cookie,如果key不存在则什么也不发生

 

子类JsonResponse

  • 返回json数据,一般用于异步请求
  • _init _(data)
  • 帮助用户创建JSON编码的响应
  • 参数data是字典对象
  • JsonResponse的默认Content-Type为application/json
from django.http import JsonResponse

def index2(requeset):
    return JsonResponse({\'list\': \'abc\'})

 

状态保持

  • http协议是无状态的:每次请求都是一次新的请求,不会记得之前通信的状态
  • 客户端与服务器端的一次通信,就是一次会话
  • 实现状态保持的方式:在客户端或服务器端存储与会话有关的数据
  • 存储方式包括cookie、session,会话一般指session对象
  • 使用cookie,所有数据存储在客户端,注意不要存储敏感信息
  • 推荐使用sesison方式,所有数据存储在服务器端,在客户端cookie中存储session_id
  • 状态保持的目的是在一段时间内跟踪请求者的状态,可以实现跨页面访问当前请求者的数据
  • 注意:不同的请求者之间不会共享这个数据,与请求者一一对应

 

cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能通过cookie的内容来判断这个是“谁”了。

cookie虽然在一定程度上解决了“保持状态”的需求,但是由于cookie本身最大支持4096字节,以及cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是session。

我们可以给每个客户端的cookie分配一个唯一的id,这样用户在访问时,通过cookie,服务器就知道来的人是“谁”。然后我们再根据不同的cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。

3、总结而言:cookie弥补了http无状态的不足,让服务器知道来的人是“谁”;但是cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过cookie识别不同的用户,对应的在session里保存私密的信息以及超过4096字节的文本。

认证应用

场景:

一个登陆页面,在验证了用户名和密码的正确性后跳转到后台的页面。

但是测试发现,如果绕过登陆页面,直接输入后台的url地址也可以直接访问的。这个显然是不合理的。

我们缺失的就是cookie和session配合的验证。

每当我们使用一款浏览器访问一个登陆页面的时候,一旦我们通过了认证。服务器端就会发送一组随机唯一的字符串(假设是123abc)到浏览器端,这个被存储在浏览端的东西就叫cookie。而服务器端也会自己存储一下用户当前的状态,比如login=true,username=hahaha之类的用户信息。但是这种存储是以字典形式存储的,字典的唯一key就是刚才发给用户的唯一的cookie值。那么如果在服务器端查看session信息的话,理论上就会看到如下样子的字典

{\'123abc\':{\'login\':true,\'username:hahaha\'}}

因为每个cookie都是唯一的,所以我们在电脑上换个浏览器再登陆同一个网站也需要再次验证。那么为什么说我们只是理论上看到这样子的字典呢?因为处于安全性的考虑,其实对于上面那个大字典不光key值123abc是被加密的,value值{\'login\':true,\'username:hahaha\'}在服务器端也是一样被加密的。所以我们服务器上就算打开session信息看到的也是类似与以下样子的东西

{\'123abc\':dasdasdasd1231231da1231231}

COOKIE

---------------------views.py

def login(request):
    if request.method == "POST":
        user=request.POST.get("user")
        pwd=request.POST.get("pwd")

        if user == "kaylee" and pwd == "123":
            print(request.COOKIES)  #第一次:{}
            print(request.session)  #第一次:<django.contrib.sessions.backends.db.SessionStore object at 0x0000000003D4D0F0>
            obj=redirect("/index/") #这一步不会走index视图,注意 return redirect才会重定向
            obj.set_cookie("Yuan123",11111111,max_age= 10)  #三个参数:key,value,期限
            return obj
    return render(request,"login.html")



def index(request):
    print("+++++++++++++",request.COOKIES)  #第一次{}  第二次:{\'Yuan123\': \'11111111\'} 第三次:{\'Yuan123\': \'11111111\'}
    print("-------------",request.session)  #<django.contrib.sessions.backends.db.SessionStore object at 0x0000000003A9A828>
    is_login=request.COOKIES.get("Yuan123",None)
    if is_login:
        return render(request,"index.html")
    else:
        return redirect("/login/")

 

SESSION

  • 先在templates目录下创建两个html,login.html负责登录页面。backend页面代表后台页面
  • 第二步 编辑app01应用下的views.py文件,编写代码逻辑部分
--------------------------------views.py

from django.shortcuts import render
from django.shortcuts import redirect
def login(request):
    if request.method=="POST":
        username=request.POST[\'username\']
        pwd=request.POST[\'passwd\']
        if username==\'abc\' and pwd==\'123\':
            #设置session内部的字典内容
            request.session[\'is_login\']=\'true\'
            request.session[\'username\']=\'abc\'
            #登录成功就将url重定向到后台的url
            return redirect(\'/backend/\')
    #登录不成功或第一访问就停留在登录页面
    return render(request,\'login.html\')
def backend(request):
    """
    这里必须用读取字典的get()方法把is_login的value缺省设置为False,
    当用户访问backend这个url先尝试获取这个浏览器对应的session中的
    is_login的值。如果对方登录成功的话,在login里就已经把is_login
    的值修改为了True,反之这个值就是False的
    """
    is_login=request.session.get(\'is_login\',False)
    #如果为真,就说明用户是正常登陆的
    if is_login:
        #获取字典的内容并传入页面文件
        cookie_content=request.COOKIES
        session_content=request.session
        username=request.session[\'username\']
        return render(request,\'backend.html\',
                      {
            \'cookie_content\':cookie_content,
            \'session_content\':session_content,
            \'username\':username
                      })
    else:
        """
        如果访问的时候没有携带正确的session,
        就直接被重定向url回login页面
        """
        return redirect(\'/login/\')
def logout(request):
    """
    直接通过request.session[\'is_login\']回去返回的时候,
    如果is_login对应的value值不存在会导致程序异常。所以
    需要做异常处理
    """
    try:
        #删除is_login对应的value值
        del request.session[\'is_login\']
    except KeyError:
        pass
    #点击注销之后,直接重定向回登录页面
    return redirect(\'/login/\')

 

  • 第三步,编辑mydjango目录下的urls.py文件。设置函数与页面的绑定关系

 

--------------------------------------urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r\'^admin/\', admin.site.urls),
    url(r\'^login/\', views.login),
    url(r\'^backend/\', views.backend),
    url(r\'^logout/\', views.logout),
]
  •  最后打开浏览器直接访问/backend/页面的时候直接就被重定向到了/login/
  • 只有在输入了正确的用户名和密码之后才进入到了/backend/页面
--------------------backend.html-----------------内容截取

<div class="container">
    <h2>cookie 内容是 {{ cookie_content }}</h2>
    <h2>session 内容是 {{ session_content }}</h2>
    <h2>登录用户名 :{{ username }}</h2>
    <a href="http://830909.blog.51cto.com/logout/">注销</a>
</div>

 页面显示结果:

 

 

从上图中我们看到有一下几点:

1、login页面正确登录的话,后台页面可以获取到浏览器携带的cookie的。

2、第一行的sessionid其实就是cookie值

3、session的内容是加密的,从客户端获取不到session的内容

4、服务端可以通过预设的key值取出session的内容并打印到前端

从火狐浏览器里查看cookie

 

 

django的session默认是存储在数据库里的,我们再到数据库查看一下真正session内容

 

 

 

 cookie、session总结:

 

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



# 2、设置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保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。

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

 

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

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

 

 1、数据库Session

jango默认支持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")
 
        ...

 

 2、缓存Session

a. 配置 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,默认修改之后才保存
 
 
 
b. 使用
 
    同上

 

 3、文件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,默认修改之后才保存
 
b. 使用
 
    同上

 

 4、缓存+数据库Session

 

 

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

 

5、加密cookie Session

a. 配置 settings.py
     
    SESSION_ENGINE = \'django.contrib.sessions.backends.signed_cookies\'   # 引擎
 
b. 使用
 
    同上

 

扩展:Session用户验证

def login(func):
    def wrap(request, *args, **kwargs):
        # 如果未登陆,跳转到指定页面
        if request.path == \'/test/\':
            return redirect(\'http://www.baidu.com\')
        return func(request, *args, **kwargs)
    return wrap

 

 四 Template

 python的模板:HTML代码+逻辑控制代码

模板支持的语法

变量(使用双大括号来引用变量)

语法格式: {{var_name}}

def current_time(req):

    now=datetime.datetime.now()

    return render(req, \'current_datetime.html\', {\'current_date\':now})

 深度变量的查找(万能的句点号)

我们通过 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。

首先,句点可用于访问列表索引
-----------views.py
fruit=[\'apples\', \'bananas\', \'carrots\']

----------------templates
<h2>{{ fruit.0 }}</h2>

 访问字典:

-----------views.py
dic={"name":"kaylee","age":18}
----------------templates
{% for i,v in dic.items %} 
{{ i }} {{ v }}
{% endfor %}

{{ dic.name }}
{{ dic.age }}

 

#同样,也可以通过句点来访问对象的属性
比方说, Python 的 datetime.date 对象有
#year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:
-----------views.py
d = datetime.date(1993, 5, 2) ----------------templates {{ d.year }} {{ d.month }} {{ d.day }}

 

使用了一个自定义的类,通过实例变量加一点(dots)来访问它的属性,这个方法适
# 用于任意的对象。
-----------views.py
>>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name ----------------templates {{ person.first_name }} {{ person.last_name }}

 

# 点语法也可以用来引用对象的方法。 例如,每个 Python 字符串都有 upper() 和 isdigit()
# 方法,你在模板中可以使用同样的句点语法来调用它们:
-----------views.py
Context={\'var\': \'123\'}
----------------templates
{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}

 

变量的过滤器(filter)的使用

  # 1  add          :   给变量加上相应的值
   #
   # 2  addslashes   :    给变量中的引号前加上斜线
   #
   # 3  capfirst     :    首字母大写
   #
   # 4  cut          :   从字符串中移除指定的字符
   #
   # 5  date         :   格式化日期字符串
   #
   # 6  default      :   如果值是False,就替换成设置的默认值,否则就是用本来的值
   #
   # 7  default_if_none:  如果值是None,就替换成设置的默认值,否则就使用本来的值

 

#实例:

#value1="aBcDe"
{{ value1|upper }}<br>

#value2=5
{{ value2|add:3 }}<br>

#value3=\'he  llo wo r ld\'
{{ value3|cut:\' \' }}<br>

#import datetime
#value4=datetime.datetime.now()
{{ value4|date:\'Y-m-d\' }}<br>

#value5=[]
{{ value5|default:\'空的\' }}<br>

#value6=\'<a href="#">跳转</a>\'

{{ value6 }}

{% autoescape off %}
  {{ value6 }}
{% endautoescape %}

{{ value6|safe }}<br>

{{ value6|striptags }}

#value7=\'1234\'
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|slice:":-1" }}<br>

#value8=\'http://www.baidu.com/?a=1&b=3\'
{{ value8|urlencode }}<br>
    value9=\'hello I am yuan\'

 

标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)

语法格式:    {% tags %}

{% if %} 的使用

{% if %}标签计算一个变量值,如果是“true”,即它存在、不为空并且不是false的boolean值,系统则会显示{% if %}和{% endif %}间的所有内容

{% if num >= 100 and 8 %}

    {% if num > 200 %}
        <p>num大于200</p>
    {% else %}
        <p>num大于100小于200</p>
    {% endif %}

{% elif num < 100%}
    <p>num小于100</p>

{% else %}
    <p>num等于100</p>

{% endif %}

 

{% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量
{% if %} 标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义,例如下面的标签是不合法的:

{% if obj1 and obj2 or obj3 %} 

 

{% for %}的使用

{% for %}标签允许你按顺序遍历一个序列中的各个元素,每次循环模板系统都会渲染{% for %}和{% endfor %}之间的所有内容

 

-----------views.py
class Person(object): def __init__(self,name): self.name=name p1=Person("egon") p2=Person("阿毛") p3=Person("ago") querySet=[p1,p2,p3] ----------------templates {% for person in querySet %} <p>{{ person.name }}</p> {% endfor %}

 

#在标签里添加reversed来反序循环列表:

    {% for obj in list reversed %}
    ...
    {% endfor %}

 

#{% for %}标签可以嵌套:

    {% for country in countries %}
        <h1>{{ country.name }}</h1>
        <ul>
         {% for city in country.city_list %}
            <li>{{ city }}</li>
         {% endfor %}
        </ul>
    {% endfor %}