阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Tornado 模板

294次阅读
没有评论

共计 9879 个字符,预计需要花费 25 分钟才能阅读完成。

一、模板配置

  • 概述

    使用模板 需要仿照静态资源文件路径设置 向 web.Application 类的构造函数传递一个名为 template_path 的参数 来告诉 tornado 从文件系统的一个特定位置 提供模板文件

  • 配置如下

    BASE_DIR = os.path.dirname(__file__)
    app = tornado.web.Application([(r'/',IndexHandler),
    ], template_path=os.path.join(BASE_DIR,'templates'),
    debug=True,
    autoreload=True,
    )

    我们设置了一个当前应用目录下名为 templates 的子目录作为 template_path 的参数。在 handler 中使用的模板将在此目录中寻找

  • 目录结构

    project/
    static/
    templates/
    common/
    index.html
    manage.py

二、模板渲染

  • render(“模板名称”,**kwargs)

    不传递参数示例

    import tornado.web
    import tornado.ioloop
    import os
    class IndexHandler(tornado.web.RequestHandler):
    def get(self):
    self.render("index.html")
    if __name__ == '__main__':
    BASE_DIR = os.path.dirname(__file__)
    app = tornado.web.Application([(r'/',IndexHandler),
    ],
    template_path=os.path.join(BASE_DIR,'templates'),
    debug=True,
    autoreload=True,
    )
    app.listen(8000)
    tornado.ioloop.IOLoop.current().start()

    传递参数示例

    概述:

    函数向模板中传递的参数 也就是变量 在模板中使用 {{变量名称}} 进行获取值

    处理类代码

    class IndexHandler(tornado.web.RequestHandler):
    def get(self):
    userInfo = {'name':'lucky',
    'age':18,
    'sex':'man',
    'num': 10
    }
    # 传递参数和 flask 一样
    # 一次传递多个值
    self.render("index.html",userInfo = userInfo)

    模板文件代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    </head>
    <body>
    <h2> 首页 </h2>
    <h4> 获取传递的参数 </h4>
    <p>{{userInfo}}</p>
    <p>{{userInfo['name'] }}</p>
    <p>{{userInfo['sex'] }}</p>
    <p>{{userInfo['age'] }}</p>
    <h5> 表达式(也就是可以在变量中进行运算操作)</h5>
    <p>{{userInfo['age'] + userInfo['num'] }}</p>
    <h5> 测试如果使用的变量不存在(也就是没有传递)结果为会报错 </h5>
    <p>{{boy}}</p>
    </body>
    </html>

    注意:

    • 如果在模板中使用了未传递的变量 则报错
    • 视图传递给模板的数据
    • 要遵守标识符规则
    • 语法:{{var}}

三、标签

  • 语法: {% tag %}

  • 说明

    可以在 Tornado 模板中 使用 Python 条件和循环语句 标签使用 {% 标签名称 %} 来使用

  • 作用

    在输出中创建文本

    控制逻辑和循环

  • if 标签

    格式

    {% if ...%}
    ...
    {% elif ... %}
    ...
    {% else %}
    ...
    {% end %}

    示例

    {% if grade >= 90 %}
    成绩优秀
    {% elif grade >= 80 %}
    成绩良好
    {% else %}
    成绩不太理想
    {% end %}
  • for 标签

    格式

    {% for xx in xx %}
    循环体
    {% end %}

    示例

    视图函数代码为:

    class IndexHandler(tornado.web.RequestHandler):
    def get(self):
    userInfo = {'name':'lucky',
    'age':18,
    'sex':'man',
    'num':10,
    }
    # 传递参数和 flask 一样
    # 一次传递多个值
    self.render("index.html",userInfo = userInfo)
    class TagHandler(tornado.web.RequestHandler):
    def get(self):
    userInfo = {'name': 'lucky',
    'age': 18,
    'sex': 'man',
    'num': 10,
    }
    self.render('test_tag.html',grade=70,userInfo=userInfo)

    模板代码

    <ol>
    {% for k,v in userInfo.items() %}
    <li>{{k}}----{{v}}</li>
    {% end %}
    </ol>
  • 转义

说明:tornado 默认开启了模板自动转义功能 防止网站收到恶意攻击

  • 示例

    
    

def get(self):
self.render(“index.html”,html=“Hello Lucky”)
“`

 模板渲染结果为:`&lt;b&gt;Hello Lucky&lt;/b&gt;`
  • 关闭转义的方式

    第一种 raw 用来输出不被转义的原始格式

    示例

    
    

{% raw html %}
“`

第二种 设置关闭自动转义功能
在 python 配置中
```python

app = tornado.web.Application([],
autoescape=None,
)
“`

第三种 模板中
```phthon

{% autoescape None %}
“`

  • 转义变量

    
    

{{escape( 需要转义的变量) }}
“`

  • 模板导入 include

    • 概述

      可以将指定的 HTML 文件 在模板中的某个位置进行导入使用 实现模板代码的复用 以及减轻后期代码的维护

    • 格式

      {% include "路径 / 模板名称.html" %}
      
    • 示例

      目录结构

      project/
      templates/
      common/
      header.html
      footer.html
      index.html
      manage.py

      index.html 代码如下

      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>Title</title>
      </head>
      <body>
      {% include 'common/header.html' %}
      <h2> 首页 </h2>
      {% include 'common/footer.html' %}
      </body>
      </html>

      header.html

      <header>
      <h4> 头部分 </h4>
      </header>

      footer.html

      <footer>
      <h4> 尾部分 </h4>
      </footer>
    • 注意事项

      我们在使用 include 的时候 会将到入文件内的所有代码都拿过来 所以注意被导入的文件中不要有其它代码的存在 否则都会被导入过来

  • 模板继承

    标签:

    • extends 继承

      格式

      {% extends '父模板.html' %}
      
    • block 使用块填充

      格式:

      {% block '名称' %}
      ...
      {% end %}
    • 示例

      目录结构

      project/
      templates/
      common/
      base.html
      index.html

      base.html 代码如下

      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>{% block title %} 标题 {% end %}</title>
      </head>
      <body>
      {% block header %}
      <h2> 页面头部分 </h2>
      {% end %}
      {% block content %}
      <h2> 页面内容部分 </h2>
      {% end %}
      </body>
      </html>

      index.html

      {% extends 'common/base.html' %}
      {% block title %}
      首页
      {% end %}
      {% block header %}
      <h4> 首页 </h4>
      {% end %}

      使用 bootstrap 创建 base 模板

      base.html

      <!DOCTYPE html>
      <html lang="zh-CN">
      <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>{% block title %} 标题 {% end %}</title>
      <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
      {% block styles %}
      {% end %}
      </head>
      <body>
      {% block header %}
      <nav class="navbar navbar-inverse" style="border-radius: 0px;">
      <div class="container-fluid">
      <!-- Brand and toggle get grouped for better mobile display -->
      <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
      data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
      <span class="sr-only">Toggle navigation</span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">Brand</a>
      </div>
      <!-- Collect the nav links, forms, and other content for toggling -->
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
      <li class="active"><a href="#"> 首页 <span class="sr-only">(current)</span></a></li>
      <li><a href="#"> 发表博客 </a></li>
      </ul>
      <ul class="nav navbar-nav navbar-right">
      <form class="navbar-form navbar-left">
      <div class="form-group">
      <input type="text" class="form-control" placeholder="Search">
      </div>
      <button type="submit" class="btn btn-default">Search</button>
      </form>
      <li><a href="#"> 登录 </a></li>
      <li><a href="#"> 注册 </a></li>
      <li class="dropdown">
      <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
      aria-expanded="false"> 个人中心 <span class="caret"></span></a>
      <ul class="dropdown-menu">
      <li><a href="#"> 个人资料 </a></li>
      <li><a href="#"> 头像修改 </a></li>
      <li><a href="#"> 我的博客 </a></li>
      <li role="separator" class="divider"></li>
      <li><a href="#"> 退出登录 </a></li>
      </ul>
      </li>
      </ul>
      </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->
      </nav>
      {% end %}
      <div class="container">
      {% block content %}
      <h1> 你好,Lucky!</h1>
      {% end %}
      </div>
      {% block scripts %}
      <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
      <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
      <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。-->
      <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
      {% end %}
      </body>
      </html>
    • 注意

      在子模板中 如果想对父母版中的哪个 block 的位置进行更改 那么就使用 block 名称 进行替换 当替换以后 内容为你替换后的内容 若是没有进行替换 则内容为原内容

四、自定义函数

  • 说明

    在模板中还可以使用一个自己编写的函数 只需要将函数名作为模板的参数传递即可 就像其它变量一样

  • 示例

    处理类中

    # 自定义函数 在模板中进行使用
    def my_join(con):
    return '+'.join(con)
    class IndexHandler(tornado.web.RequestHandler):
    def get(self):
    self.render("index.html",my_join=my_join)

    模板中

    <h4> 传递函数在模板中使用 </h4>
    {{my_join('lucky') }}

五、静态文件

说明:我们的图片、样式、js 效果 统称为我们的静态资源文件 需要配置静态资源目录 static 进行使用

  • 目录结构

    project/
    static/
    img/
    css/
    js/
    upload/
    manage.py
  • static_path

    • 说明:我们可以通过向 web.Application 类的构造函数传递一个名为 static_path 的参数来告诉 Tornado 从文件系统的一个特定位置提供静态文件
  • 配置如下

    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    app = tornado.web.Application([(r'/', IndexHandler)],
    static_path=os.path.join(BASE_DIR, "static"),
    )

    static_path 配置了静态资源访问的目录

    • 代码示例

      import tornado.ioloop
      import tornado.httpserver
      from tornado.web import RequestHandler, Application
      import os
      class IndexHandler(RequestHandler):
      def get(self):
      self.render('index.html')
      if __name__ == "__main__":
      BASE_DIR = os.path.dirname(os.path.abspath(__file__))
      app = Application([(r"/", IndexHandler),
      ],
      static_path=os.path.join(BASE_DIR, "static"),
      template_path=os.path.join(BASE_DIR, "templates"),
      debug = True,
      )
      http_server = tornado.httpserver.HTTPServer(app)
      http_server.listen(8000)
      tornado.ioloop.IOLoop.current().start()

      index.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <link rel="stylesheet" href="/static/css/test.css">
      <script src="/static/js/test.js"></script>
      </head>
      <body>
      <img src="/static/img/test.jpg" alt="">
      </body>
      </html>

      注意:对于静态文件目录的命名,为了便于部署,建议使用 static

    • 模板中动态构造

      格式

      {{static_url('plugins/bootstrap/css/bootstrap.min.css')}}
      

      示例

      <img src="{{static_url('img/a.jpg')}}" alt="">
      

      解析后

      <img src="/static/img/test.jpg?v=11c671ed35a4611f3576ab4cbe5ee59c" alt="">
      

      优点

      • static_url 函数创建了一个基于文件内容的 hash 值 并将其添加到 URL 末尾 (查询字符串的参数 v) 这个 hash 值确保浏览器总是加载一个文件的最新版 而不是之前的缓存版本 无论是在应用到开发阶段 还是在部署环境中 都非常有用 因为用户不必在为了看到最新的页面内容而清除浏览器缓存了
      • 另外一个好处可以动态改变应用 URL 的结构 而不需要改变模板中的代码 需要配置 static_url_prefix 来进行更改 如果你使用它进行了更改 那么模板中不需要改变

      前缀示例

      BASE_DIR = os.path.dirname(__file__)
      app = tornado.web.Application([(r'^/$',IndexHandler),
      ],
      template_path=os.path.join(BASE_DIR,'templates'),
      static_path=os.path.join(BASE_DIR,'static'),
      debug=True,
      static_url_prefix='/lucky/',
      )
    • 直接通过 url 进行访问

      http://127.0.0.1:8000/static/img/test.jpg

  • StaticFileHandler

    • 说明

      • 我们再看刚刚访问页面时使用的路径 http://127.0.0.1:8000/static/img/test.jpg,这中 url 显然对用户是不友好的,访问很不方便。我们可以通过 tornado.web.StaticFileHandler 来自由映射静态资源文件与其访问的路径 url
      • urltornado.web.StaticFileHandler 是 tornado 预置的用来提供静态资源文件的 handler
    • 示例

      import os
      from tornado.web import StaticFileHandler
      BASE_DIR = os.path.dirname(os.path.abspath(__file__))
      app = Application(
      [(r'^/(.*?)$', StaticFileHandler, {"path":os.path.join(BASE_DIR, "static/img"), "default_filename":"test.jpg"}),
      (r'^/view/(.*)$', StaticFileHandler, {"path":os.path.join(BASE_DIR, "static/img")}),
      ],
      static_path=os.path.join(BASE_DIR, "static"),
      template_path=os.path.join(BASE_DIR, 'templates'),
      )

      模板中

      <img src="/static/img/a.jpg" alt="">
      <img src="/view/a.jpg" alt="">
      <img src="/" alt="">
      <img src="/b.jpg" alt="">
    • 参数说明

      path 用来指明提供静态文件的根路径,并在此目录中寻找在路由中用正则表达式提取的文件名

      default_filename 用来指定访问路由中未指明文件名时,默认提供的文件

现在,对于静态文件 statics/img/test.jpg,可以通过三种方式进行访问:

  • http://127.0.0.1:8000/static/img/test.jpg
  • http://127.0.0.1:8000/
  • http://127.0.0.1:8000/test.jpg
  • http://127.0.0.1:8000/view/test.jpg

六、文件上传

文件上传注意事项

  • 表单的 enctype 注意修改在进行文件上传的时候
  • 文件上传的文本域 需要存在 name 属性值
  • 提交方式为 post

示例

import tornado.web
import tornado.ioloop
import os
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
# 处理文件上传内的 Handler
class UploadHandler(tornado.web.RequestHandler):
def get(self):
self.render('upload.html')
def post(self):
# 获取上传过来的文件
files = self.request.files
if files:
# 取出包含当前上传文件数据的列表
img = files.get('img')
# 获取上传文件名称 (获取到文件名称 是不是可以自己通过文件名称判断该类型文件是否允许上传 以及生成新的文件唯一名称)
filename = img[0]['filename']
img_file = img[0]['body']
# 文件的存储操作
# 拼接文件上传存储路径
path = os.path.join('static/upload',filename)
file = open(path,'wb')
file.write(img_file)
file.close()
# 上传成功还可以对文件进行大小缩放 以适应其他场合的使用
self.write('文件上传成功')
self.write('来了老弟')
if __name__ == '__main__':
app = tornado.web.Application([(r'/',IndexHandler),
(r'/upload/',UploadHandler),
],
debug=True,
autoreload=True,
template_path=os.path.join(os.path.dirname(__file__),'templates'),
)
app.listen(8000)
tornado.ioloop.IOLoop.current().start()

upload.html

{% extends 'common/base.html' %}
{% block title %}
文件上传
{% end %}
{% block content %}
<div class="page-header"><h2> 文件上传 </h2></div>
<form action="/upload/" method="post" enctype="multipart/form-data">
<p><input type="file" name="img"></p>
<p><input type="submit" value="上传"></p>
</form>
{% end %}

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-05-26发表,共计9879字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中