• 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">
    
    1. 服务器同时交给浏览器保存一个名字为csrftoken的cookie信息。
    2. 提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则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切换也没办法

  • 相关阅读:
    「日常训练」More Cowbell(Codeforces Round #334 Div.2 B)
    「日常训练」Battle Over Cities
    「日常训练」湫湫系列故事——设计风景线(HDU-4514)
    「日常训练」Caterpillar(POJ-3310)
    python压缩解压文件
    python图像处理
    python删除文件或者目录
    python循环执行程序的装饰器
    python中logging模块的使用
    python中yield迭代器
  • 原文地址:https://www.cnblogs.com/chenxi188/p/12186537.html
Copyright © 2020-2023  润新知