• Django-C004-模板


    此文章完成度【100%】留着以后忘记的回顾。多写多练多思考,我会努力写出有意思的demo,如果知识点有错误、误导,欢迎大家在评论处写下你的感想或者纠错。

    【Django version】: 2.1

    【pymysql version】:0.9.3

    【python version】: 3.7

    模板


     作为web框架,Django提供了模板,用于编写html代码,还可以嵌入模板代码更快捷、更方便的完成页面开发,在通过在视图中的渲染,将生成最终的HTML字符串返回给客户端,模板致力于表达外观,而不是程序逻辑,模板的设计实现了业务逻辑与显示内容的分离,一个视图可以使用任意的模板,一个模板可以供多个视图使用。

     

    模板包含两个部分:

    • 静态:html、css、js

    • 动态:模版语言

    Django处理模板分为两个阶段:

    • 加载:根据给定的路径找到模板文件,编译后保存在内存中

    • 渲染:使用上下文数据对模板插入值并返回生成的字符串

    为了减少加载和渲染,Django提供了简单函数render用于调用模板

    Django模板语言,定义在django.template,创建完项目后,在项目文件下的settings.py文件中定义关于模板的配置

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]

    DIRS定义一个目录列表,模板引擎按列表顺序搜索这些目录以便找到模板文件,通常是在项目的根目录下创建templates目录。

    按照咱们的习惯,每当新开始的章节,当然是创建一个新的项目,温故而知新。

    1)创建项目test4

    django-admin startproject test4

    2)进入项目目录,创建应用school

    cd test4
    python manage.py startapp school

    3) 在项目的settings.py文件中,添加应用

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'school',  # 添加应用
    ]

    4)在项目的settings.py文件中,更改使用的数据库引擎

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 't_school',
            'USER': 'root',
            'PASSWORD': 'toor',
            'HOST': 'localhost',
            'PORT': 3306,
        }
    }

    5)在项目下的__init__.py文件中导入pymysql

    import pymysql
    pymysql.install_as_MySQLdb()

    6)在项目下的settings.py文件中添加模本文件夹

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR,'templates')],  # 添加模板文件夹路径
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]

    7)创建模板文件夹

    8)配置项目下的urls.py指向我们应用的urls.py, 并且创建应用专属urls.py

    项目下的urls.py

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include("school.urls")),
    ]

    应用下的urls.py

    from django.urls import re_path
    from school import views
    
    urlpatterns = [
        re_path(r"^$", views.index),
    ]

    9)在应用下的views.py,定义index视图

    10)在templates的t_school中创建index.html 模板文件

    11)在school/models.py文件中,定义ClassInfoModel类

    from django.db import models
    
    
    class ClassInfoModel(models.Model):
        """班级信息模型类"""
        name = models.CharField(max_length=20)
        class_date = models.DateField()
        class_size = models.IntegerField(default=0)
        reported_size = models.IntegerField(default=0)
        isDelete = models.BooleanField(default=False)

    12)测试

     

    模板语言


     

    模版语言的四种类型:变量、标签、过滤器、注释

    模版变量

    模板班里那个的作用是计算并输出,变量名必须由字母、数字、下划线(但是不能作为开头)和组成

    {{ 变量 }}

    当模版引擎遇到点如 c.name .这里的c是ClassInfoModel的对象,会按照以下顺序解析

    1. 字典 c['name'] 方式解析

    2. 先属性后方法,c当做对象,查找name属性,如果没有在查找name()方法

    3. 如果个为c.0则解析为c[0]

      如果对象不存在则插入空字符串,在模板调用方式时,不能传递参数

    视图:views.py,创建pass_on

    def pass_on(request):
        po_dict = {'key' : 'po_dict_var'}
        c = ClassInfoModel.objects.get(id=1)
        context = {'po_dict': po_dict, 'c': c}
        return render(request, "t_school/pass_on.html", context)

    URL : urls.py

    urlpatterns = [
        re_path(r"^$", views.index),
        re_path(r"^pass_on$", views.pass_on),
    ]

    模板:templates>t_school>pass_on.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        dict = : {{po_dict.key}}<br>
        ClassInfoModel = : {{c.name}}<br>
    </body>
    </html>

    展示效果

    标签


     

    {% 代码段 %}

    for循环标签

    {%for item in 列表%}
    循环逻辑
    {{forloop.counter}}表示当前是第几次循环,从1开始
    {%empty%}
    列表为空或不存在时执行此逻辑
    {%endfor%}

    if判断标签

    {%if ...%}
    逻辑1
    {%elif ...%}
    逻辑2
    {%else%}
    逻辑3
    {%endif%}

    比较运算符

    ==  # 注意:运算符左右两侧不能紧挨变量或常量,必须有空格。
    !=
    <
    >
    <=
    >=

    布尔运算符

    and
    or
    not

    视图:views.py 创建视图 use_tag

    def use_tag(request):
        context = {'class_list': ClassInfoModel.objects.all()}
        return render(request, 't_school/use_tag.html', context)

    URL: urls.py 

    re_path(r"^use_tag$", views.use_tag),

    模板:templates>t_school>use_tag.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <ul>
            {%for class in class_list %}
                {%if class.id <= 3 %}
                    <li style="color:pink;">{{ class.name }}</li>
                {%else%}
                    <li style="color:#666;">{{ class.name }}</li>
                {%endif%}
            {%empty%}
                error: 没找到班级信息
            {%endfor%}
        </ul>
    </body>
    </html>

    展示效果

    过滤器


     

    语法

    • 使用管道符 | 来应用过滤器,用于进行计算、转换操作、可以使用在变量、标签中
    • 如果过滤器需要参数,则使用冒号:传递参数
    变量|过滤器:参数

    长度length,返回字符串包含字符的个数,或列表、元祖、字典的元素个数

    默认值default,如果变量不存在时则返回默认值

    data|default:'默认值'

    日期date, 用于对日期类型的值进行字符串格式化,常用的格式化标签

    • Y表示年,格式为4位,y表示两位的年。
    • m表示月,格式为01,02,12等。
    • d表示日, 格式为01,02等。
    • j表示日,格式为1,2等。
    • H表示时,24进制,h表示12进制的时。
    • i表示分,为0-59。
    • s表示秒,为0-59。
    alue|date:"Y年m月j日  H时i分s秒"

    视图 : views.py 定义视图use_filter

    def use_filter(request):
        context = {'class_list': ClassInfoModel.objects.all()}
        return render(request, 't_school/use_filter.html', context)

    URL: urls.py 

    re_path(r"^use_filter$", views.use_filter),

    模板:templates>t_school>use_filter.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <ul>
            {%for class in class_list %}
                {%if class.id <= 3 %}
                    <li style="color:pink;">
                        {{ class.name }}--->
                        {{ class.class_date}}    
                    </li>
                {%else%}
                    <li style="color:#666;">
                        {{ class.name }}--->
                        {{ class.class_date|date:"Y-m-j"}}
                    </li>
                {%endif%}
            {%empty%}
                error: 没找到班级信息
            {%endfor%}
        </ul>
    </body>
    </html>

    展示效果

    自定义过滤器


    过滤器就是Python中的函数,注册后就可以在模板中当做过滤器使用,下面以求余为例开发一个自定义过滤器mod

    首先在应用中创建templatetags目录,创建__init__文件和filters.py文件

    去settings里面的INSTALLED_APPS中注册:

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'school',  # 添加应用
        'school.templatetags'  # 作为APP注册
    ]

    在filters.py中编写代码:

    # 导入Library类
    from django.template import Library
    
    # 创建一个Library对象
    register = Library()
    
    # 使用装饰器进行注册
    @register.filter
    def mod(value):  # 定义求余函数,返回布尔值
        return value % 2 == 0

    使用这个自定义的过滤器之前还需要在模板文件中导入,去模板文件中使用自定义的过滤器

    {%load filters%}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <ul>
            {%for class in class_list %}
                {%if class.id|mod %}
                    <li style="color:pink;">
                        {{ class.name }}--->
                        {{ class.class_date}}    
                    </li>
                {%else%}
                    <li style="color:#666;">
                        {{ class.name }}--->
                        {{ class.class_date|date:"Y-m-j"}}
                    </li>
                {%endif%}
            {%empty%}
                error: 没找到班级信息
            {%endfor%}
        </ul>
    </body>
    </html>

    展示效果

    其实自定义的过滤器还是可以指定参数的,接下来把上面的例子改进一下,不让固定只能取余2

    1)首先到filters.py中定义过滤器函数

    @register.filter
    def mod_num(value, num):  # 定义求余函数,返回布尔值
        return value % num == 0

    2)去模板中使用

    {%load filters%}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <ul>
            {%for class in class_list %}
                {%if class.id|mod_num:3 %}
                    <li style="color:pink;">
                        {{ class.name }}--->
                        {{ class.class_date}}    
                    </li>
                {%else%}
                    <li style="color:#666666;">
                        {{ class.name }}--->
                        {{ class.class_date|date:"Y-m-j"}}
                    </li>
                {%endif%}
            {%empty%}
                error: 没找到班级信息
            {%endfor%}
        </ul>
    </body>
    </html>

    效果展示

     

    注释


    在模板中使用如下模板注释,这段代码不会被编译,不会输出到客户端;html注释只能注释html内容,不能注释模板语言 

    1)单行注释

    {#...#}

    2)多行注释

    {%comment%}
    ...
    {%endcomment%}

    模板继承 


     

    模板继承和类的继承含义是一样的,主要是为了提高代码的重用

    典型应用 : 网站头部、尾部

    父模板

    如果发现在多个模板中内容相同,那就应该把这段内容定义在父模板中。

    标签block:用于在父模板中预留区域,留给紫墨般填充差异性的内容,名字不能相同,为了更好的可读性,建议给endblock 标签上写名字,这个名字与对应的block名字相同,父模板中也可以使用上下文中传递过来的来的数据

    {%block 名称%}
    预留区域,可以编写默认内容,也可以没有默认内容
    {%endblock  名称%}

    子模板

    标签extends: 继承写在模版文件的第一行

    {% extends "父模板路径"%}

    子模板不用填充父模板中的所有预留区域,如果子模板没有填充,则使用父模板定义的默认值。

    填充父模板中指定名称的预留区域

    {%block 名称%}
    实际填充内容
    {{block.super}}用于获取父模板中block的内容
    {%endblock 名称%}

    视图 views.py 定义temp_inherit.

    def temp_inherit(request):
        context = {'title': '模板继承', 'class_list': ClassInfoModel.objects.all()}
        return render(request, 't_school/temp_inherit.html', context)

    URL urls.py

      re_path(r"^temp_inherit$", views.temp_inherit),

    模板 创建一个名为temp_inherit_base.html父模板,在创建一个temp_inherit.html子模板

    temp_inherit_base.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{{ title }}</title>
    </head>
    <body>
        <h1>网页头部</h1>
        <hr>
        {%block qu1%}
            这是区域一,默认值
        {%endblock qu1%}
        <hr>
        {%block qu2%}
        {%endblock qu2%}
        <hr>
        <h1>网页尾部</h1>
    </body>
    </html>

    temp_inherit.html

    {%extends 't_school/temp_inherit_base.html'%}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    
        {%block qu1%}
            {{block.super}}
            <br>
            这是区域一,添加的内容
        {%endblock qu1%}    
    
        {%block qu2%}
            <ul>
                {% for class in class_list%}
                <li>
                    {{class.name}}
                </li>
                {%endfor%}
            </ul>
        {%endblock qu2%}    
        
    </body>
    </html>

    展示效果

    HTML转义


     

     模板对上下文传递的字符串进行输出时,会对以下字符自动转义。

    小于号< 转换为 &lt;
    
    大于号> 转换为 &gt;
    
    单引号' 转换为 &#39;
    
    双引号" 转换为 &quot;
    
    与符号& 转换为 &amp;

    视图 views.py 修改index视图的context

    def index(request):
        context = {'h1': '<h1> index ok </h1>'}
        return render(request, "t_school/index.html", context)

    访问127.0.0.1:8000

    转义后标记代码不会被直接解释执行,而是被直接呈现,防止客户端通过嵌入js代码攻击网站.

    过滤器escape可以实现对变量的html转义,默认模板就会转义,一般省略。

    {{数据|escape}}

    关闭转义

    过滤器safe:禁用转义,告诉模板这个变量是安全的,可以解释执行。

    {{数据|safe}}

    模板 修改index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        自动转义 : {{ h1 }}
        <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10>
        关闭转义 : {{h1|safe}}
    </body>
    </html>

     autoescape标签:设置一段代码都禁用转义,接受on、off参数。

    {%autoescape off%}
    ...
    {%endautoescape%}

    模板修改index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    自动转义:{{h1}}
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10>
    过滤器safe关闭转义:{{h1|safe}}
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10>
    标签autoescape关闭转义:
    {%autoescape off%}
    {{h1}}
    {%endautoescape%}
    </body>
    </html>

    字符串字面值

    对于在模板中硬编码的html字符串,不会转义。如果希望出现转义的效果,则需要手动编码转义。

    模板修改index.html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    自动转义:{{h1}}
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10>
    过滤器safe关闭转义:{{h1|safe}}
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10>
    标签autoescape关闭转义:
    {%autoescape off%}
    {{h1}}
    {%endautoescape%}
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10>
    模板硬编码不转义:{{data|default:'<b>hello</b>'}}
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10>
    模板硬编码手动转义:{{data|default:"&lt;b&gt;123&lt;/b&gt;"}}
    </body>
    </html>

     

     

    CSRF


    CSRF(Cross Site Request Forgery)跨站请求伪造。CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发送消息,盗取你的账号,甚至于购买商品,虚拟货币转账,个人隐私泄露以及财产安全。

    CSRF示意图

    如果想防止CSRF,首先是重要的信息传递都采用POST方式而不是GET方式,接下来就说POST请求的攻击方式以及在Django中的避免。

     视图:views.py 创建视图login、login_check、post、post_action

    def login(request):
        return render(request, 't_school/login.html')
    
    def login_check(request):
        username = request.POST.get('username')  # 获取用户名
        password = request.POST.get('password')  # 获取密码
    
        # 校验
        if username == 'circle' and password == '123':
            request.session['username'] = username
            request.session['isLogin'] = True 
            return redirect('/post')
        else:
            return redirect('/login')
    
    def post(request):
        return render(request, 't_school/post.html')
    
    def post_action(request):
        if request.session['isLogin']:
            username = request.session['username']
            title = request.POST.get('title')  # 获取标题
            content = request.POST.get('content')  # 获取内容
            return HttpResponse(username + '发了一篇%s : %s' % (title, content))
        else:
            return HttpResponse('失败')

     URL : urls.py

        re_path(r"^login$", views.login),
        re_path(r"^login_check$", views.login_check),
        re_path(r"^post$", views.post),
        re_path(r"^post_action$", views.post_action),

    模板:在templates/school中创建login.html 和 post.html

    login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <form method="post" action="/login_check">
            username:<input type="text" name="username"><br>
            password:<input type="text" name="password"><br>
            <input type="submit" value="提交">
        </form>
    </body>
    </html>

    post.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>发帖页</title>
    </head>
    <body>
    <form method="post" action="/post_action">
        标题:<input type="text" name="title"/><br/>
        内容:<textarea name="content"></textarea>
        <input type="submit" value="发帖"/>
    </form>
    </body>
    </html>

    运行服务,username : circle password : 123 就可以后续的操作

    login.html

     

    post.html

    post_action

    这些都是正常的操作,但是如果直接输入127.0.0.1:8000/post.html 也是可以的,所以这个就是有问题的,在生产环境中也是不允许的,所以我们需要验证post.html 是否已经登录

    需要修改视图的post函数

    def post(request):
        if request.session.has_key('isLogin'):
            return render(request, 't_school/post.html')
        else:
            return redirect('/login')

    验证效果,但是我们需要清除浏览器中的Cookie否则你的登录状态是登录过的

     

    当你再次访问127.0.0.1:8000/post.html,会直接跳转到login这个页面,问题来了,如果重复的页面都需要使用这个访问限制。难道我们需要在每一个函数中添加一条判断语句吗?当然不用,我们可以用到python中的装饰器

    视图中定义一个装饰器

    # 定义一个登录的装饰器,用来验证是否这个页面需要先登录在访问
    def login_required(view_func):
        def wrapper(request, *args, **kwargs):
            if request.session.has_key('isLogin'):
                return view_func(request, *args, **kwargs)
            else:
                return redirect('/login')
        return wrapper
    @login_required
    def post(request):
        return render(request, 't_school/post.html')
    
    
    @login_required
    def post_action(request):
        if request.session['isLogin']:
            username = request.session['username']
            title = request.POST.get('title')  # 获取标题
            content = request.POST.get('content')  # 获取内容
            return HttpResponse(username + '发了一篇%s : %s' % (title, content))
        else:
            return HttpResponse('失败')

    但是到底什么是csrf,当黑客制作了一个钓鱼网站,当你在没有删除Cookie和你登录状态的时候,你通过这个浏览器访问了他的钓鱼网站B,但是他的站点设置一些对你操作的A网站里面的信息进行修改,他就可以跳过登录,直接操作A网站

    那么咱们自己的网站如果做到防护呢,在Django中有一个中间件就是CSRF

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',  # 防止CSRF的中间件
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]

    这个中间件默认是开启,但是我们自己网站需要用到的post提交也会被阻止,这里就需要在你的表单提交post数据的时候,添加{% csrf_token %}标签

    login.html中添加

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <form method="post" action="/login_check">
            {% csrf_token %}
            username:<input type="text" name="username"><br>
            password:<input type="text" name="password"><br>
            <input type="submit" value="提交">
        </form>
    </body>
    </html>

    post.html中添加

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>发帖页</title>
    </head>
    <body>
    <form method="post" action="/post_action">
        {% csrf_token%}
        标题:<input type="text" name="title"/><br/>
        内容:<textarea name="content"></textarea>
        <input type="submit" value="发帖"/>
    </form>
    </body>
    </html>

    这里,就可以成功完成CSRF防护

    防护原理

    1. 渲染模板文件时在页面生成一个名字叫做csrfmiddlewaretoken的隐藏域。

    2. 服务器交给浏览器保存一个名字为csrftoken的cookie信息。

    3. 提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则csrf验证通过,否则失败。

    说明:当启用中间件并加入标签csrf_token后,会向客户端浏览器中写入一条Cookie信息,这条信息的值与隐藏域input元素的value属性是一致的,提交到服务器后会先由csrf中间件进行验证,如果对比失败则返回403页面,而不会进行后续的处理。

    验证码


     

    在用户注册、登录页面,为了防止暴力请求,可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻业务服务器、数据库服务器的压力。

    实现验证码

    安装pillow

    pip install Pillow

    点击查看PIL模块API,以下代码中用到了Image、ImageDraw、ImageFont对象及方法。

    视图 views.py 创建视图verify_code

    • 提示1:随机生成字符串后存入session中,用于后续判断。
    • 提示2:视图返回mime-type为image/png。
    from PIL import Image, ImageDraw, ImageFont
    from django.utils.six import BytesIO
    
    def verify_code(request):
        #引入随机函数模块
        import random
        #定义变量,用于画面的背景色、宽、高
        bgcolor = (random.randrange(100), random.randrange(
            20, 100), 255)
        width = 100
        height = 35
        #创建画面对象
        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'
        #随机选取4个值作为验证码
        rand_str = ''
        for i in range(0, 4):
            rand_str += str1[random.randrange(0, len(str1))]
        #构造字体对象  windows下的字体路径'C:\Windows\Fonts\arial.ttf' 26是字体大小
        font = ImageFont.truetype('C:\Windows\Fonts\arial.ttf', 26)
        #构造字体颜色
        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
        #存入session,用于做进一步验证
        request.session['verifycode'] = rand_str
        #内存文件操作
        buf = BytesIO()
        #将图片保存在内存中,文件类型为png
        im.save(buf, 'png')
        #将内存中的图片数据返回给客户端,MIME类型为图片png
        return HttpResponse(buf.getvalue(), 'image/png')

    配置URL urls.py

     re_path(r'^verify_code$', views.verify_code),

    访问127.0.0.1:8000/verify_code

    在login页面中调用verify_code

    在templates中找到login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <form method="post" action="/login_check">
            {% csrf_token %}
            username:<input type="text" name="username"><br>
            password:<input type="password" name="password"><br>
            <img src="/verify_code"><input type="text" name="verify_code"><br>
            <input type="submit" value="提交">
        </form>
    </body>
    </html>

    在视图views.py的login_check中添加验证

    def login_check(request):
        username = request.POST.get('username')  # 获取用户名
        password = request.POST.get('password')  # 获取密码
        verifycode_input = request.POST.get('verify_code')  # 获取验证码
        verifycode_session = request.session['verifycode']  # 获取session中存储的验证码
    
        # 校验验证码
        if verifycode_input != verifycode_session:
            return redirect('/login')
    
        # 校验用户名密码是否正确
        if username == 'circle' and password == '123':
            request.session['username'] = username
            request.session['isLogin'] = True 
            return redirect('/post')
        else:
            return redirect('/login')

    访问127.0.0.1:8000/login,如果验证码输入错误会重新返回到login这个页面

    反向解析


    如果在视图,模板中使用硬编码连接(也就说把地址写死),在url配置发生改变时,需要变更的代码会非常多,这样导致我们的代码结构不是很容易维护,使用反向解析可以提高我们代码的扩展性和可维护性。

    第一步:在项目中urls.py中的include添加namespace

    path('', include("school.urls", namespace='school')),

    结果悲剧发生,报错信息如下:

    django.core.exceptions.ImproperlyConfigured:
    Specifying a namespace in include() without providing an app_name is not supported.
    Set the app_name attribute
    in the included module,
    or pass a 2-tuple containing the list of patterns and app_name instead.
    # 翻译过来大概意思
    在include()指定一个 namespace必须提供app_name,在include模块中设置一个app_name属性,
    或者传递一个包含规则和app_name的2个元素的元祖

    修改结果为:

    from django.contrib import admin
    from django.urls import path, include
    
    
    school_app_patterns = ("school.urls",'school')
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        # path('', include("school.urls", namespace='school')),
        path('', include(school_app_patterns, namespace='school')),
    ]

    第二步:在应用下的urls.py中为url定义name属性

    re_path(r"^$", views.index, name='index'),

    视图 views.py 定义reverse_url

    def reverse_url(request):
        return render(request, 't_school/reverse_url.html')

    URL urls.py 添加 url

    re_path(r'^reverse_url$', views.reverse_url),

    Templates 创建reverse_url.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>反向解析</title>
    </head>
    <body>
        普通链接<a href="/">首页</a><br>
        反向链接<a href="{% url 'school:index' %}">首页</a><br>
    </body>
    </html>

    访问127.0.0.1:8000/reverse_url,查看网页源码,会发现反向链接自己动态的生成了url,这里如果我们更改一下urls.py中的正则,对比一下普通和反向的区别

    # re_path(r"^$", views.index, name='index'),
    re_path(r"^index$", views.index, name='index'),

    这里我们可以看出反向的链接自动生成了,更改配置后的路径

    URL的参数

    有些url配置项正则表达式中是有参数的,接下来讲解如何传递参数。

    应用下的urls.py 添加url

    re_path(r'^reverse_url_args/(d+)/(d+)$', views.reverse_url_args, name='reverse_url_args'),

    视图 定义reverse_url_args

    def reverse_url_args(request, a, b):
        return HttpResponse(a + ":" + b)

    Templates 下修改reverse_url.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>反向解析</title>
    </head>
    <body>
        普通链接<a href="/">首页</a><br>
        反向链接<a href="{% url 'school:index' %}">首页</a><br>
    
        普通链接<a href="/reverse_url_args/2/3">/views.reverse_url_args/2/3</a><br>
        反向链接<a href="{% url 'school:reverse_url_args' 2 3 %}">/views.reverse_url_args/2/3</a><br>
    </body>
    </html>
    {% url 'school:reverse_url_args' 2 3 %}
    这里school是namespace中的值
    这里reverse_url_args是URL中name的值
    这里 'school:reverse_url_args'之后加上空格加上第一个参数2,在加上空格加上第二个参数3

    访问127.0.0.1:8000/reverse_url 

    查看源码同样反向链接中的href也是动态生成的

    URL的关键字参数

    首先在urls.py 中添加带有关键字参数的路径

    re_path(
      r'^reverse_url_kwargs/(?P<username>w+)$',
    views.reverse_url_kwargs,
    name='reverse_url_kwargs'),

    视图 定义reverse_url_kwargs 

    def reverse_url_kwargs(request, username):
        return HttpResponse(username)

    Templates中添加刚才的路径a标签,

        普通链接<a href="/reverse_url_kwargs/circle">/views.reverse_url_args/2/3</a><br>
        反向链接<a href="{% url 'school:reverse_url_kwargs' username='circle' %}">/views.reverse_url_args/2/3</a><br>

    访问127.0.0.1:8000/reverse_url

    1.视图里使用反向解析

    视图views.py中定义reverse_redirect

    from django.urls import reverse
    def reverse_redirect(request):
        # 重定向到index
        # 写死路径的方式
        # return redirect('/index')
    
        # 使用反向解析
        url = reverse('school:index')
        return redirect(url)

    应用下的urls.py 中配置reverse_redirect

    re_path(r'^reverse_redirect$', views.reverse_redirect)

    访问127.0.0.1:8000/reverse_redirect,会直接返回index页面

    2.视图里使用反向解析带有位置参数的url

    视图中修改reverse_redirect

    def reverse_redirect(request):
        # 重定向到reverse_url_args
        url = reverse('school:reverse_url_args', args=(2,3))
        return redirect(url)

    访问127.0.0.1:8000/reverse_redirect,会直接返回reverse_url_args

    页面

    3.视图里使用反向解析带有关键字参数的url

    视图中修改reverse_redirect

    from django.urls import reverse
    def reverse_redirect(request):
        # 重定向到reverse_url_kwargs
        url = reverse('school:reverse_url_kwargs', kwargs={'username' : 'circle'})
        return redirect(url)

    访问127.0.0.1:8000/reverse_redirect,会直接返回reverse_url_kwargs

    页面

     

     -The End-

  • 相关阅读:
    UNIX常用shell
    exit函数
    linux消息队列
    互斥量
    RCS版本控制
    linux samba
    UML建模
    linux syslog
    python基础-列表List及内置方法
    仿美团详情页与购物车源码-详情页
  • 原文地址:https://www.cnblogs.com/Hannibal-2018/p/11094368.html
Copyright © 2020-2023  润新知