精华内容
下载资源
问答
  • webquest网站左右框架式详细指南模板
  • django框架模板

    千次阅读 2019-03-10 21:40:35
    作为Web框架,Django提供了模板,用于编写html代码,还可以嵌入模板代码更快更方便的完成页面开发,再通过在视图中渲染模板,将生成最终的html字符串返回给客户端浏览器。模版致力于表达外观,而不是程序逻辑。模板...

    模板

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

    模板包含两部分:

    • 静态部分,包含html、css、js。

    • 动态部分,就是模板语言。

    Django模板语言,简写DTL,定义在django.template包中。 创建项目后,在"项目名称/settings.py"文件中定义了关于模板的配置。

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR,'tamplates')],
            '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目录。

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

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

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

    为了减少开发人员重复编写加载、渲染的代码,Django提供了简写函数render,用于调用模板。

    1、创建示例项目

    1)创建项目demo4

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

    3)在demo4/settings.pyINSTALLED_APPS项注册应用。

    4)在demo4/settings.pyDATABASES项配置使用MySQL数据库demo3,数据库在昨天已经创建。

    5)在demo4/__init__.py中,导入pymysql,并且调用install_as_MySQLdb()方法

    6)在demo4/settings.py中TEMPLATES项配置模板查找路径。

    7)创建模板目录。

    8)打开demo4/urls.py文件,包含app1url配置。

    9)在app1目录下创建urls.py,配置url

    from django.conf.urls import url
    from .views import *
    urlpatterns=[
        url(r'^$',index),
    ]

    10)打开views.py文件,定义视图index。

    from django.shortcuts import render
    def index(request):
        return render(request,'app1/index.html')

    11)在templates/app1目录下创建文件index.html,代码如下:

    <html>
    <head>
        <title>首页</title>
    </head>
    <body>
    </body>
    </html>

    12)打开app1/models.py文件,定义模型类NewsInfo,结构参照第二部分设计。

    from django.db import models
    ​
    class NewsInfo(models.Model):
        news_title = models.CharField(max_length=20)
        news_content = models.TextField()
        news_date = models.DateField()
        isDelete = models.BooleanField(default=False)

    13)生成迁移、执行迁移

    14)设置管理界面本地化、打开demo4/settings.py文件,找到语言编码、时区的设置项,将内容改为如下:

    LANGUAGE_CODE = 'zh-hans' #使用中国语言
    TIME_ZONE = 'Asia/Shanghai' #使用中国上海时间

    15、创建admin管理员,命令如下

    python manage.py createsuperuser

    16、admin.py中注册模型类,

    from .models import *
    admin.site.register(NewsInfo)

    16、启动服务器,登录admin后台,

    17、添加几条初始数据

     

    2、模板语言

    模板语言包括4种类型,分别是:

    • 变量

    • 标签

    • 过滤器

    • 注释

    接下来逐个介绍4种类型。

    2.1、模板变量

    模板变量的作用是计算并输出,变量名必须由字母、数字、下划线(不能以下划线开头)和点组成。

    语法如下:

    {{变量}}

    当模版引擎遇到点如dict.title,会按照下列顺序解析:

    • 1.字典dict['title']

    • 2.先属性后方法,将dict当作对象,查找属性title,如果没有再查找方法title()

    • 3.如果是格式为dict.0则解析为列表dic[0]

      如果变量不存在则插入空字符串''。

    在模板中调用方法时不能传递参数。

    示例

    1)打开views.py文件,创建视图temp。

    def temp(request):
        a = 'aaaa'
        b = {'b1':'b1111','b2':'b2222'}
        c = [1,2,3]
        news = NewsInfo()
        news.title = 'new的属性'
        return render(request,'app1/temp.html',locals())

    2)打开booktest/urls.py文件,配置url。

    url(r'^temp/$', views.temp),

    3)修改在templates/app1下创建temp.html。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>temp</title>
    </head>
    <body>
    <h2>
    a变量:{{a}}<br>
    字典属性值b.b1:{{b.b1}},<br>
    列表对象的下标c.0的值:{{c.0}}<br>
    对象news的tltle属性:{{news.title}}<br>
    </h2>
    </body>
    </html>

    4)运行服务器,在浏览器中输入如下网址。

    http://127.0.0.1:8000/temp

    5)浏览效果如下图。

    2.1、模板标签

    语法如下:

    {%代码段%}

    for标签语法如下:

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

    if标签语法如下:

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

    比较运算符如下:

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

    ==
    !=
    <
    >
    <=
    >=

    布尔运算符如下:

    and
    or
    not

    点击查看内建标签了解更多标签,还有一些常用的标签会在后续地章节中讲解。

    示例:for的使用

    1)打开views.py文件,创建视图temp2。

    from .models import NewsInfo
    def temp2(request):
        news_list = NewsInfo.object.all()
        return render(request,'app1/temp2.html',locals())

    2)打开app1/urls.py文件,配置url。

        url(r'^temp2$',temp2),

    3)在templates/app1下创建temp2.html。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>标签</title>
    </head>
    <body>
    ​
    <h1>新闻列表如下:</h1>
    <ul>
        {%for new in news_list%}
            <h2><li>{{new.news_title}}</li></h2>
        {%endfor%}
    </ul>
    ​
    </body>
    </html>

    4)运行服务器,在浏览器中输入如下网址。

    http://127.0.0.1:8000/temp2/

    浏览效果如下图:

    示例:if的使用

    1) 更改temp2.html代码,如下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>标签</title>
    </head>
    <body>
    <h1>新闻列表如下:</h1>
    <ul>
        {%for new in new_list%}
            {%if new.id < 2%}
                <h2><li style="background: red">{{new.news_title}}</li></h2>
            {%elif new.id < 4%}
                <h2><li style="background: gold">{{new.news_title}}</li></h2>
            {%else%}
                <h2><li style="background: grey">{{new.news_title}}</li></h2>
            {%endif%}
        {%empty%}
            <h2 style="color: red">没有获取到数据</h2>
        {%endfor%}
    </ul>
    </body>
    </html>

    2)刷新浏览器,效果如下

    2.3、过滤器

     

    语法如下:

    • 使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。

    • 如果过滤器需要参数,则使用冒号:传递参数。

    • 语法:{ { 变量|过滤器 }},例如{ { name|lower }},表示将变量name的值变为小写输出

      变量|过滤器:参数
    • 使用管道符号 (|)来应用过滤器

    • 通过使用过滤器来改变变量的计算结果

    • 可以在if标签中使用过滤器结合运算符

    if list1|length > 1
    • 过滤器能够被“串联”,构成过滤器链

    name|lower|upper
    • 过滤器可以传递参数,参数使用引号包起来

    list|join:", "
    • default:如果一个变量没有被提供,或者值为false或空,则使用默认值,否则使用变量的值

    value|default:"什么也没有"
    • 日期date,用于对日期类型的值进行字符串格式化,常用的格式化字符如下:

    value|date:'Y-m-d'
    • Y表示年,格式为4位,y表示两位的年。

    • m表示月,格式为01,02,12等。

    • d表示日, 格式为01,02等。

    • j表示日,格式为1,2等。

    • H表示时,24进制,h表示12进制的时。

    • i表示分,为0-59。

    • s表示秒,为0-59。

    点击查看详细的过滤器

    示例

    1)打开app1/views.py文件,创建视图temp3。

    def temp3(request):
        #模板过滤器
        new_list = NewsInfo.objects.all()
        return render(request,'app1/temp3.html',locals())

    2)打开app1/urls.py文件,配置url。

        url(r'^temp3/$', temp3),

    3)在templates/booktest下创建temp_filter.html。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>过滤器</title>
    </head>
    <body>
    <h1>新闻数量为:{{new_list|length}}</h1>  //通过过滤器获取新闻总数
    {%for new in new_list%}
        {%if new.id > 3%}
        <h2><li style="color: red">
        {{new.news_title}}
        ---原来的日期格式:{{new.news_date}}</li></h2>
            
        {%else%}
        <h2><li style="color: gold">
        {{new.news_title}}
        ---过滤器格式化的日期:
        {{new.news_date|date:"y-m-j"}}</li></h2>   
        {%endif%}
    {%endfor%}
    ​
    </body>
    </html>

    4)运行服务器,在浏览器中输入如下网址。

    http://127.0.0.1:8000/temp3

    浏览效果如下图:

    2.4、注释

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

    1)单行注释语法如下:

    {#...#}

    注释可以包含任何模版代码,有效的或者无效的都可以。

    {# { % if foo % }bar{ % else % } #}

    2)多行注释使用comment标签,语法如下:

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

    3、模板继承

    模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量。

    典型应用:网站的头部、尾部信息。

    3.1、父模板

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

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

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

    3.2、子模板

    标签extends:继承,写在子模板文件的第一行。

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

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

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

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

    示例

    1)打开app1/views.py文件,创建视图temp4。

    def temp4(request):
        #模板过滤器
        title = '模板继承'
        new_list = NewsInfo.objects.all()
        return render(request,'app1/temp3.html',locals())

    2)打开app1/urls.py文件,配置url。

        url(r'^temp4$',temp4),  

    3)在templates下创建inherit_base.html。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{{title}}</title>
    </head>
    <body>
    <h1>-------------------网页头部-------------------</h1>
    <hr>
    {% block quyu1%}
    <p>这个是区域1,请添加内容</p>
    {%endblock quyu1%}
    <hr>
    {%block quyu2%}
    <p>这个是区域2,请添加内容</p>
    {%endblock quyu2%}
    <hr>
    <h1>-------------------网页尾部---------------------</h1>
    </body>
    </html>

    4)在templates/app1下创建temp4。

    {% extends 'base.html'%}  <!--继承bash.html页面-->
    ​
    {% block quyu2%}      <!--添加预留区域的内容-->-->
    ​
    {%for new in new_list%}
    <h3><li>{{new.news_title}}</li></h3>
    {%endfor%}
    ​
    {%endblock quyu2%}  

    5)运行服务器,在浏览器中输入如下网址。

    http://127.0.0.1:8000/temp4
    

    6)浏览效果如下图。

    4、HTML转义

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

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

    示例

    1)打开app1/views.py文件,创建视图html_escape。

        #模板过滤器
        content = '<h1>新闻列表</h1>'
        return render(request,'app1/temp_html.html',locals())

    2)打开app1/urls.py文件,配置url。

        url(r'^temp_html$',temp_html),

    3)在templates/app1/目录下创建temp_html.html。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>HTML转义</title>
    </head>
    <body>
    content:{{content}}
    </body>
    </html>

    4)运行服务器,在浏览器中输入如下网址。

    http://127.0.0.1:8000/temp_html

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

    浏览效果如下图:

    4.1、关闭转义

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

    {{content|escape}}

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

    {{content|safe}}
    

    1)修改templates/app1/temp_html.html代码如下。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>HTML转义</title>
    </head>
    <body>
    content:{{content}}
    <hr>
    过滤器safe关闭转义content:{{content|safe}}
    </body>
    </html>
    

    刷新浏览器后效果如下图:

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

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

    1)修改templates/booktest/html_escape.html代码如下。

    <html>
    <head>
        <title>转义</title>
    </head>
    <body>
    自动转义:{{content}}
    <hr>
    过滤器safe关闭转义:{{content|safe}}
    <hr>
    标签autoescape关闭转义:
    {%autoescape off%}
    {{content}}
    {%endautoescape%}
    </body>
    </html>

    刷新浏览器后效果如下图:

    4.2、字符串字面值

    对于在模板中硬编码的html字符串,不会转义。

    1)修改templates/app1/temp.html代码如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>HTML转义</title>
    </head>
    <body>
    content:{{content}}
    <hr>
    过滤器safe关闭转义content:{{content|safe}}
    <hr>
    {%autoescape off%}
    标签autoescape关闭转义:{{content}}
    {%endautoescape%}
    <hr>
    模板硬编码:{{data|default:"<h2>新闻1</h2>"}}
    </body>
    </html>

    2)刷新浏览器后效果如下图:

    如果希望出现转义的效果,则需要手动编码转义。

    1)修改templates/app1/temp.html代码如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>HTML转义</title>
    </head>
    <body>
    content:{{content}}
    <hr>
    过滤器safe关闭转义content:{{content|safe}}
    <hr>
    {%autoescape off%}
    标签autoescape关闭转义:{{content}}
    {%endautoescape%}
    <hr>
    模板硬编码不转义:{{data|default:"<h2>新闻1</h2>"}}
    <hr>
    模板硬编码手动转义:{{data|default:"&lt;h2&gt;新闻2&lt;/h2&gt;"}}
    </body>
    </html>

    2)刷新浏览器后效果如下图:

     

    5、CSRF

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

    5.1. csrf攻击

    示例

    攻击过程的操作了解即可,不需要重现。

    首先做一个登录页,让用户输入用户名和密码进行登录,登录成功之后跳转的修改密码页面。在修改密码页面输入新密码,点击确认按钮完成密码修改。

    登录页需要一个模板文件login.html.修改密码页面也需要一个模板文件change_pwd.html.

    显示登录页的视图login,验证登录的视图login_check,显示发帖页的视图change_pwd,处理修改密码的视图change_pwd_action.

    加功能:

    a)只有用户登录之后才可以进行修改密码操作。

    **案例流程图:**
    

    5.2django防止csrf的方式:

    1) 默认打开csrf中间件。

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

    防御原理:

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

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

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

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

    6、反向解析

    先看看原来怎么做

    1) 打开app1/views.py文件,创建视图reverse1、reverse2。

    def reverse1(request):
        content = '这个是页面reverse1'
        return render(request,'app1/reverse1.html',locals())
    ​
    def reverse2(request):
        content = '这个是页面reverse2'
        return render(request,'app1/reverse2.html',locals())

    2)打开app1/urls.py文件,配置url。

        url(r'^reverse1$',reverse1),
        url(r'^reverse2$',reverse2),

    3)在templates/app1/目录下创建reverse1.html和reverse2.html

    reverse1.html 代码如下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>页面1</title>
    </head>
    <body>
    <h1>{{content}}</h1>
    <h3><a href="/reverse2">reverse2页面链接</a></h3>
    </body>
    </html>

    reverse2.html 代码如下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>页面1</title>
    </head>
    <body>
    <h1>{{content}}</h1>
    </body>
    </html>

    4)运行服务器,在浏览器中输入如下网址:

    http://127.0.0.1:8000/reverse1
    

    浏览效果如下图:

    5)点击链接后转向了reverse2,效果如下图:

    6)打开app1/urls.py文件,修改"reverse2"的正则表达式为"rever2"。

    url(r'^reve2$',reverse2),

    7)打开浏览器,后退一下,刷新后再次点击链接,浏览如下图:

    问题就来了:随着功能的增加会出现更多的视图,可能之前配置的正则表达式不够准确,于是就要修改正则表达式,但是正则表达式一旦修改了,之前所有对应的超链接都要修改,真是一件麻烦的事情,而且可能还会漏掉一些超链接忘记修改,有办法让链接根据正则表达式动态生成吗? 答:反向解析

    反向解析应用在两个地方:模板中的超链接,视图中的重定向。

    反向解析实现

    要实现反向解析功能,需要如下步骤:

    1)在demo4/urls.py中为include定义namespace属性。

        url(r'^',include('app1.urls',namespace='app1'))

    2)在app1/urls.py中为url定义name属性,并修改为reverse2

        url(r'^reverse2$',reverse2,name='reverse2'),

    3)在模板中使用url标签做超链接,此处为templates/app1/reverse2.html文件。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>页面1</title>
    </head>
    <body>
    <h1>{{content}}</h1>
    <h3><a href="/reverse2">reverse2页面链接</a></h3>
    <h3><a href="{%url 'app1:reverse2'%}">reverse2页面反向链接</a></h3>
    </body>
    </html>

    4)回到浏览器中,后退,刷新,查看源码如下图,两个链接地址一样。

    5)在app1/urls.py中,将reverse2修改为rev2。

        url(r'^rev2$',reverse2,name='reverse2'),

    6)回到浏览器中,刷新,查看源文件如下图,两个链接地址不一样。

    7)反向解析也可以应用在视图的重定向中。

    创建视图函数 re3

    from django.shortcuts import redirect
    from django.core.urlresolvers import reverse
    ​
    def re3(request):
        return redirect(reverse('app1:reverse2'))

    配置ur

    def re3(request):
        return redirect(reverse('app1:reverse2'))

    访问页面re3,被重定向到reverse3页面

    总结:在定义url时,需要为include定义namespace属性,为url定义name属性,使用时,在模板中使用url标签,在视图中使用reverse函数,根据正则表达式动态生成地址,减轻后期维护成本。

     

     

     

     

     

     

     

    7、验证码案列、

    验证码

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

    8.1、手动实现验证码

    接下来的代码不要求手动写出来,因为这种代码在网上可以搜到很多。

    1)安装包Pillow3.4.1。

    pip install Pillow==3.4.1
    
    

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

    2)在app1/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(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'
        #随机选取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('FreeMono.ttf', 23)
        #构造字体颜色
        fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
        #绘制4个字
        draw.text((0, 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')

    3)打开booktest/urls.py文件,配置url。

        url(r'^verify_code/$', views.verify_code),
    
    

    4)运行服务器,在浏览器中输入如下网址。

    http://127.0.0.1:8000/verify_code/
    
    

    5)浏览效果如下图:

    可以多刷新几次看值会不会变。

    8.2、调用验证码

    1)在booktest/views.py文件中,创建视图verify_show。

    def verify_show(request):
        return render(request,'booktest/verify_show.html')
    
    

    2)打开booktest/urls.py文件,配置url。

        url(r'^verify_show/$', views.verify_show),
    
    

    3)在templates/booktest/目录下创建verify_show.html。

    <html>
    <head>
        <title>验证码</title>
    </head>
    <body>
    <form method="post" action="/verify_yz/">
        {%csrf_token%}
        <input type="text" name="yzm">
        <img id="yzm" src="/verify_code/"/>
        <span id="change">看不清,换一个</span>
        <br>
        <input type="submit" value="提交">
    </form>
    </body>
    </html>
    
    

    4)运行服务器,在浏览器中输入如下网址。

    http://127.0.0.1:8000/verify_show/
    
    

    5)浏览效果如下图:

    验证

    1)在booktest/views.py文件中,创建视图verify_yz。

    def verify_yz(request):
        yzm=request.POST.get('yzm')
        verifycode=request.session['verifycode']
        response=HttpResponse('no')
        if yzm==verifycode:
            response=HttpResponse('ok')
        return response
    ​

    2)打开booktest/urls.py文件,配置url。

        url(r'^verify_yz/$', views.verify_yz),
    
    

    3)回到浏览器后刷新,在文本框中填写验证码,点击提交按钮。

     

     

    展开全文
  • bootstrap html5响应式后台框架模板源码,一款后台模板源码,核心基于HTML5/CSS3,自带一些例子,界面采用了左右两栏式布局,集成了一些可以重新组合的UI组件,以及一些jQuery插件,可广泛应用于Web程序,如网站管理...
  • 简单的SSM框架模板

    2020-02-11 16:51:13
      由于肺炎的影响,自己不能提前返校,因此只能在家里学习了。但是不得不说家真的是温暖的...于是自己搭建了一个简单的SSM模板框架,以便随时自己使用。当然,简单嘛,肯定是十分简单的,但是可以自己扩展嘛。...

      由于肺炎的影响,自己不能提前返校,因此只能在家里学习了。但是不得不说家真的是温暖的港湾,学习效率太低了。。。。。

      自己搭建了一个简单的SSM框架,主要是每次自己使用时都需要去网上找一些配置文件,依赖文件什么的,十分麻烦。虽然IDEA已经极大的简化了这些问题。但是还是挺麻烦的。于是自己搭建了一个简单的SSM模板的框架,以便随时自己使用。当然,简单嘛,肯定是十分简单的,但是可以自己扩展嘛。需要什么,在这个简单的模板的基础上加就行。
    整个项目已经上传至GitHub上了,下载即可使用
    GitHub地址Simple_SSM
      需要学习如何自己搭建,这里推荐一个博客,使用的是IDEA+Maven搭建。十分详细,但是博主是分布的,所以看起来可能有点多和复杂,30000字左右。但是学习嘛,看完博客,在根据自己搭建或者是使用我的这个简单的框架,SSM框架的如何搭建很快就入门了。
    史上最详细的IDEA优雅整合Maven+SSM框架
    我的这个框架的简单介绍。

    一,项目目录结构:

    在这里插入图片描述

    1.1 代码部分文件结构:(java文件夹)

    在这里插入图片描述

    1.2 配置文件的文件结构:

    在这里插入图片描述

    1.3 webapp文件结构

    在这里插入图片描述

    二,项目的配置介绍:

    2.1 简单介绍

    编译器
    IntelliJ IDEA 2018.3.4 x64
    Tomcat:9.0.221
    Mysql:8.0.16

    2.2 特殊点的介绍
    2.2.1数据源

    数据源使用的是阿里的
    在这里插入图片描述

    2.2.2 mybatis和spring整合

    在这里插入图片描述
    这里对mybatis和spring的整合,扫描了对应的包,所以就不需要配置mybatis映射文件了。

    2.2.3 已经处理了乱码的问题

    在这里插入图片描述
    如还有乱码问题,请检查jsp文件是否是UTF-8的编码格式。

    三,运行效果

    由于自己只是简答的搭建了框架,没有写很多代码。只是简单地做了一个演示。
    在这里插入图片描述
    在这里插入图片描述

    四,PS

    4.1

    自己在上传到GitHub上又重新下载试了一次。发现resource文件夹没有其作用,只是一个普通文件夹,导致找不到相关配置文件。
    因此需要自己重新手动将resource变为resource root
    在这里插入图片描述
    鼠标选择resource文件夹,右键。
    在这里插入图片描述
    在这里插入图片描述
    此时idea就会自动为你对应匹配到相应的配置文件。

    展开全文
  • amaze漂亮html框架多页面模板
  • 自己做的基于c#语言的asp.net开发框架。包含frameset、ajax‘javascript 灰常传统的上左右框架
  • Beego框架模板处理

    千次阅读 2018-11-21 01:05:21
    beego 的模板处理引擎采用的是 Go 内置的 html/template 包...beego 中默认的模板目录是 views,用户可以把模板文件放到该目录下,beego 会自动在该目录下的所有模板文件进行解析并缓存,开发模式下每次都会重新解析...

    beego 的模板处理引擎采用的是 Go 内置的 html/template 包进行处理,而且 beego 的模板处理逻辑是采用了缓存编译方式,也就是所有的模板会在 beego 应用启动的时候全部编译然后缓存在 map 里面。

    #模板目录
    beego 中默认的模板目录是 views,用户可以把模板文件放到该目录下,beego 会自动在该目录下的所有模板文件进行解析并缓存,开发模式下每次都会重新解析,不做缓存。当然,用户也可以通过如下的方式改变模板的目录(只能指定一个目录为模板目录):

    beego.ViewsPath = "myviewpath"
    

    #自动渲染
    用户无需手动的调用渲染输出模板,beego 会自动的在调用完相应的 method 方法之后调用 Render 函数,当然如果您的应用是不需要模板输出的,那么可以在配置文件或者在 main.go 中设置关闭自动渲染。

    配置文件配置如下:

    autorender = false
    

    main.go 文件中设置如下:

    beego.AutoRender = false
    

    #模板标签
    Go 语言的默认模板采用了 {{ 和 }} 作为左右标签,但是我们有时候在开发中可能界面是采用了 AngularJS 开发,他的模板也是这个标签,故而引起了冲突。在 beego 中你可以通过配置文件或者直接设置配置变量修改:

    beego.TemplateLeft = "<<<"
    beego.TemplateRight = ">>>"
    

    #模板数据
    模板中的数据是通过在 Controller 中 this.Data 获取的,所以如果你想在模板中获取内容 {{.Content}} ,那么你需要在 Controller 中如下设置:

    this.Data["Content"] = "value"
    

    如何使用各种类型的数据渲染:

    ##结构体

    结构体结构

    type A struct{
        Name string
        Age  int
    }
    

    控制器数据赋值

    this.Data["a"]=&A{Name:"astaxie",Age:25}
    

    模板渲染数据如下:

    the username is {{.a.Name}}
    the age is {{.a.Age}}
    

    ##map

    控制器数据赋值

    mp["name"]="astaxie"
    mp["nickname"] = "haha"
    this.Data["m"]=mp
    

    模板渲染数据如下:

    the username is {{.m.name}}
    the username is {{.m.nickname}}
    

    ##slice

    控制器数据赋值

    ss :=[]string{"a","b","c"}
    this.Data["s"]=ss
    

    模板渲染数据如下:

    {{range $key, $val := .s}}
    {{$key}}
    {{$val}}
    {{end}}
    

    ##模板名称
    beego 采用了 Go 语言内置的模板引擎,所有模板的语法和 Go 的一模一样,至于如何写模板文件,详细的请参考 模板教程。

    用户通过在 Controller 的对应方法中设置相应的模板名称,beego 会自动的在 viewpath 目录下查询该文件并渲染,例如下面的设置,beego 会在 admin 下面找 add.tpl 文件进行渲染:

    this.TplName = "admin/add.tpl"
    

    我们看到上面的模板后缀名是 tpl,beego 默认情况下支持 tpl 和 html 后缀名的模板文件,如果你的后缀名不是这两种,请进行如下设置:

    beego.AddTemplateExt("你文件的后缀名")
    

    当你设置了自动渲染,然后在你的 Controller 中没有设置任何的 TplName,那么 beego 会自动设置你的模板文件如下:

    c.TplName = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
    

    也就是你对应的 Controller 名字+请求方法名.模板后缀,也就是如果你的 Controller 名是 AddController,请求方法是 POST,默认的文件后缀是 tpl,那么就会默认请求 /viewpath/AddController/post.tpl 文件。

    #Layout 设计
    beego 支持 layout 设计,例如你在管理系统中,整个管理界面是固定的,只会变化中间的部分,那么你可以通过如下的设置:

    this.Layout = "admin/layout.html"
    this.TplName = "admin/add.tpl"
    

    在 layout.html 中你必须设置如下的变量:

    {{.LayoutContent}}
    

    beego 就会首先解析 TplName 指定的文件,获取内容赋值给 LayoutContent,然后最后渲染 layout.html 文件。

    目前采用首先把目录下所有的文件进行缓存,所以用户还可以通过类似这样的方式实现 layout:

    {{template "header.html" .}}
    Logic code
    {{template "footer.html" .}}
    

    特别注意后面的.,这是传递当前参数到子模板

    #LayoutSection
    对于一个复杂的 LayoutContent,其中可能包括有javascript脚本、CSS 引用等,根据惯例,通常 css 会放到 Head 元素中,javascript 脚本需要放到 body 元素的末尾,而其它内容则根据需要放在合适的位置。在 Layout 页中仅有一个 LayoutContent 是不够的。所以在 Controller 中增加了一个 LayoutSections属性,可以允许 Layout 页中设置多个 section,然后每个 section 可以分别包含各自的子模板页。

    layout_blog.tpl:

    <!DOCTYPE html>
    <html>
    <head>
        <title>Lin Li</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
        <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap-theme.min.css">
        {{.HtmlHead}}
    </head>
    <body>
    
        <div class="container">
            {{.LayoutContent}}
        </div>
        <div>
            {{.SideBar}}
        </div>
        <script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
        <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
        {{.Scripts}}
    </body>
    </html>
    html_head.tpl:
    
    <style>
         h1 {
            color: red;
         }
    </style>
    

    scripts.tpl:

    <script type="text/javascript">
        $(document).ready(function() {
            // bla bla bla
        });
    </script>
    

    逻辑处理如下所示:

    type BlogsController struct {
        beego.Controller
    }
    
    func (this *BlogsController) Get() {
        this.Layout = "layout_blog.tpl"
        this.TplName = "blogs/index.tpl"
        this.LayoutSections = make(map[string]string)
        this.LayoutSections["HtmlHead"] = "blogs/html_head.tpl"
        this.LayoutSections["Scripts"] = "blogs/scripts.tpl"
        this.LayoutSections["Sidebar"] = ""
    }
    

    #renderform 使用
    定义 struct:

    type User struct {
        Id    int         `form:"-"`
        Name  interface{} `form:"username"`
        Age   int         `form:"age,text,年龄:"`
        Sex   string
        Intro string `form:",textarea"`
    }
    

    StructTag 的定义用的标签用为 form,和 ParseForm 方法 共用一个标签,标签后面有三个可选参数,用 , 分割。第一个参数为表单中类型的 name 的值,如果为空,则以 struct field name 为值。第二个参数为表单组件的类型,如果为空,则为 text。表单组件的标签默认为 struct field name 的值,否则为第三个值。
    如果 form 标签只有一个值,则为表单中类型 name 的值,除了最后一个值可以忽略外,其他位置的必须要有 , 号分割,如:form:",姓名:"
    如果要忽略一个字段,有两种办法,一是:字段名小写开头,二是:form 标签的值设置为 -
    现在的代码版本只能实现固定的格式,用 br 标签实现换行,无法实现 css 和 class 等代码的插入。所以,要实现 form 的高级排版,不能使用 renderform 的方法,而需要手动处理每一个字段。
    controller:

    func (this *AddController) Get() {
        this.Data["Form"] = &User{}
        this.TplName = "index.tpl"
    }
    

    Form 的参数必须是一个 struct 的指针。

    template:

    <form action="" method="post">
    {{.Form | renderform}}
    </form>
    

    上面的代码生成的表单为:

        Name: <input name="username" type="text" value="test"></br>
        年龄:<input name="age" type="text" value="0"></br>
        Sex: <input name="Sex" type="text" value=""></br>
        Intro: <input name="Intro" type="textarea" value="">
    

    学院Go语言视频主页
    https://edu.csdn.net/lecturer/1928

    清华团队带你实战区块链开发
    扫码获取海量视频及源码 QQ群:721929980
    在这里插入图片描述

    展开全文
  • iframe网页上下左右布局模板

    千次下载 热门讨论 2014-03-04 13:31:47
    iframe网页上下左右布局模板,可以修改里面的css代码来更改各个模块的大小,位置。很基本使用的网页布局模板
  • 加入框架设计兴趣小组:http://bbs.tinygroup.org/group-113-1.html Tiny模板引擎的实现方式原来是采用的编译方式,最近发生了一些问题,因此我觉得有必要把编译方式调整为解释方式,为此就开始了此次实现活动。...
    点滴悟透设计思想,Tiny模板引擎优化实录!
    加入框架设计兴趣小组: http://bbs.tinygroup.org/group-113-1.html

     

    Tiny模板引擎的实现方式原来是采用的编译方式,最近发生了一些问题,因此我觉得有必要把编译方式调整为解释方式,为此就开始了此次实现活动。

    编译方式存在的问题

    当时采用编译方式,主要是考虑到编译方式在运行时不必再去遍历语法树,因此就采用了编译方式。但是在实际应用当中,出现了如下问题:

    文件路径冲突的问题

    由于采用的是编译方式,这个时候就存在在一个选择,即:Java源代码落地或不落地的选择。如果Java文件不落地,则在有问题的时候,如果想要进行代码调试(虽然这种场景并不多见),那么就没有源代码可供调试。如果Java代码落地,则会存在一个问题,那就是资源文件在磁盘文件中产生冲突的问题。

    同样的问题对于class文件也存在,如果不落地,那么每次应用重启动的时候,都要重新编译这些文件以产生class文件;如果落地,则也会产生冲突的问题。

    当然,Tiny模板引擎通过增加一个配置项,解决了这个冲突的问题,但是由于增加了一个配置项,从客观上增加了维护人员的工作量,也容易造成当维护人员不了解这里面的道道,忘记设置从而导致在一台服务器中部署多个Tiny应用时多个应用中的模板文件生成的java文件和class文件的冲突,从而导致出现问题。

    PermSize内存占用问题

    采用编译方式的时候,由于每个模板文件都要生成一个类,每个宏也要生成一个类,在宏调用过程中,也要生成一些类。(本来是可以不生成这些类的,但是由于Tiny模板引擎支持了一些非常有用的特性,所以宏调用时时采用编译方式,就要生成一些内嵌类来完成)。这样,就会生成大量的Java类,从工程非常大的时候,就会导致PermSize战胜非常大。尤其是在系统还在调试的时候,模板文件变化的时候,就要重新编译生成新的类,为了避免必须重新启动应用服务器才能生生效,因此采用了自己编写ClassLoader的方式来达到即时刷新的问题,但是由于Java的垃圾回收机制,决定了垃圾不是及时回收的,但是由于每个类都要有一个ClassLoader来支持,以便及时替换,因此这会进一步放大内存的占用。

    加载速度比较长的问题

    由于Tiny模板引擎中提供了宏,而这些宏可以独立存在,因此在应用启动的时候就必须加载所有的宏到内存中,以便查找。所以就导致第一次启动的时候,由于要编译所有的宏文件并加载之,导致启动速度非常慢。在以后的启动的时候,也必须检测模板文件与生成的类是否一致,是否有被修改过,当a项目规模比较大的时候,这个时间也是比较长的。尤其是在开发期,启动时间增加10秒,都会让开发人员感觉到难以忍受。

    访问速度的问题

    采用编译方式的问题,在访问上也有一些问题。

    为了提升应用启动时间,只有宏文件是在启动时预选编译好并加载了的,而模板文件和布局文件则没有这种待遇,这就导致如果在访问的时候,第一次访问的时候,需要编译模板文件为java文件,再把java文件编译为class文件,如果这次访问还用到了布局文件,还import了其它的模板文件,那么悲剧了,第一个访问者可能要多等待几秒钟的时间。同时,为了避免多次编译情况的地生,还要增加同步锁,这样会进一步影响到访问的效率。

    具体还没有测试过ClassLoader太多对性能有多大的影响,但是毛估估是有一定影响的,毕竟要增加查找的层数。干的活多了,干的活慢了也是自然的,人是这样,计算机也是同样的道理。

    采用解释方式带来的好处

    由于采用解释方式,因此不必生成java源文件和class文件,因此也就不存在文件路径冲突的问题;同样也不存在PermSize和众多ClassLoader大量占用内存的问题。

    由于采用解释方式,第一次加载,只定性扫描部分关系的内容即可,因此扫描速度非常快;只有在直接执行的时候,才需要更详细的处理,同时由于不需要进行编译,不需要做同步处理,因此加载速度会比编译方式高许多,尤其是和编译方式的第一次加载时间相比。

    访问速度方面的问题,我原来的感觉来说,感觉编译方式会快一些,毕竟它不用再云遍历语法树,但是实际执行下来,感觉解释方式大致有一倍左右的提升,我分析了一下原因,大致可以认为是如下原因:1.由于Java的优化策略,导致使用频率高的访问会进行深度性能优化,采用解释方式,由于用到的就是那几个函数,因此可以很快满足Java虚拟机的要求,更早的进行深度优化;2.由于解释方式和编译方式相比,可以采用更优化的解决方案,因此遍历语法树的时间由避免做一些事情弥补回来了,因此感受性能反而更高一点点。总之,这次编译改解释,的效果还是明显的,各方面全面让我满意,尤其是前面担心的执行效率方面也有大概50%左右的提升是让我喜出望外的。还有一个意外之喜是通过把编译方式改成解释执行方式,代码规模缩小了近一半,由原来的8000+行,变成4000+行。同时,由于不必要依赖JDT,antlr也只要依赖runtime包即可,还顺便减少了3M的WAR包大小。

    OK,说了这么多,那就说说这次改造过程。

    由于团队去岛国旅游,当时把这个任务交给一个留守同学来完成,但是前后两周的时候,没有提交出我满意的结果,由于看不到后续完成的时间节点,没有办法,只好我老先生亲自动手来完成了,OK开工,相信仔细阅读下面一节内容的同学,会对ANTLR解释引擎的开发有深入了解,甚至拿我的代码照葫芦画瓢,直接就可用。

    解释引擎改造实录

    解释引擎总控类

    解释引擎总控类是解释引擎的核心,由于这个东东是为了Tiny模板引擎定制编写的,因此如果有同学要拿来改造,请照葫芦画瓢即可。由于类不大,我就直接贴源码上来,以便亲们理解和我下面讲解。

     

    public class TemplateInterpreter {
    
        TerminalNodeProcessor[] terminalNodeProcessors = new TerminalNodeProcessor[200];
    
        Map<Class<ParserRuleContext>, ContextProcessor> contextProcessorMap = new HashMap<Class<ParserRuleContext>, ContextProcessor>();
    
        OtherTerminalNodeProcessor otherNodeProcessor = new OtherTerminalNodeProcessor();
    
     
    
     
    
        public void addTerminalNodeProcessor(TerminalNodeProcessor processor) {
    
            terminalNodeProcessors[processor.getType()] = processor;
    
        }
    
     
    
        public void addContextProcessor(ContextProcessor contextProcessor) {
    
            contextProcessorMap.put(contextProcessor.getType(), contextProcessor);
    
        }
    
     
    
        public TinyTemplateParser.TemplateContext parserTemplateTree(String sourceName, String templateString) {
    
            char[] source = templateString.toCharArray();
    
            ANTLRInputStream is = new ANTLRInputStream(source, source.length);
    
            // set source file name, it will be displayed in error report.
    
            is.name = sourceName;
    
            TinyTemplateParser parser = new TinyTemplateParser(new CommonTokenStream(new TinyTemplateLexer(is)));
    
            return parser.template();
    
        }
    
     
    
        public void interpret(TemplateEngineDefault engine, TemplateFromContext templateFromContext, String templateString, String sourceName, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {
    
            interpret(engine, templateFromContext, parserTemplateTree(sourceName, templateString), pageContext, context, writer);
    
            writer.flush();
    
        }
    
     
    
        public void interpret(TemplateEngineDefault engine, TemplateFromContext templateFromContext, TinyTemplateParser.TemplateContext templateParseTree, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {
    
            for (int i = 0; i < templateParseTree.getChildCount(); i++) {
    
                interpretTree(engine, templateFromContext, templateParseTree.getChild(i), pageContext, context, writer);
    
            }
    
        }
    
     
    
        public Object interpretTree(TemplateEngineDefault engine, TemplateFromContext templateFromContext, ParseTree tree, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {
    
            Object returnValue = null;
    
            if (tree instanceof TerminalNode) {
    
                TerminalNode terminalNode = (TerminalNode) tree;
    
                TerminalNodeProcessor processor = terminalNodeProcessors[terminalNode.getSymbol().getType()];
    
                if (processor != null) {
    
                    returnValue = processor.process(terminalNode, context, writer);
    
                } else {
    
                    returnValue = otherNodeProcessor.process(terminalNode, context, writer);
    
                }
    
            } else if (tree instanceof ParserRuleContext) {
    
                ContextProcessor processor = contextProcessorMap.get(tree.getClass());
    
                if (processor != null) {
    
                    returnValue = processor.process(this, templateFromContext, (ParserRuleContext) tree, pageContext, context, engine, writer);
    
                }
    
                if (processor == null || processor != null && processor.processChildren()) {
    
                    for (int i = 0; i < tree.getChildCount(); i++) {
    
                        Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer);
    
                        if (value != null) {
    
                            returnValue = value;
    
                        }
    
                    }
    
                }
    
     
    
            } else {
    
                for (int i = 0; i < tree.getChildCount(); i++) {
    
                    Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer);
    
                    if (returnValue == null && value != null) {
    
                        returnValue = value;
    
                    }
    
                }
    
            }
    
            return returnValue;
    
        }
    
     
    
        public static void write(Writer writer, Object object) throws IOException {
    
            if (object != null) {
    
                writer.write(object.toString());
    
                writer.flush();
    
            }
    
        }
    
    }

     

     
    这个类,所以行数是80行,去掉15行的import和package,也就是65行而已,从类的职能来看,主要完成如下事宜: 
    1. 管理了TerminalNodeProcessor和ParserRuleContext
    2. parserTemplateTree:解析文本内容获取语法树
    3. interpret:解释执行语法树
    4. interpret:遍历所有节点并解释执行之
    5. interpretTree:如果是TerminalNode那么找到合适的TerminalNode执行器去执行,如果找不到,则由OtherTerminalNodeProcessor去处理--实际上就是返回字符串了;如果是ParserRuleContext节点,那么就由对应的执行器去执行,执行完了看看是不是要执行子节点,如果需要,那么就继续执行子节点,否则就返回。如果这两种都不是,那就遍历所有子节点去解释执行了。

    所以逻辑还是比较清晰,最复杂的核心算法也只有30行,不管是什么样层级的同学,看这些代码都没有任何难度了。

    需要交待的一件事情是:为什么ContextProcessor的处理类是用Map保存的,而TerminalNodeProcessor则是用数组?这里主要是为了考虑到TerminalNode都有一个类型,用数据的方式速度更快一些。

    上面说到有两个接口,一个是处理TerminalNodeProcessor,另外一个是处理ContextProcessor的,下面交待一下这两个接口。

    TerminalNodeProcessor

    public interface TerminalNodeProcessor<T extends ParseTree> {
    
        int getType();
    
        Object process(T parseTree, TemplateContext context, Writer writer) throws Exception;
    
    }


     

    getType:用于返回处理器可处理的类型,用于解释引擎检查是不是你的菜
    1. process:真正的处理逻辑实现的地方

    ContextProcessor

    public interface ContextProcessor<T extends ParserRuleContext> {
    
        Class<T> getType();
    
     
    
        boolean processChildren();
    
     
    
        Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, T parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception;
    
     
    
    }


    1. getType:用于返回处理器可处理的类型,用于解释引擎检查是不是你的菜
    2. processChildren:用于告诉引擎,你的儿子们是自己处理好了,还是让解释引擎继续执行。返回true表示让引擎继续处理
    3. process:真正的处理逻辑实现的地方

    至此,整个解析引擎的框架就搭好了,剩下要做的就是去写这些处理器了。

    TerminalNodeProcessor实现类示例

    其实这些实现类真的太简单了,我都不好意思贴出来,为了让大家看明白,贴几个说说意思就好 

    DoubleNodeProcessor
    public class DoubleNodeProcessor implements TerminalNodeProcessor<TerminalNode> {
    
        public int getType() {
    
            return TinyTemplateParser.FLOATING_POINT;
    
        }
    
     
    
        public boolean processChildren() {
    
            return false;
    
        }
    
     
    
        public Object process(TerminalNode terminalNode, TemplateContext context, Writer writer) {
    
            String text=terminalNode.getText();
    
            return Double.parseDouble(text);
    
        }
    
    }


    这货的意思是:如果是Double类型的数据,就把字符串转换成Double值返回。 

    StringDoubleNodeProcessor

    public class StringDoubleNodeProcessor implements TerminalNodeProcessor<TerminalNode> {
    
        public int getType() {
    
            return TinyTemplateParser.STRING_DOUBLE;
    
        }
    
        public boolean processChildren() {
    
            return false;
    
        }
    
        public Object process(TerminalNode terminalNode, TemplateContext context, Writer writer) {
    
            String text=terminalNode.getText();
    
            text=text.replaceAll("\\\\\"","\"");
    
            text=text.replaceAll("[\\\\][\\\\]","\\\\");
    
            return text.substring(1, text.length() - 1);
    
        }
    
    }

    这货的意思是,如果是双引号引住的字符串,那么就把里面的一些转义字符处理掉,然后把外面的双引号也去掉后返回。 

    其它的和这个大同小异,总之非常简单,想看的同学可以自己去看源码,这里就不贴了。

    ContextProcessor类的实现示例

    这里面的处理,说实际的也没有什么复杂的,主要原因是原来在写模板引擎的时候,把运行时的一些东西,进行良好的抽象,因此这里只是个简单的调用而已。这里贴2个稍微复杂的示范一下: 

    ForProcessor

    public class ForProcessor implements ContextProcessor<TinyTemplateParser.For_directiveContext> {
    
     
    
        public Class<TinyTemplateParser.For_directiveContext> getType() {
    
            return TinyTemplateParser.For_directiveContext.class;
    
        }
    
        public boolean processChildren() {
    
            return false;
    
        }
    
        public Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, TinyTemplateParser.For_directiveContext parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception {
    
            String name = parseTree.for_expression().IDENTIFIER().getText();
    
            Object values = interpreter.interpretTree(engine, templateFromContext, parseTree.for_expression().expression(),pageContext, context, writer);
    
            ForIterator forIterator = new ForIterator(values);
    
            context.put("$"+name + "For", forIterator);
    
            boolean hasItem = false;
    
            while (forIterator.hasNext()) {
    
                TemplateContext forContext=new TemplateContextDefault();
    
                forContext.setParent(context);
    
                hasItem = true;
    
                Object value = forIterator.next();
    
                forContext.put(name, value);
    
                try {
    
                    interpreter.interpretTree(engine, templateFromContext, parseTree.block(),pageContext, forContext, writer);
    
                } catch (ForBreakException be) {
    
                    break;
    
                } catch (ForContinueException ce) {
    
                    continue;
    
                }
    
            }
    
            if (!hasItem) {
    
                TinyTemplateParser.Else_directiveContext elseDirectiveContext = parseTree.else_directive();
    
                if (elseDirectiveContext != null) {
    
                    interpreter.interpretTree(engine, templateFromContext, elseDirectiveContext.block(), pageContext,context, writer);
    
                }
    
            }
    
            return null;
    
        }
    
    }

    这里解释一下它的执行逻辑: 

    1. 首先获取循环变量名
    2. 接下来获取要循环的对象
    3. 然后构建一个循环迭代器,并在上下文中放一个循环变量进去
    4. 然后真正执行循环,如果有在循环过程中有break或continue指令,那么就执行之
    5. 如果最后一个循环也没有执行,那么检查 else 指令是否存在,如果存在就执行之

    是不是非常简单?

    MapProcessor

    public class MapProcessor implements ContextProcessor<TinyTemplateParser.Expr_hash_mapContext> {
    
        public Class<TinyTemplateParser.Expr_hash_mapContext> getType() {
    
            return TinyTemplateParser.Expr_hash_mapContext.class;
    
        }
    
        public boolean processChildren() {
    
            return false;
    
        }
    
        public Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, TinyTemplateParser.Expr_hash_mapContext parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception {
    
            List<TinyTemplateParser.ExpressionContext> expressions = parseTree.hash_map_entry_list().expression();
    
            List<TinyTemplateParser.ExpressionContext> expressionContexts = expressions;
    
            Map<String, Object> map = new HashMap<String, Object>();
    
            if (expressions != null) {
    
                for (int i = 0; i < expressions.size(); i += 2) {
    
                    String key = interpreter.interpretTree(engine, templateFromContext, expressions.get(i), pageContext,context, writer).toString();
    
                    Object value = interpreter.interpretTree(engine, templateFromContext, expressions.get(i + 1),pageContext, context, writer);
    
                    map.put(key, value);
    
                }
    
            }
    
            return map;
    
        }
    
    }


     

     

    这个是个构建MAP的处理器,它的执行逻辑是: 

    1. 新建个MAP对象,然后循环往MAP里put数据即可以了。
    2. 最后返回map对象

    我已经拿了最复杂的两个来讲了,其它的就更简单了,因此就不再贴了,关心的同学们可以去看源代码。

    总结

    1. 实际上用Java写个新的语言啥的,没有什么难的,难的是你心头的那种恐惧,毕竟现在的一些开源框架如Antlr等的支持下,做词法分析,语法树构建是非常容易的一件事情,只要规划并定义好语法规则,后面的实现并没有多复杂。
    2. 好的设计会让你受益颇多,Tiny模板引擎由编译换成解释执行,没有什么伤筋动骨的变化,只是用新的方式实现了原有接口而已
    3. 对问题的分析的深入程度决定了你代码编写的复杂程度,上次和一个人讨论时有说过:之所以你写不简单,是因为你考虑得还不够多,分析的还不够细
    4. 至此此次重构完成,正在测试当中,将在近日推出。  


    欢迎访问开源技术社区:http://bbs.tinygroup.org。本例涉及的代码和框架资料,将会在社区分享。《自己动手写框架》成员QQ群:228977971,一起动手,了解开源框架的奥秘!或点击加入QQ群:http://jq.qq.com/?_wv=1027&k=d0myfX

     

    展开全文
  • H+ 后台主题UI框架4.1.0(牛B的收费 的框架) H+是一个完全响应式,基于Bootstrap3.3.6最新版本开发的扁平化主题,她采用了主流的左右两栏式布局,使用了Html5+CSS3等现代技术,她提供了诸多的强大的可以重新组合的UI...
  • 什么是模板: html加模板语法 模板语法变量: 形式:{{variable}} 深度查询:使用句点符号“.” 过滤器:{{variable|filter_name}} 标签 形式:{% tag %},tag包含:if、for、csrf_token、url 自定义变量和...
  • BootStrap前端框架网页模板

    千次阅读 2019-03-20 08:37:40
    --container容器是页面左右两边预留空白的容器--> <!--container-fluid容器是100%宽度的容器--> <!-- Indicators --> <li data-target="#hot-products" data-slide-to="0" class="active"></li> ...
  • ThinkPHP框架使用Smarty模板引擎

    千次阅读 2016-09-23 15:45:03
    ThinkPHP框架使用Smarty模板引擎 原文 http://phpquan.com/lamp/php/thinkphp-smarty/ 主题 ThinkPHP 最近公司使用ThinkPHP框架,所以比较关注,想到之前公司使用的框架用的模板引擎是 Smarty,而且用的还...
  • 例子:(2013年蓝桥杯省赛第三题):第39级台阶 ...先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢? #include <iostream> usin...
  • 随着经验慢慢的有点苗头,开始想要封装一套通用模板框架或者工程,用来满足以后项目的开发需求。在这样思索的过程中,找了不少的开源库,集思广益,博览众长,把不错的框架融合进自己的项目里,一点一滴的孵化项目成...
  • 文章左右翻页,用户体验提供升; 首页轮播样式优化,用户体验提供升; 优化标签列表页,更符合SEO规则; 优化标签显示,色彩化用户体验提供升; 文章页增加复制弹窗提示,防止内容被恶意盗取; 首页滚动公告,用户体验...
  • H+是一个完全响应式,基于Bootstrap3.3.6最新版本开发的扁平化主题,她采用了主流的左右两栏式布局,使用了Html5+CSS3等现代技术,她提供了诸多的强大的可以重新组合的UI组件,并集成了最新的jQuery版本(v2.1.4),...
  • 这些大家根据自己的项目去写,写个500字左右,别太多,别太少。由于本项目的顺利上线涉及到业务的考核,因此,在本项目中,xx管理尤为重要,在本项目中,我作为项目经理特别除了对其余管理领域进行管理外,特别对xx...
  • 模板使用步骤 1.创建模板 2.设置模板查找路径 3.模板接收视图传入的数据 4.模板处理数据 1.创建模板 在应用同级目录下创建模板文件夹templates. 文件夹名称固定写法. 在templates文件夹下, 创建应用同名文件夹. 例,...
  • HTML页面模板左右结构),商业化的标准模板
  • 由于做论坛时要给用户能复制地址,但框架页...只能用模板页,但又要做出框架页的效果。html>head>style type="text/css">...html,body{...}{width:100%;height:100%;margin:0px;padding:0px;overflow:hidden;}#Main{..
  • html框架模板 后台页面,页面分上 中(左右)下三html框架模板 后台页面,页面分上 下(左右)三部做jsp动态网站,相同内容可以用模板、include指令这3种分别在什么情况下各自适用,请有经验的朋友分析一下include 是...
  • 后来我查询 了一下资料,可以运用T4模板来解决这个问题。 1. 下载文件GetSummery.ttinclude,这里我提供了,大家去下载: 下载 2. 把我们下载下来的文件解压,将解压出来的文件放入解决方案中 3. ...
  • 查找最好的模板引擎,发现这个搜索词出来的是beetl,于是就仔细学习了Beetl,试图找寻“最好的”三个字表现在哪里?于是搭建环境,阅读代码,与鄙人所做的TinyTemplate进行了粗略的对比,在征得beetl作者@闲.大赋 的...
  • WeUI框架

    千次阅读 2019-10-23 08:22:35
    WeUI是一套小程序的UI框架,所谓UI框架就是一套界面设计方案,有了组件,我们可以用它来拼接出一个内容丰富的小程序,而有了UI框架,我们就可以让我们的小程序变得更加美观。 体验WeUi小程序 WeUI是微信官方设计团队...
  • 后台模板hplus H+ 后台主题UI框架 H+是一个完全响应式,基于Bootstrap3.3.6最新版本开发的扁平化主题,她采用了主流的左右两栏式布局,使用了Html5+CSS3等现代技术
  • 什么是前端框架与后端框架

    万次阅读 2017-05-22 10:11:40
    什么是框架框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。
  • 后台框架合集

    2019-01-16 11:35:59
    H-UI框架:H-ui.admin只是个前端模板,不带后台功能和数据库,不是完整的后台管理系统,他的诞生只是为后端开发工程师提供一套相对好看一点的后台页面,简少后端工程师拼页面的过程; H+ UI框架:H+是一个完全响应式...
  • asp.net后台管理模板

    热门讨论 2009-04-05 17:28:36
    asp.net后台管理模板 精美漂亮 实用 采用框架结构 分左右两侧

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,037
精华内容 11,214
关键字:

左右框架模板