0%

Django(十六)基于模板的登录案例:登录装饰器、csrf攻击方式及防护、ajax的Post 的csrf开启写法、生成验证码、加验证码登录、反向解析+传参

一、csrf攻击

1.1 csrf攻击(跨站请求伪造)

【csrf攻击即】:通过第3方网站,伪造请求(前提条件是你已经登录正常网站,并保存了session或cookie登录信息且没有退出),第三方网站即可通过你的session或cookie直接修改正常网站的用户名密码。

在这里插入图片描述

  1. 首先做一个登录页,让用户输入用户名和密码进行登录,登录成功之后跳转的修改密码页面。在修改密码页面输入新密码,点击确认按钮完成密码修改。
  2. 登录页需要一个模板文件login.html.修改密码页面也需要一个模板文件change_pwd.html.
  3. 显示登录页的视图login,验证登录的视图login_check,显示发帖页的视图change_pwd,处理修改密码的视图change_pwd_action.
  4. 加功能:
    a)只有用户登录之后才可以进行修改密码操作。
  5. 登录装饰器函数(app2/views.py):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def login_required(view_func): #参数即调用它的函数
    '''登录判断装饰器'''
    def wrapper(request, *view_args, **view_kwargs): # 内部函数,包装一下
    # 判断用户是否登录
    if request.session.has_key('islogin'): #如果登录了,就返回真正页面(调用此装饰器函数的函数)
    # 用户已登录,调用对应的视图
    return view_func(request, *view_args, **view_kwargs)
    else:
    # 否则,用户未登录,则跳转到登录页
    return redirect('/login')
    return wrapper
  6. 登录装饰器调用,比如一个页面必须要登录才能操作,否则跳转到登录页
    【app2/views.py】@login_required
    1
    2
    3
    4
    5
    # /change_pwd
    @login_required #作用:把此页面作为一个参数传到login_required里
    def change_pwd(request):
    '''显示修改密码页面'''
    return render(request, 'booktest/change_pwd.html')

    【csrf攻击改密码实例】(必须登录后才能操作):

    1) project2/settings.py 注释掉csrf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    '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',
    ]

    2)templates/change_pwd.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>修改密码页面</title>
    </head>
    <body>
    <form method="post" action="/change_pwd_action/">
    新密码:<input type="password" name="pwd">
    <input type="submit" value="确认修改">
    </form>
    </body>
    </html>

    3) app2/views.py【登录装饰器】

    【1】登录装饰器:用户已登录,调用对应的视图;用户未登录,跳转到登录页
    【2】需要登录才能操作的页面调用登录装饰器
    【3】需要登录才能操作的页面调用登录装饰器
    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
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    from django.shortcuts import render,redirect
    from django.template import loader,RequestContext
    from django.http import HttpResponse,JsonResponse
    from app2.models import BookInfo

    def login(request):
    '''登录页'''
    # 判断用户是否登录,用户已登录, 直接跳转到图书列表
    if request.session.has_key('islogin'):
    return redirect('/change_pwd/')
    else:
    #如果用户名密码已经在cookie中,则取到它,并做为参数返回给渲染页面
    if 'username' in request.COOKIES:
    #获取cookie中的用户名、密码
    username=request.COOKIES['username']
    #password=request.COOKIES['password']

    else:
    username=''
    password=''
    return render(request,'app2/login.html',{'username':username,'password':password})


    def login_check(request):
    '''登录校验'''
    #1.获取用户名密码
    username=request.POST.get('username')
    password=request.POST.get('password')
    remember=request.POST.get('remember') #接收remeber
    #2.进行校验,并返回json数据
    if username=='jim' and password=='123':
    #return redirect('/books')
    response = JsonResponse({'res':1}) #正确返回1,重写

    #如果remember==on,则把用户名,密码设置cookie到cookie
    if remember=='on':
    #response.set_cookie('username',username,max_age=7*24*3600)
    #response.set_cookie('password',password,max_age=7*24*3600)
    # 记住用户登录状态把用户名设置到session
    request.session['username'] = username
    # 返回应答
    # 如果用户勾选了remember的条件下,设置session,记住用户登录状态
    request.session['islogin'] = True # 只有session中有islogin,就认为用户已登录
    return response #不要忘记返回response
    else:
    #return redirect('/login')
    return JsonResponse({'res':0}) #错误返回0

    #【1】登录装饰器:用户已登录,调用对应的视图;用户未登录,跳转到登录页
    def login_required(view_func):
    '''登录判断装饰器'''
    def wrapper(request, *view_args, **view_kwargs):
    # 判断用户是否登录
    if request.session.has_key('islogin'):
    # 用户已登录,调用对应的视图
    return view_func(request, *view_args, **view_kwargs)
    else:
    # 用户未登录,跳转到登录页
    return redirect('/login')
    return wrapper


    # /change_pwd
    @login_required #【2】需要登录才能操作的页面调用登录装饰器
    def change_pwd(request):
    '''显示修改密码页面'''
    # # 进行用户是否登录的判断
    # if not request.session.has_key('islogin'):
    # # 用户未登录,跳转到登录
    # return redirect('/login')

    return render(request, 'app2/change_pwd.html')


    # /change_pwd_action
    @login_required #【3】需要登录才能操作的页面调用登录装饰器
    def change_pwd_action(request):
    '''模拟修改密码处理'''
    # # 进行用户是否登录的判断
    # if not request.session.has_key('islogin'):
    # # 用户未登录,跳转到登录
    # return redirect('/login')

    # 1.获取新密码
    pwd = request.POST.get('pwd')
    # 获取用户名
    username = request.session.get('username')
    # 2.实际开发的时候: 修改对应数据库中的内容...
    # 3.返回一个应答
    return HttpResponse('%s修改密码为:%s'%(username,pwd))

    4)app2/urls.py

    1
    2
    3
    4
    5
    path('login/',views.login),#登录页
    path('login_check',views.login_check),#登录检测

    path('change_pwd/', views.change_pwd), # 修改密码页面显示
    path('change_pwd_action/', views.change_pwd_action), # 修改密码处理

    5 ) templates/login.html(ajax登录技术)

    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 lang="zh">
    <head>
    <meta charset="UTF-8">
    <!-- 【0】引入jquery -->
    <script src="/static/js/jquery-1.12.4.min.js"></script>
    <title>登录页面</title>
    </head>
    <script>
    // 写ajax处理函数
    $(function () {
    $('#btnLogin').click(function () {
    //1.获取用户名、密码、是否记住用户名
    username=$('#username').val()
    password=$('#password').val()
    remember=$('#remember').val() //【2】是否记住用户名
    //2.发起ajax--post(username,password)请求验证,地址:/login_check
    $.ajax({
    'url':'/login_check',//验证地址
    'type':'post',//请求类型
    'data':{'username':username,'password':password,'remember':remember},//【3】发送数据,加上remember
    'dataType':'json',//希望返回数据类型
    }).success(function(data){
    //成功返回{'res':1},失败{'res':0}
    if(data.res===0){
    $('#msg').show().html('用户名或密码错误请重试!')//登录失败则显示msg,并在里写入信息
    }else{//成功跳转到books页面
    location.href='/change_pwd'
    }
    })

    })
    })
    </script>
    <style>
    /* 信息提示样式 */
    #msg{
    display: none;
    color:red;
    }
    </style>
    <body>
    <!-- 原form删除,input的name变id,方便jquery操作 -->
    <!-- 【4】把views页的login()函数传过来的用户名,密码赋值给对应处 -->
    用户名:<input type="text" id="username" value="{{username}}"><br/>
    密码:<input type="password" id="password" value="{{password}}"><br/>
    <!-- 加入一个信息提示框,用于密码等错误提示 -->
    <div id="msg"></div>
    <!-- 【1】记住用户名,设置cookie用,如果勾选则其value=on -->
    <input type="checkbox" id="remember">记住用户名<br/>
    <!-- 按钮type改button,加一个id方便jquery操作 -->
    <input type="button" id="btnLogin" value="登录">

    </body>
    </html>

    6)效果:http://127.0.0.1:8000/login/

    用户名或密码错误:
    在这里插入图片描述
    不登录直接访问修改密码页面:http://127.0.0.1:8000/change_pwd 跳回登录页
    u/p正确(jim,123)跳转到:http://127.0.0.1:8000/change_pwd
    在这里插入图片描述
    然后提示:jim修改密码为:456

    7)通过第3方网站修改密码具体操作过程如下:

  7. 首先查看访问服务器的局域网IP及公网IP
    1
    2
    3
    4
    5
    ipconfig /all
    找到:
    192.168.1.4

    公网直接百度IP,查看即可
  8. 创建服务,以下操作2选1
    1
    2
    3
    4
    5
    如果在局域网创建服务:
    py manage.py runserver 192.168.1.4:8000

    如果在【虚拟机】或【真正服务器】创建服务:
    py manage.py runserver 公网IP:8000
  9. 配置settings.py
    1
    2
    DEBUG = False #True
    ALLOWED_HOSTS = ['*']
  10. 在局域网另一台电脑访问:192.168.1.4:8000/login 即可访问网站,输入(jim,123)登录成功。
  11. 【创建伪造页】在访问电脑自建一个页面如下:
    【csrf-test.html】
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>修改密码页面</title>
    </head>
    <body>
    <form method="post" action="http://192.168.1.4:8000/change_pwd_action/">
    <input type="hidden" name="pwd" value='789'><!--【1】用了隐藏input,页面只能看到一个按钮,并给新密码定为789-->
    <input type="submit" value="点我有惊喜!">
    </form>
    </body>
    </html>
  12. 打开第5步的页面,点按键即可修改成功对应网站密码:
    在这里插入图片描述
  13. 跨站伪造post请求修改用户密码成功!
    1
    jim修改密码为:789

1.1.1 django防止csrf的方式:

1) 默认打开csrf中间件(project2/settings.py)。

【1】django默认打开csrf防护,它只对post提交有效

1
2
3
4
5
6
7
8
9
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', #【1】django默认打开csrf防护,它只对post提交有效
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

2) 表单post提交数据时加上{ % csrf_token % }标签

(对应模板本例:change_pwd.html)。
【注意】:需要有post页面提交的页面必须加上{ % csrf_token % },否则即使本站页面也会访问失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>修改密码页面</title>
</head>
<body>
<form method="post" action="/change_pwd_action/">
{% csrf_token %}<!--【1】本页面要有post提交,所以加上-->
新密码:<input type="password" name="pwd">
<input type="submit" value="确认修改">
</form>
</body>
</html>

3)templates/app2/login.html是Ajax页,所以不能用普通写法

【参考】:https://code.ziqiangxuetang.com/django/django-csrf.html?bd_source_light=4317393

  1. 在视图中使用 render(而不要使用 render_to_response)
  2. 在ajax中加:
    1
    2
    3
    $.ajaxPost({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
    });
  3. 详细代码:【4】csrf防护
    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
    <!DOCTYPE html>
    <html lang="zh">
    <head>
    <meta charset="UTF-8">
    <!-- 【0】引入jquery -->
    <script src="/static/js/jquery-1.12.4.min.js"></script>
    <title>登录页面</title>
    </head>

    <script>
    // 写ajax处理函数
    $(function () {
    $('#btnLogin').click(function () {
    //1.获取用户名、密码、是否记住用户名
    username=$('#username').val()
    password=$('#password').val()
    remember=$('#remember').val() //【2】是否记住用户名
    //2.发起ajax--post(username,password)请求验证,地址:/login_check
    $.ajax({
    'url':'/login_check',//验证地址
    'type':'post',//请求类型
    'data':{//【3】发送数据,加上remember
    'username':username,
    'password':password,
    'remember':remember,
    csrfmiddlewaretoken: '{{ csrf_token }}',//【4】csrf防护
    },
    'dataType':'json',//希望返回数据类型
    }).success(function(data){
    //成功返回{'res':1},失败{'res':0}
    if(data.res===0){
    $('#msg').show().html('用户名或密码错误请重试!')//登录失败则显示msg,并在里写入信息
    }else{//成功跳转到books页面
    location.href='/change_pwd'
    }
    })

    })
    })
    </script>
    <style>
    /* 信息提示样式 */
    #msg{
    display: none;
    color:red;
    }
    </style>
    <body>
    <!-- 原form删除,input的name变id,方便jquery操作 -->
    <!-- 【4】把views页的login()函数传过来的用户名,密码赋值给对应处 -->
    用户名:<input type="text" id="username" value="{{username}}"><br/>
    密码:<input type="password" id="password" value="{{password}}"><br/>
    <!-- 加入一个信息提示框,用于密码等错误提示 -->
    <div id="msg"></div>
    <!-- 【1】记住用户名,设置cookie用,如果勾选则其value=on -->
    <input type="checkbox" id="remember">记住用户名<br/>
    <!-- 按钮type改button,加一个id方便jquery操作 -->
    <input type="button" id="btnLogin" value="登录">

    </body>
    </html>

再跨站修改(7.6):

1
2
3
4
5
<p style="background-color:rgb(255, 255, 204)">
禁止访问 (403)
CSRF验证失败. 请求被中断.
您看到此消息是由于该站点在提交表单时需要一个CSRF cookie。此项是出于安全考虑,以确保您的浏览器没有被第三方劫持。
If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for “same-origin” requests.</p>

1.1.2 防御原理:

1) 渲染模板文件时在页面生成一个名字叫做csrfmiddlewaretoken的隐藏域。
在网站右键查看源码,可看到这个隐藏域:name=csrfmiddlewaretoken 的input

1
2
3
4
5
<form method="post" action="/change_pwd_action/">
<input type="hidden" name="csrfmiddlewaretoken" value="O6lfnrybooqSP9Je0bZyCOr8qGObvK0jgzV7fMUW259X117OkpAm8OjtCsadu9tk">
新密码:<input type="password" name="pwd">
<input type="submit" value="确认修改">
</form>

把上例的隐藏域的value传给服务器。

1
<input type="hidden" name="csrfmiddlewaretoken" value="O6lfnrybooqSP9Je0bZyCOr8qGObvK0jgzV7fMUW259X117OkpAm8OjtCsadu9tk">

2) 服务器同时交给浏览器保存一个名字为csrftoken的cookie信息。
3) 提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则csrf验证通过,否则失败。

二、验证码

  • 在用户注册、登录页面,为了防止暴力请求(穷举法破解用户名密码、自动注册大量账号等),可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻业务服务器、数据库服务器的压力。
  • 【其它生成验证码参考】:https://blog.csdn.net/ding_312/article/details/82258442

    1)生成验证码函数 app2/views.py

    【1】存入session,用于做进一步验证
    【2】将图片保存在内存中,文件类型为png
    【3】将内存中的图片数据返回给客户端,MIME类型为图片png
    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
    from PIL import Image, ImageDraw, ImageFont
    import io

    # /verify_code
    def verify_code(request):
    # 引入随机函数模块
    import random
    # 定义变量,用于画面的背景色、宽、高 RGB
    bgcolor = (random.randrange(20, 100), random.randrange(20, 100), 255)
    width = 100
    height = 25
    # 创建画面对象
    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)

    # 定义验证码的备选值
    str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0' #qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789
    # 随机选取4个值作为验证码
    rand_str = ''
    for i in range(0, 4):
    rand_str += str1[random.randrange(0, len(str1))]

    # 构造字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont”
    font = ImageFont.truetype('AdobeFanHeitiStd-Bold.otf', 23)
    # 构造字体颜色
    fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
    # 绘制4个字
    draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
    draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
    draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
    draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
    # 释放画笔
    del draw
    # 【1】存入session,用于做进一步验证
    request.session['verifycode'] = rand_str
    # 内存文件操作
    buf = io.BytesIO()
    # 【2】将图片保存在内存中,文件类型为png
    im.save(buf, 'png')
    # 【3】将内存中的图片数据返回给客户端,MIME类型为图片png
    return HttpResponse(buf.getvalue(), 'image/png')

    2)app2/urls.py

    1
    path('verify_code',views.verify_code),#生成验证码

    3)效果:http://127.0.0.1:8000/verify_code

    在这里插入图片描述

    4)在templates/app2/login.html加验证码(这次用表单提交)

    【1】添加验证码,直接把验证码地址写在src内
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    </head>
    <body>
    <form method="post" action="/login_check">
    {% csrf_token %}
    用户名:<input type="text" name="username" value="{{ username }}"><br/>
    密码:<input type="password" name="password"><br/>
    <input type="checkbox" name="remember">记住用户名<br/>
    <!--【1】添加验证码,直接把地址写在src内-->
    <img src="/verify_code"><br/>
    <input type="text" name="vcode">输入验证码<br/>
    <input type="submit" value="登录">
    </form>
    </body>
    </html>

    5)app2/views.py验证函数修改

    【1】获取用户输入验证码
    【2】获取session中保存的验证码(在第1步)
    【3】进行验证码校验,如果验证码输入不对直接返回登录页面,下面的密码验证就不再进行操作了
    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
    def login(request):
    '''登录页'''
    # 判断用户是否登录,用户已登录, 直接跳转到图书列表
    if request.session.has_key('islogin'):
    return redirect('/change_pwd/')
    else:
    #如果用户名密码已经在cookie中,则取到它,并做为参数返回给渲染页面
    if 'username' in request.COOKIES:
    #获取cookie中的用户名、密码
    username=request.COOKIES['username']
    #password=request.COOKIES['password']

    else:
    username=''
    #password=''
    return render(request,'app2/login.html',{'username':username}) #,'password':password


    def login_check(request):
    '''登录校验'''
    #1.获取用户名密码
    username=request.POST.get('username')
    password=request.POST.get('password')
    remember=request.POST.get('remember') #接收remeber

    vcode1 = request.POST.get('vcode') # 【1】获取用户输入验证码
    vcode2 = request.session.get('verifycode') # 【2】获取session中保存的验证码
    # 【3】进行验证码校验,如果验证码输入不对直接返回登录页面,下面的密码验证就不再进行操作了
    if vcode1 != vcode2:
    # 验证码错误
    return redirect('/login')

    #2.进行校验,并返回json数据
    if username=='jim' and password=='123':
    #return redirect('/books')
    response = JsonResponse({'res':1,'msg':'login success!'}) #密码正确,登录成功,返回1

    #如果remember==on,则把用户名,密码设置cookie到cookie
    if remember=='on':
    response.set_cookie('username',username,max_age=7*24*3600)
    #response.set_cookie('password',password,max_age=7*24*3600)
    # 记住用户登录状态把用户名设置到session
    request.session['username'] = username
    # 返回应答
    # 如果用户勾选了remember的条件下,设置session,记住用户登录状态
    request.session['islogin'] = True # 只有session中有islogin,就认为用户已登录
    return response #不要忘记返回response
    else:
    #return redirect('/login')
    return JsonResponse({'res':0,'msg':'login faild'}) #密码错误返回0

    6)效果:http://127.0.0.1:8000/login/

    在这里插入图片描述
  1. 用户名、密码、验证码、都正确返回:1 –登录成功
  2. 有一个错误返回 :0– 登录失败

三、反向解析

  1. 当某一个url配置的地址发生变化时,页面上使用反向解析生成地址的位置不需要发生变化。
  2. 根据url 正则表达式的配置动态的生成url。
  3. 在项目urls中包含具体应用的urls文件时指定namespace;

1)project2/urls.py配置namespace【重点1】

1-1)Django2.0之后写法

【参考】https://blog.csdn.net/weixin_43883625/article/details/100545439
【1】配置namespace,注意写法:re_path(r'^',include(('app2.urls','booktest'),namespace="booktest"))

1
2
3
4
5
6
7
8
from django.contrib import admin
from django.urls import path,include,re_path

urlpatterns = [
path('admin/', admin.site.urls),
# 【1】配置namespace,注意写法:include(('app2.urls','booktest'),namespace="booktest")
re_path(r'^',include(('app2.urls','booktest'),namespace="booktest")),
]

1-2)Django2.0之前写法:project2/urls.py配置namespace

【1】2.0之前写法:url(r'^', include('app2.urls', namespace='booktest')),

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

urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
#【1】2.0之前写法:include('app2.urls', namespace='booktest')
url(r'^', include('app2.urls', namespace='booktest')),
]

2)app2/urls.py配置 【重点2】

【1】 配置name反向解析配置path('index2/', views.index, name='index')

1
2
3
4
5
6
7
8
9
from django.contrib import admin
from django.urls import path,re_path
from . import views

urlpatterns = [
# 【1】 配置name反向解析配置path('index2/', views.index, name='index')
path('index/', views.index, name='index'),
path('url_reverse/', views.url_reverse),
]

3)app2/views.py写url_reverse函数

1
2
3
# /url_reverse
def url_reverse (request):
return render(request,'app2/url_reverse.html')

4)模板引用templates/app2/url_reverse.html【重点3】

【1】反向解析模板写法 <a href="{ % url 'booktest:index' % }">反向解析</a>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>反向解析</title>
</head>
<body>

<a href="/index">写死地址</a>
<br/>

<!--【1】反向解析模板写法-->
<a href="{% url 'booktest:index' %}">反向解析</a>

</body>
</html>

5)效果:http://127.0.0.1:8000/url_reverse/

写死地址 http://127.0.0.1:8000/index
反向解析 http://127.0.0.1:8000/index

  1. 当2步的path('index/', views.index, name='index')path('index2/', views.index, name='index')时。
  2. 写死地址的连接不会变,反向解析会自动变为:http://127.0.0.1:8000/index2

3.2 反向解析+动态传参

1)app2/views.py

1
2
3
4
5
6
7
8
9
10
from django.shortcuts import render,redirect
from django.http import HttpResponse,JsonResponse

# show_args
def show_args(request, a, b):
return HttpResponse(a+':'+b)

# show_kwargs
def show_kwargs(request, c, d):
return HttpResponse(c+':'+d)

2)【重点1:反向解析+传参配置url】app2/urls.py

【1】反向解析+分组传参数
【2】反向解析+字典传参数(以下两种写法都可)

1
2
3
4
5
6
7
8
9
10
11
from django.contrib import admin
from django.urls import path,re_path
from . import views

urlpatterns = [
re_path(r'^show_args/(\d+)/(\d+)', views.show_args,name='show_args'),#【1】反向解析+分组传参数

#【2】反向解析+字典传参数(以下两种写法都可)
#path(r'show_kwargs/<str:c>/<str:d>',views.show_kwargs,name='show_kwargs'),
path(r'show_kwargs/<c>/<d>',views.show_kwargs,name='show_kwargs'),
]

3)【重点2:反向解析+传参+模板写法】templates/app2/url_reverse.html

【1】反向解析+分组传参:href="{ % url 'booktest:show_args' 1 2 % }"
【2】反向解析+字典传参:href="{ % url 'booktest:show_kwargs' c=3 d=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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>反向解析</title>
</head>
<body>

<a href="/index">写死地址</a>
<br/>
<a href="{% url 'booktest:index' %}">反向解析</a>
<hr/>

<br/>
<a href="/show_args/1/2">写死+分组传参:/show_args/1/2</a><br/>

<br/>
<a href="{% url 'booktest:show_args' 1 2 %}">【1】反向解析+分组传参:/show_args/1/2</a><br/>
{% url 'booktest:show_args' 1 2 %}
<hr/>

<br/>
<a href="/show_kwargs/3/4">写死+字典传参:/show_kwargs/3/4</a><br/>
<br/>

<a href="{% url 'booktest:show_kwargs' c=3 d=4 %}">【2】反向解析+字典传参:/show_kwargs/3/4</a><br/>
{% url 'booktest:show_kwargs' c=3 d=4 %}
</body>
</html>

4)效果:http://127.0.0.1:8000/url_reverse/

【1】写死+分组传参:/show_args/1/2
【2】反向解析+分组传参:/show_args/1/2
【3】写死+字典传参:/show_kwargs/3/4
【4】反向解析+字典传参:/show_kwargs/3/4

  • 当2)步的【show_args】变【show_args2】时,【2】【4】都可正确更新链接,【1】【2】不行
    1
    2
    3
    4
    5
    6
    7
    urlpatterns = [
    re_path(r'^show_args2/(\d+)/(\d+)', views.show_args,name='show_args'),#【1】反向解析+分组传参数

    #【2】反向解析+字典传参数(以下两种写法都可)
    #path(r'show_kwargs/<str:c>/<str:d>',views.show_kwargs,name='show_kwargs'),
    path(r'show_kwargs2/<c>/<d>',views.show_kwargs,name='show_kwargs'),
    ]

    3.3 views函数里调用反向解析+传参

    1)app2/views.py【重点1】

  1. redirect('/index')
  2. reverse('booktest:index')
  3. reverse('booktest:show_args', args=(1,2))
  4. reverse('booktest:show_kwargs', kwargs={'c':3, 'd':4})
  5. 记得返回
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # from django.core.urlresolvers import reverse #【0-1】2.0之前导入reverse
    from django.urls import reverse #【0-2】2.0之后导入reverse

    # /test_redirect
    def test_redirect(request):
    # 【0写死】重定向到/index
    # return redirect('/index')

    # 【1反向解析】
    # url = reverse('booktest:index')

    # 【2反向解析-分组传参】重定向到/show_args/1/2
    url = reverse('booktest:show_args', args=(1,2))

    # 【3反向解析-字典(关键字)传参】重定向到/show_kwargs/3/4
    #url = reverse('booktest:show_kwargs', kwargs={'c':3, 'd':4})
    return redirect(url)

    2)app2/urls.py

    1
    path('test_redirect/',views.test_redirect),

    3)其它配置见3.2(重点2)

    效果:把1)步的【0-3】多选1解除注释查看效果

    访问:http://127.0.0.1:8000/test_redirect 会动态反向解析到对应的网址。3.2切换也没办法