视图

概述

  1. 作用:
    视图接受web请求,并响应web请求.
  2. 本质:
    视图就是python中的函数
  3. 响应的内容:
    • 网页:
      1. 重定向
      2. 错误视图:
        404错误.
        500错误.(服务器内部请求)
        400错误.
    • JSON数据

URL配置

  1. 配置流程:

    • 指定根级url配置文件:
      在settings.py的ROOT_URLCONF选项

      1
      ROOT_URLCONF = 'me.urls'
    • urlpatterns:

      1
      2
      3
      4
      5
      6
      7
      from django.conf.urls import url,include
      from django.contrib import admin

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

      一个url实例的列表,元素是url对象

    • url对象的三参数:

      1. 正则表达式

      2. 视图名称

      3. 名称(之后再讲)

        url匹配的注意事项:

      4. 如果想从url中获取一个值,需要对正则加小括号形成分组.

      5. 匹配正则前方不需要添加反斜杠.
        因为都是在最后加的.

        1
        url(r'^students/$',views.index)
  2. 引入其他其他url配置:

    在应用中创建urls.py文件,定义本应用的url配置.
    在工程urls.py文件中使用include()方法

    1
    2
    3
    4
    urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^',include('myapp.urls'))
    ]
  • ulr的反向解析:

    1. 概述:
      如果在视图,模板中使用了硬编码链接,在rul配置发生改变时,动态生成链接的地址.
    2. 解决:在使用链接时,通过url配置的名称,动态生成url地址
    3. 作用:使用url模板

视图函数

  1. 定义视图:
    本质:一个函数.
    视图参数:

    • 一个HttpRequest的实例.
    • 通过正则表达式获取的参数.

    位置:在views.py中定义

  2. 错误视图:

    • 404视图:

      1. 找不到网页时返回(url匹配不成功时).

      2. 404.html不必匹配视图,在templates目录下定义404.html即可

      3. django会自动给404.html传递一个request_path参数(就是错误的路径)

      4. 配置settings.py:
        debug:

        1
        2
        # 如果为True,则永远不会调用404.html
        DEBUG = False

        allowed_hosts:

        1
        2
        # 允许任何人访问
        ALLOWED_HOSTS = [*]
    • 500视图:
      在视图代码中出现错误(服务器代码)

    • 400视图:
      错误出现在客户的操作.

HttpRequest对象

  • 概述:
    服务器接收http请求后,会根据报文创建HttpRequest对象.
    视图的第一个参数就是HttpRequest.
    HttpRequest对象是django创建的,之后调用视图时传递给视图.

  • 属性:

    1. path:
      请求的完整路径(不包含域名和端口)

    2. method:
      请求的方式

    3. encoding:
      就是请求响应头的accept-encoding参数,
      表示浏览器提交的数据的编码方式.一般为UTF-8

    4. GET:
      类似于字典的对象,包含了get请求的所有参数
      (比方说,当你传递的url为www.baidu.com/?a=1&b=2A&c=3,get对象就是{'a':1,'b':2,'c':3})

    5. POST:

      类似于字典的对象,包含了post请求的所有参数.

    6. FIELS:
      类似于字典的对象,包含了所有上传的文件.

    7. COOKIES:
      字典,包含所有的cookies

    8. session:
      类似字典的对象,表示当前会话

  • 方法:

    is_ajax():
    如果是通过XMLHttpRequst发起的,返回True

  • QueryDict对象:

    1. request对象中的GET,POST都属于QueryDict对象
    2. 方法:
      get():作用:根据键获取值
      getlist():将键的值易列表的形式返回,可以获取多个值.
  • GET属性:
    获取浏览器传递过来给服务器的数据.

    1. 如果传入的url为:127.0.0.1/8000/get1?a=1&b=2&c=3
      可以使用下面的视图函数获取:

      1
      2
      3
      4
      5
      6
      def get1(request):
      # 使用request.GET.get获取abc参数的值
      a = request.GET.get('a')
      b = request.GET.get('b')
      c = request.GET.get('c')
      return HttpResponse(a,b,c)
    2. 如果传入的url为:127.0.0.1/8000/get1?a=1&a=2&c=3(有两个相同的参数)
      可以使用下面的视图函数获取:

      1
      2
      3
      4
      5
      6
      def get1(request):
      # 使用request.GET.get获取ac参数的值
      a = request.GET.getlist('a')
      a1,a2 = a[0],a[1]
      c = request.GET.get('c')
      return HttpResponse(a1,a2,c)
  • POST属性:
    使用表单实现POST请求.
    因为表单本身就是一个字典,所有可以使用request.POST.get获取

    1
    2
    3
    4
    5
    6
    7
    8
    def post(request):
    name = request.POST.get('name')
    gender = request.POST.get('gender')
    age = request.POST.get('age')
    # 因为hobby是一个复选框,所以使用getlist
    hobby = request.POST.getlist('hobby')

    return HttpResponse(name,gender,age,hobby)

HttpResponse对象:

  • 作用:给浏览器返回数据.
    HttpRequst对象是由django创建的,HttpResponse对象是由程序员创建.

  • 用法:

    1. 不调用模板:

      1
      2
      3
      4
      from django.http import HttpResponse

      def index(request):
      return HttpResponse('hello world')
    2. 调用模板:使用render方法:

      • 原型:
        render(request,templateName [,context])

      • 作用:
        结合数据和模板,返回完整的HTML页面

      • 参数:
        request:请求体对象
        templateName:模板路径
        context:传递给需要渲染在模板上的数据.

      • 示例:

        1
        2
        3
        def students(request):
        studentslist = Students.objects.all()
        return render(request,'myapp/students.html',{'students':studentslist})
      • 属性:

      • content:返回的内容.(就是响应体)

      • charset:编码格式

      • status_code:响应状态码

      • content-type:
        就是响应头的Content-Type: text/html,
        表示返回的内容的类型

        指定输出的MIME类型

      • 方法:

    3. init:
      使用页面内容实例化HttpResponse对象

    4. write(content):
      以文件的形式写入

    5. flush():
      以文件的形式输出缓冲区

    6. set_cookie(key,value=’’,max_age=None,exprise=None):
      设置cookie

      1
      2
      3
      4
      5
      # 定义一个视图函数,并且为其配置url
      def setcookies(request):
      res = HttpResponse()
      res.set_cookie('cookie_key','cookie_value')
      return res

      当我们访问url就可以得到:
      1555553501652
      1555553522059

      1
      2
      3
      4
      5
      6
      # 获取cookie值,并且展示到页面上
      def cookietest(reqeust):
      res = HttpResponse()
      cookie = request.COOKIES
      res.write('<h1>'+cookie['cookie_key']+'</h1>')
      return res
    7. delete_cookie(key):
      删除cookie,如果删除一个不存在的key,就当什么都没发生.

      • 子类HttpResponseRedirect:
    8. 功能:重定向,服务器端跳转

    9. 使用:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      # 匹配url
      url(r'^before$',views.before),
      url(r'^after$',views.after),

      # 匹配视图
      from django.http import HttpResponseRedirect
      def before(request):
      return HttpResponseRedirect('after')

      def after(request):
      return HttpResponse('this is after HTML')

      当我们输入before时,得到:

      1555554581025

    10. 简写:redirect(to)
      其实就是换了一个函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 匹配url
    url(r'^before$',views.before),
    url(r'^after$',views.after),

    # 匹配视图
    from django.shortcuts import redirect
    def before(request):
    return redirect('after')

    def after(request):
    return HttpResponse('this is after HTML')
    **to推荐使用反向解析**
  • 子类JsonResponse:

    1. 返回Json数据,一般用于异步请求(ajax)

    2. __init__(self.data)

      1
      2
      3
      4
      5
      from django.http import JsonResponse
      def jsonresponse(request):
      if request.is_ajax():
      a = JsonResponse({...})
      return a
    3. __init__(self.data)中的data:字典对象

    4. JsonResponse的Content-type类型为application/json

状态保持:

  • 概述:

    1. http协议时无状态的,每次请求都是一次新的请求,他不记得以前的请求.

    2. 客户端与服务器的一次通信就是一次会话

    3. 实现状态保持,在客户端或者服务端存储有关会话的数据

    4. 存储方式:cookie,session.
      cookie:所有的数据存储在客户端,不要存敏感的数据.
      session:所有数据存储在服务端,在客户端使用cookie存储session_id.

      简单来说,session是一个字典,敏感数据存储在字典的values中,现在我将字典的key作为cookie返回给客户端.
      这样就实现了敏感数据的传输,不会被截获.

    5. 状态保持的目的:
      在一段时间内跟踪请求者的状态,可以实现跨页面访问当前请求者的数据.

      • 启用session:
        在setting.py的installed_apps,MIDDLEWARE选项:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    # 默认启用sessions应用
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp'
    ]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # 默认启用sessions中间件
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    • 使用session:
    1. 启用session后,每个HttpRequest对象都有一个session属性,他其实就是一个类型字典的对象.

    2. get(key,default=None):
      根据键获取session值

    3. clear():
      清除所有的会话

    4. flush()
      删除当前会话并删除会话的cookie

    5. 使用如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      # 配置url
      url(r'^main$',views.main),
      url(r'^login$',views.login),
      url(r'^showmain',views.showmain)

      # 视图函数:
      def main(request):
      username = request.session.get('username','游客')
      return render(request,'myapp/main.html',{'username':username})

      def login(request):
      return render(request,'myapp/login.html')

      def showmain(request):
      username = request.POST.get('username')
      request.session['username'] = username
      return HttpResponseRedirect('main')
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <!-- templates/myapp/main.html  -->
      <html lang='cn'>
      <head>
      <meta charset="UTF-8">
      <title>我的</title>
      </head>

      <body>
      <h1>欢迎:{{username}}</h1>
      <a href="/login">登录</a>

      </body>
      </html>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      <!-- templates/myapp/login.html  -->
      <html lang='cn'>
      <head>
      <meta charset="UTF-8">
      <title>登录</title>
      </head>

      <body>
      <!-- action会跳转到/showmain页面 -->
      <form action="/showmain" method="post">
      <input type="text" name="username">
      <input type="submit" value="登录">
      </form>
      </body>
      </html>

      1555575956733
      1555575968695
      1555575985398

    6. 还可以添加一个退出登录:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      # 匹配url
      url(r'^quit$',views.quit)

      # 退出登录的视图
      from django.contrib.auth import logout
      def quit(request):
      # 清除session,建议使用这种方法
      logout(request)
      # 或者使用:request.session.clear()
      # 或者使用:request.session.flush()
      return HttpResponseRedirect('main')
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      <!-- 添加一个退出登录的按钮 -->
      <!DOCTYPE html>
      <html lang='cn'>
      <head>
      <meta charset="UTF-8">
      <title>我的</title>
      </head>

      <body>
      <h1>欢迎:{{username}}</h1>
      <a href="/login">登录</a>
      <a href="/quit">退出登录</a>

      </body>
      </html>

      1555577659145

      点击退出登录

      1555577668606

    7. 设置过期时间:
      session默认寿命:两个星期.

      修改使用set_expiry(value)函数

      1
      2
      3
      4
      def showmain(request):
      username = reqeust.POST.get('username')
      # 设置10秒寿命
      request.sessionn.set_expiry(10)

      value的值:

      • 整数,表示秒
      • 时间对象
      • 0,表示关闭浏览器是失效
      • None,永不过期
  1. 存储session的位置:

    • 默认存储在数据库中:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      INSTALLED_APPS = [
      # 启用 sessions 应用
      'django.contrib.sessions',
      ]

      MIDDLEWARE = [
      # 启用 Session 中间层
      'django.contrib.sessions.middleware.SessionMiddleware',
      ]
      1
      2
      3
      # /setting.py
      # SESSION_ENGINE的默认值为:
      SESSION_ENGINE = 'django.contrib.sessions.backends.db'
    • 如果要修改session的存储方式.必须修改SESSION_ENGINE
      保存到存缓:

      1
      2
      # 只存储在内容中,速度比存储在数据库快,若丢失了则无法找回.
      SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
    • 保存到存缓和数据库:

      1
      2
      # 优先从内存获取,若读取失败再去数据库中读取
      SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
    • 保存到文件:

      1
      2
      3
      SESSION_ENGINE = 'django.contrib.sessions.backends.file'
      # 可选配置
      SESSION_FILE_PATH = '/monkey/www/'
    • 保存到cookies:

      1
      2
      3
      SESSION_ENGINE = 'jango.contrib.sessions.backends.signed_cookies'
      # 建议配置,阻止 javascript 对会话数据的访问,提高安全性。
      SESSION_COOKIE_HTTPONLY= True
  2. 使用redis存缓session:

    1
    2
    # 先安装依赖库
    pip install django-redis-sessions
    1
    2
    3
    4
    5
    6
    7
    # /setting.py
    SESSION_ENGINE = 'redis_sessions.session'
    SESSION_REDIS_HOST = 'localhost'
    SESSION_REDIS_PORT = 6379
    SESSION_REDIS_DB = 0
    SESSION_REDIS_PASSWORD = ''
    SESSION_REDIS_PREFIX = 'session'

模版

概述

  1. 模板由两部分组成:
    • html代码
    • 逻辑控制代码
  2. 作用:
    快速生成HTML页面
  3. 优点:
    • 模板的设计实现了业务逻辑和现实内容的分离
    • 视图可以使用任何模板
  4. 模板处理:
    • 加载
    • 渲染

定义模板

  1. 变量:

    • 视图传递给模板的数据
    • 语法:
    • 注意:
      如果使用的变量不存在,则插入的是空字符串
    • 在模板中使用点语法:
      • 字典查询
      • 属性或者方法
      • 数字索引
    • 在模板中对象的方法:
      在模板里确实能使用对象的方法,但是不能传递参数.
      (简单来说,就是不能调用有参数的方法)
  2. 标签:

    • 语法:{% tag %}

    • 作用:

      1. 在输出中创建文本
      2. 控制逻辑和循环
    • if

      1. 格式:

        1
        2
        3
        4
        5
        6
        7
        {% if 表达式 %}
        语句
        {% elif 表达式 %}
        语句
        {% else 表达式 %}
        语句
        {% endif %}
      2. 示例:

        1
        2
        3
        {% if num %}
        <h1>hello world</h1>
        {% endif %}
    • for:

      1. 格式:

        1
        2
        3
        {% for 变量 in 列表 %}
        语句
        {% endfor %}
        1
        2
        3
        4
        5
        6
        <!-- 列表为空或者列表为空或者不存在时执行语句2 -->
        {% for 变量 in 列表 %}
        语句1
        {% empty %}
        语句1
        {% endfor %}
        1
        2
        3
        4
        <!-- 这不是语句,而是一个变量 -->
        <!-- 表示当前是第几次循环 -->
        <!-- 一般在for循环体里使用 -->
        {{forloop.counter}}
      2. 示例:

        1
        2
        3
        4
        5
        {% for stu in students %}
        <li>{{forloop.counter}}--{{stu.name}}</li>
        {% empty %}
        <li>目前没有学生</li>
        {% endfor %}
    • comment:

      1. 作用:多行注释

      2. 格式:

        1
        2
        3
        {% comment %}
        注释的内容
        {% endcomment %}
      3. 示例:

        1
        2
        3
        4
        {% comment %}
        <h1>被注释</h1>
        {{stu.name}}
        {% endcomment %}
    • ifequal、ifnotequal:

      1. 判断是否相等,是否不相等

      2. 格式:

        1
        2
        3
        4
        <!-- 值1==值2则执行语句-->
        {% ifequal 值1 值2 %}
        语句
        {% endifequal %}
    • include:

      1. 作用:
        加载模板并以标签内的参数渲染

      2. 格式:

        1
        {% include '模板目录' 参数1 参数2 %}
    • url:

      1. 作用:
        反向解析

      2. 格式:

        1
        {% url 'namespace.name' p1 p2 %}
    • csrf_token:

      1. 作用:
        用于跨站请求伪造保护

      2. 格式:

        1
        {% csrf_token %}
    • block、extends

      1. 作用:
        用于模板的继承
    • autoescape

      1. 作用:
        用于HTML转义
  3. 过滤器:

    • 语法:

      1
      {{var|过滤器}}
    • 作用:
      在变量被显示前修改它,但是并不修改这个变量

    • 示例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      {% comment %}
      {{grade.headteacher}}的值是hyl
      {{grade.headteacher|upper}}的值是HYL
      {% endcomment %}


      <li>
      <!-- 输出hyl -->
      {{grade.headteacher}}
      <!-- 输出HYL -->
      {{grade.headteacher|upper}}
      </li>
    • 过滤器其实就是函数.
      过滤器可以传递参数,参数使用引号引起来.

      join:
      和python的join的功能是一样的

      1. 格式:

        1
        {{var|default:'good'}}
      2. 示例:

        1
        2
        3
        4
        5
        6
        <h1>
        {{the_list|join:'#'}}
        </h1>

        <!-- the_list=['hyl','123','456'] -->
        <!-- 返回:hyl#123#456 -->
    • 如果一个变量没有被提供.或者值为false,空.可以使用默认值.
      default:

      1. 格式:

        1
        {{var|default:'good'}}
      2. 示例

        1
        2
        3
        4
        <h1>
        {{test_num|default:'没有'}}
        </h1>
        <!-- 如果没有test_num这个变量,就会使用默认值'没有' -->
    • 根据给定格式转换日期为字符串:
      date:

      格式:

      1
      {{dateVal|date:'y-m-d'}}
    • HTML转义:
      escape

    • 加减乘除

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <!-- 加减是过滤器 -->

      <!-- 加 -->
      <h1>{{num|add:10}}</h1>
      <!-- 减 -->
      <h1>{{num|add:-10}}</h1>


      <!-- 乘除不是过滤器,是一个标签 -->

      <!-- 乘:num乘以5 -->
      <!-- 实际上是(num/1)*5 -->
      <h1>{% widthratio num 1 5 %}</h1>
      <!-- 除:num除以2 -->
      <!-- 实际上是(num/2)*1 -->
      <h1>{% widthratio num 2 1 %}</h1>
  4. 注释:
    单行注释:

    1
    {# 注释内容 #}

    多行注释:

    1
    2
    3
    4
    {% comment %}
    <h1>被注释</h1>
    {{stu.name}}
    {% endcomment %}

反向解析

  1. 实现功能在index.html页面点击超链接,然后跳转到good.html
    代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # project/urls.py
    urlpatterns = [url(r'^',include('myapp.urls'))]

    # myapp/urls.py
    urlpatterns = [
    url(r'^good/(\d+)$',views.good),
    url(r'^index$',views.index)]

    # myapp/views.py
    def good(request,num):
    return render(request,'myapp/good.html',{'num':num})
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    {# templates/myapp/index.html #}
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>index</title>
    </head>

    <body>
    <a href="/good/188">链接</a>
    </body>
    </html>


    {# templates/myapp/good.html #}
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>good</title>
    </head>

    <body>
    good---{{num}}
    </body>
    </html>

    现在确实能获取网页.

    1555596678103

    1555595962864

    但是,假如某天project/urls.py的根路径改变了

    1
    2
    3
    4
    # project/urls.py
    urlpatterns = [url(r'^',include('myapp.urls'))]
    # 改变为
    urlpatterns = [url(r'^me',include('myapp.urls'))]

    那么全部就都会报废.出现问题的根本原因:
    这里是根据a标签的路径<a href="/good/188">链接</a>得出url为/good/188,然后再根据这个url去寻找视图.这时候urls.py中可能没有这个url,所以也就找不到视图.

    解决方法:反向解析
    我们不再根据url去找视图,而是直接调用想要的视图

  2. 示例,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # project/urls.py
    urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 注意这里不是r'^'而是r'^hyl/'
    # 使用include的namespace参数
    url(r'^hyl/',include('myapp.urls',namespace='myapp'))
    ]

    # myapp/urls.py
    urlpatterns = [
    # 使用url的name参数
    url(r'^good/(\d+)$',views.good,name='good'),
    url(r'^redict/$',views.redict)
    ]

    # 两个视图函数不变
    def good(request,num):
    return render(request,'myapp/good.html',{'num':num})
    def redict(request):
    return render(request,'myapp/redict.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
    {# templates/myapp/good.html #}
    <!DOCTYPE html>
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>good</title>
    </head>

    <body>
    good---{{num}}
    </body>
    </html>

    {# templates/myapp/redict.html #}
    <!DOCTYPE html>
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>redict</title>
    </head>

    <body>
    {# 注意这里就是:直接调用myapp的good对应的视图 #}
    <a href="{% url 'myapp:good' 199 %}">链接</a>
    </body>
    </html>

    结果:
    1555635099413

    使用<a href="{% url 'myapp:good' 199 %}">链接</a>
    直接跳转到127.0.0.:8000/hyl/good/199

    1555635111445
    上一个例子是使用:<a href="/good/188">链接</a>
    这个url为127.0.0.:8000/good/199,
    这是找不到对应的视图的.

  3. 总结:
    使用反向解析只需要修改三个地方:

    1
    2
    3
    4
    5
    6
    # project/urls.py
    url(r'^hyl/',include('myapp.urls',namespace='myapp'))
    # myapp/urls.py
    url(r'^good/(\d+)$',views.good,name='good')
    # templates/good.html
    <a href="{% url 'myapp:good' 199 %}">链接</a>

模板继承

  1. 作用:
    减小页面的内容的重复定义,实现页面的重用

  2. block标签:

    • 在父模板中预留区域,子模板去填充.

    • 语法:

      1
      2
      3
      {%block 标签名 %}
      ...
      {%endblock 标签名 %}
  3. extends标签:

    • 继承模板,需要写在模板文件的第一行

    • 语法:

      1
      {% extends '父模板的路径' %}
  4. 示例:

    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
    {# templates/myapp/base.html #}
    <!DOCTYPE html>
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
    #header{
    width: 100%;
    height: 100px;
    background-color: red;
    }
    #footer{
    width: 100%;
    height: 100px;
    background-color: bule;
    }
    </style>

    </head>

    <body>
    <div id='header'>header</div>
    <div>
    {% block hyl %}

    {% endblock hyl %}
    </div>
    <div id='footer'>footer</div>
    </body>
    </html>
    1
    2
    3
    4
    5
    6
    {# templates/myapp/test.html #}
    {% extends 'myapp/base.html' %}

    {% block hyl %}
    <h1>hello world</h1>
    {% endblock hyl %}

    配置路由,视图.得到结果:
    1555637359104

HTML转义

  1. 问题,现在我们在视图函数中传递html代码作为参数

    1
    2
    def index(request):
    return render(request,'myapp/index.html',{'code':'<h1>heyingliang</h1>'})
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>index</title>
    </head>

    <body>
    {{code}}
    </body>
    </html>

    得到结果
    1555638153092
    Django会将接收到的code当成普通字符串渲染.
    现在想要将接收到的字符串当成HTML代码渲染.

  2. 过滤器:

    • escape:转成字符串

      1
      2
      {# 将传来的code当成字符串渲染 #}
      {{code|escape}}
    • safe

      1
      2
      {# 将传来的code当成代码渲染 #}
      {{code|safe}}
  3. 标签:
    autoescape:上面的过滤器确实可以开启和关闭HTML转义.
    但是不够方便,这时可以使用qutoescape:
    语法:

    1
    2
    3
    4
    {# off/on表示开启/关闭转义 #}
    {%autoescape off%}
    {{code}}
    {%endautoescape%}

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!DOCTYPE html>
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>index</title>
    </head>

    <body>
    {# 开启转义 #}
    {%autoescape off%}
    {{code}}
    {%endautoescape%}
    {# 关闭转义 #}
    {%autoescape on%}
    {{code}}
    {%endautoescape%}
    </body>
    </html>

    1555638949488

CSRF

  1. 概念解释:
    CSRF, Cross Site Request Forgery,跨站点伪造请求。
    某些恶意网站包含链接,表单,按钮,js利用登录用户在浏览器中认证,从而攻击服务器.

举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,而且与你的网站的session 尚未过期,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造

但是我们在使用post发起请求的时候就有可能csrf校验失败错误,此时request请求被丢弃掉。
img

  1. 防止CSRF:

    • 出现上面错误的原因就是Django默认开启了防止CSRF,把正确的post请求都防下来了

      1
      2
      3
      4
      5
      # project/settings.py
      MIDDLEWARE = [
      'django.middleware.csrf.CsrfViewMiddleware',
      ...
      ]

      虽然我们可以通过关闭这个应用来取消,此时将不会进行csrf的校验,但如前面所述,这是一种不安全的行为。而且djano也不推荐使用。

    • 第二种方式:
      使用csrf_toke标签

      1
      {% scrf_token %}

      也就是说是:
      在网页中加入csrf_token的标签就可以通过csrf校验

    • 示例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <form action='/showinfo/' method='post'>
      {# 仅仅是添加了这句 #}
      {% scrf_token %}
      姓名:<input type='text' name='username'/>
      <hr/>
      密码:<input type='password' name='pwd'/>
      <hr/>
      <input type='submit' value='登录'/>
      </form>
    • Django 提供的 CSRF 防护机制:

      1. django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,这样就能避免被 CSRF 攻击。
      2. 在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token,在所有的 POST 表单时,必须包含一个 csrfmiddlewaretoken 字段 (只需要在模板里加一个 tag, django 就会自动帮你生成)
      3. 在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden.
      4. 在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值

验证码

  1. 作用:
    在用户注册,登录页面的时候使用,防止暴力请求.减轻服务器的压力.还是防止CSRF的一种方式.

  2. 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    # myapp/urls.py

    # 验证码图片的url
    url(r'^verifycode/$',views.verifycode),
    # 呈现验证码的网址
    url(r'^show_verifycode/$',views.show_verifycode),
    # 验证验证码的网址
    url(r'^verifycodecheck/$',views.verifycodecheck)
    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
    56
    57
    58
    59
    60
    61
    62
    63
    # mypp/views.py

    # 制作验证码
    def verifycode(request):
    #引入绘图模块
    from PIL import Image, ImageDraw, ImageFont
    #引入随机函数模块
    import random
    #定义变量,用于画面的背景色、宽、高
    bgcolor = (random.randrange(20, 100), random.randrange(
    20, 100), random.randrange(20, 100))
    width = 100
    height = 50
    #创建画面对象
    im = Image.new('RGB', (width, height), bgcolor)
    #创建画笔对象
    draw = ImageDraw.Draw(im)
    #调用画笔的point()函数绘制噪点
    for i in range(0, 100):
    xy = (random.randrange(0, width), random.randrange(0, height))
    fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
    draw.point(xy, fill=fill)
    #定义验证码的备选值
    str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
    #随机选取4个值作为验证码
    rand_str = ''
    for i in range(0, 4):
    rand_str += str[random.randrange(0, len(str))]
    #构造字体对象
    font = ImageFont.truetype(r'C:\Windows\Fonts\AdobeArabic-Bold.otf', 40)
    #构造字体颜色
    fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
    #绘制4个字
    draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
    draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
    draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
    draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
    #释放画笔
    del draw
    #存入session,用于做进一步验证
    request.session['verifycode'] = rand_str
    #内存文件操作
    import io
    buf = io.BytesIO()
    #将图片保存在内存中,文件类型为png
    im.save(buf, 'png')
    #将内存中的图片数据返回给客户端,MIME类型为图片png
    return HttpResponse(buf.getvalue(), 'image/png')

    def show_verifycode(request):
    return render(request,'myapp/verifycode.html')

    def verifycodecheck(request):
    userinput = request.POST.get('verifycode').upper()
    ture_val = request.session['verifycode'].upper()
    if userinput == ture_val:
    return render(request,'myapp/verifycode_success.html')
    else:
    ifsuccess = '失败,请重新输出'
    return render(request,'myapp/verifycode.html',{'ifsuccess':ifsuccess})
    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
    {# verfifycode.html #}
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>verifycode</title>
    </head>

    <body>
    <form method="post" action='/verifycodecheck/'>
    <input type="text" name="verifycode"/>
    <img src="/verifycode/">
    <input type="submit" name="登录">
    <span>{{ifsuccess}}</span>
    </form>
    </body>
    </html>


    {# verifycode_success.html #}
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>verifycode</title>
    </head>

    <body>
    成功
    </body>
    </html>

    效果:
    1555663891643
    1555663902752
    1555663923872

    如果使用重定向的话:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def show_verifycode(request):
    # 在这里获取session的值
    f = request.session.get('flag',True)
    str = ''
    if f == False:
    str = '验证失败,请重新输入'
    # 注意要清除session
    request.session.clear()
    return render(request,'myapp/verifycode.html',{'flag':str})

    def verifycodecheck(request):
    userinput = request.POST.get('verifycode').upper()
    ture_val = request.session['verifycode'].upper()

    if userinput == ture_val:
    return render(request,'myapp/verifycode_success.html')
    else:
    # 在这里给session赋值
    request.session['flag'] = False
    # 重定向
    return HttpResponseRedirect('/show_verifycode/')
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {# verfifycode.html #}
    <html lang='cn'>
    <head>
    <meta charset="UTF-8">
    <title>verifycode</title>
    </head>

    <body>
    <form method="post" action='/verifycodecheck/'>
    <input type="text" name="verifycode"/>
    <img src="/verifycode/">
    <input type="submit" name="登录">
    <span>{{flag}}</span>
    </form>
    </body>
    </html>

    简单来说,如果要使用重定向的话,就类似于scrapy的Meta参数.==Django是使用session来传递参数的.==