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):
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
  1. 登录装饰器调用,比如一个页面必须要登录才能操作,否则跳转到登录页
    【app2/views.py】@login_required
# /change_pwd
@login_required #作用:把此页面作为一个参数传到login_required里
def change_pwd(request):
    '''显示修改密码页面'''
    return render(request, 'booktest/change_pwd.html')

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

1) project2/settings.py 注释掉csrf

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

<!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】需要登录才能操作的页面调用登录装饰器

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

    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登录技术)

<!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方网站修改密码具体操作过程如下:

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

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

如果在【虚拟机】或【真正服务器】创建服务:
py manage.py runserver 公网IP:8000
  1. 配置settings.py
DEBUG = False #True
ALLOWED_HOSTS = ['*']
  1. 在局域网另一台电脑访问:192.168.1.4:8000/login 即可访问网站,输入(jim,123)登录成功。
  2. 【创建伪造页】在访问电脑自建一个页面如下:
    【csrf-test.html】
<!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>
  1. 打开第5步的页面,点按键即可修改成功对应网站密码:
    在这里插入图片描述
  2. 跨站伪造post请求修改用户密码成功!
jim修改密码为:789

1.1.1 django防止csrf的方式:

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

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

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 %},否则即使本站页面也会访问失败。

<!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中加:
$.ajaxPost({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
  1. 详细代码:【4】csrf防护
<!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):

<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

<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传给服务器。

 <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
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

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

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

在这里插入图片描述

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

【1】添加验证码,直接把验证码地址写在src内

<!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】进行验证码校验,如果验证码输入不对直接返回登录页面,下面的密码验证就不再进行操作了

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"))

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')),

"""
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')

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函数

# /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>

<!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

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】反向解析+字典传参数(以下两种写法都可)

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 %}"

<!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】不行
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. 记得返回
# 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

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

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

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

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

猜你喜欢

转载自www.cnblogs.com/chenxi188/p/12186537.html