【flask】JinJa2模版渲染及其他小记

By Heanny
2020-04-08
2010 read

最近在将本站升级为python语言,并使用flask进行实现,中途遇见的问题和一些方法的记录


token及密码校验

class user_info(db.Model):
    __tablename__ = 'user_info'
    username = db.Column(db.Integer, primary_key=True)
    password = db.Column(db.String(255), unique=False)
    role = db.Column(db.Integer, unique=False)
    name = db.Column(db.String(255), unique=False)

    def to_dict(self):
        '''将对象转换为字典数据'''
        reDict = {
            "username": self.username,
            "role": self.role,
            "name": self.name,
        }
        return reDict

    # 获取token,时长秒
    def generate_auth_token(self, expiration=3600 * 24):
        s = Serializer(app.config['SECRET_KEY'], expires_in=expiration)
        return s.dumps(self.username)

    # 通过token解析用户信息
    @staticmethod
    def verify_auth_token(token):
        s = Serializer(app.config['SECRET_KEY'])
        try:
            username = s.loads(token)
        except SignatureExpired:
            print('SignatureExpired', SignatureExpired)
            return None  # valid token, but expired
        except BadSignature:
            print('BadSignature', BadSignature)
            return None  # invalid token
        except Exception as e:
            print('Error: ', e)
            return None
        user = heanny_user_info.query.get(username)
        return user

    def hash_password(self, password):  # 加密
        self.password = pwd_context.encrypt(password)

    def verify_password(self, password):  # 解密
        return pwd_context.verify(password, self.password)

model外键

class h_posts(db.Model):
    __tablename__ = 'posts'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255), unique=False)
    content = db.Column(db.Text, unique=False)

    user = db.Column(db.String(255),db.ForeignKey(user_info.username), unique=False)
    author = db.relationship('user_info', backref=db.backref('h_posts'))

分页

    pagination = posts.query.order_by(desc('id')).paginate(page=int(page) if page else 1, per_page=15, error_out=False)
    blogs = list(map(lambda x: x.to_dict(), pagination.items))

blog.html

 <ul class="pagination flex-wrap">
                    {% if pagination.has_prev %}
                        <li class="page-item">
                            <a href="blog-1.html" class="page-link" data-ci-pagination-page="1"
                               rel="start"><i class="fa fa-angle-double-left"></i></a>
                        </li>
                        <li class="page-item ">
                            <a href="/blog-{{ pagination.prev_num }}.html" class="page-link"
                               data-ci-pagination-page="{{ pagination.prev_num }}" rel="prev">
                                <i class="fa fa-chevron-left"></i></a>
                        </li>
                    {% endif %}
                    {% for page in pagination.iter_pages(0,2,4,1) %}
                        {% if page %}
                            <li class="page-item {% if page==pagination.page %}active{% endif %}">
                                <a class="page-link" href="/blog-{{ page }}.html">{{ page }}</a>
                            </li>
                        {% else %}
                            <li class="page-item disabled"><a class="page-link" href="#">&hellip;</a></li>
                        {% endif %}
                    {% endfor %}

                    {% if pagination.has_next %}
                        <li class="page-item ">
                            <a href="/blog-{{ pagination.next_num }}.html" class="page-link"
                               data-ci-pagination-page="{{ pagination.next_num }}" rel="next"><i
                                    class="fa fa-chevron-right"></i></a>
                        </li>
                        <li class="page-item ">
                            <a href="/blog-{{ pagination.pages }}.html" class="page-link"
                               data-ci-pagination-page="pagination.pages"><i class="fa fa-angle-double-right"></i></a>
                        </li>
                    {% endif %}
                </ul>

模板渲染

Jinja2模版:

  1. 让页面逻辑独立于业务逻辑,开发的程序易于维护
  2. 提供流程控制,继承等高级功能使得模版非常灵活,快速,安全

强大模版

  1. 模版支持任何基于文本的格式(HTML/XML/CSV/LaTex等等),并没有特定的扩展名

    基础语法:
     {%- ... -%}
     说明: 主要解析流程控制等
     {#-  ...  -#}
     说明: 主要添加代码注释等
     {{    ...    }}
     说明: 主要解析模版表达式
    
    数据类型

    说明: 模版中支持字符串,数值,列表,元组,字典, boolean(true/false,注意是小写的),除此之外还支持全局函数/内置过滤器|/内置测试器is/in关键字/if关键字/字符连接符~

    1. 支持算术运算符,+,-,/,//,%,,*,
    2. 支持比较运算符,==,!=,>,>=,<,<=
    3. 支持逻辑运算符,and,or,not,(expr)
    4. 支持其它运算符,in,is,|,~(连接字符串使用{{ ‘hello ‘ ~ name ~ ‘!’ }},如果name是limanman则返回hello limanman!),()调用可调用量,./[]获取对象属性
    变量相关
    1. 应用可将任意类型变量传递给模版,访问带属性变量既可以加.访问,也支持[]访问,两者的区别是查找的先后顺序,前者先加.测试,然后再[]测试,后者先[]测试然后在.测试,所以根据具体传递变量类型决定

    2. 模版中中可以为变量赋值,在顶层的(块,宏,循环之外)赋值是可导出的,也就是说可以从别的模块儿中导入使用

      {%- set navigation = [('index.html', 'Index'), ('about.html', 'About')] -%}
      {%- set key, value =  call_something() -%}
      
    with语句
    1. 模版中支持with语句,和PY内置with类似,with后面常跟表达式,主要用于限制对象的作用域
      % with arr=range(10) %}
       {% for i in arr %}
           {{ i }}
       {% endfor %}
      {% endwith %}
      
    过滤器
    1. 过滤器和管道符|配合可链式修改变量,过滤器还支持传递参数,内置的过滤器列表可参考(http://docs.jinkan.org/docs/jinja2/templates.html#builtin-filters)
      {{ v|abs }}
      说明: 返回值的绝对值
      {{ v|attr(name) }}
      说明: 返回v对象的name属性值
      {{ v|batch(linecount, fill_with=None) }}
      说明: 将v序列linecount个元素分为一组,保证每组长度相同,如果不够用fill_with的值补充,常用于生成表格
      
      <!--说明: 创建一个10行10列的表格,通过batch分割成10行,然后再次遍历生成10个td-->
      <table width="600px" style="border-collapse: collapse;margin: 0 auto;" border="1">
       {%- for tr in table_tds|batch(10, "&nbsp;") -%}
           <tr>
               {%- for td in tr -%}
                   <td>{{ td }}</td>
               {%- endfor -%}
           </tr>
       {%- endfor -%}
      </table>
      
      {{ v|capitalize }}
      说明: 整个语句的首字母大写,其它字母小写
      {{ v|default(default_value='', boolean=False) }}
      说明: boolean为False,只有未定义时才返回default_value,boolean为True时,只要v为False就使用default_value.
      {{ v|dictsort(case_sensitive=False, by='key') }}
      说明: 对v字典进行排序case_sensitive设置是否大小写敏感,by指定键排序
      {{ v|escape }}
      说明: 对v进行安全转义,可以简写为e
      {{ v|filesizeformat(binary=False) }}
      说明: 格式化v为可读的文件大小默认以字节为单位
      {{ v|first }}
      说明: 返回序列对象的第一个元素
      {{ v|float(default=0.0) }}
      说明:转换value值为float类型,如果转换失败则返回default的值
      {{ v|forceescape }}
      说明: 不管v是否被转义过,一律进行html转义,可能会二次转义
      {{ v|format(*args, **kwargs) }}
      说明: 使用*args和**kwargs去填充格式化字符串v
      {{ v|groupby(attr) }}
      说明: 对序列v中的对象/字典按照attr分组,分组后组名存储在group.grouper里,而组员保存在group.list,如下是一个站点地图的简单实现
      
      <ul>
       {%- for group in persons|groupby("gender") -%}
           <li>{{ group.grouper }}</li>
           <ul>
               {%- for person in group.list -%}
                   <li>{{ person['name'] }}, {{ person['age'] }}</li>
               {%- endfor -%}
           </ul>
       {%- endfor -%}
      </ul>
      
      {{ vint(default=0) }}
      说明: 将v的值转换为int如果转换失败返回default的值
      {{ v|join(d='', attribute=None) }}
      说明: 使用d分割符拼接v,如果attribute为字符串则首先获取value的attribute属性然后用d分割符去拼接属性值
      {{ v|last }}
      说明: 返回序列对象v的最后一个元素
      {{ v|length }}
      说明: 说明返回对象的长度
      {{ v|list }}
      说明: 转换v为列表,如果value为字符串则转换为字符列表
      {{ v|lower }}
      说明: 转换为小写
      {{ v|map(filter/attribute='') }}
      说明: filter过滤器作用域v的每个元素,attribute过滤出v中包含指定属性的元素
      
      {%- for val_unit in  [1024, 2048, 4096]|map('filesizeformat') -%}
       <p>{{ val_unit }}</p>
      {%- endfor -%}
      
      {{ v|pprint(verbose=False) }}
      说明: 更优美的方式打印,用于调试
      {{ v|random }}
      说明: 从序列对象中随机返回一个元素
      {{ v|replace(old, new, count=None) }}
      说明: 将v中的old替换为new,如果指定了count定义替换n次
      {{ v|reverse }}
      说明: 反转value序列
      {{ v|round(precision=0, method='common') }}
      说明: 四舍五入v的值precision表示保留小数点几位,method的值可以为common/ceil/floor
      {{ v|safe }}
      说明: 标记value为安全字符串,按照正常规则去解析
      {{ value|sort(reverse=False, case_sensitive=False, attribute=None) }}
      说明: 如果指定attribute则按照attribute排序,reverse表示是否逆向排序,case_sensitive表示是否区分大小写
      
      {%- for cur_item in data|sort(false, true, attribute='index') -%}
       <p>{{ cur_item['index'] }}</p>
      {%- endfor -%}
      
      {{ v|string }}
      说明: 将v对象转换为字符串
      {{ v|striptags }}
      说明: 清除v中的html/xml等文档中的标签
      {{ v|sum(attribute=None, start=0) }}
      说明: 如果设置了attribute则会获取v中所有元素的attribute属性的值的总和,start为初始值
      {{ v|title }}
      说明: 让v中的每个字母的首字母大写
      {{ v|trim }}
      说明: 去除v两边的空白
      {{ v|truncate(length=255, kilwords=False, end='...') }}
      说明: v字符串超过length长度的部分用...代替,如果要精确到字符的话需设置killwards为true,否则设置为false
      {{ v|tojson|safe }}
      说明: 将v转换为json,但是为了不被转义,常常配合safe在JavaScript脚本中使用
      
      <script type="text/javascript">
       var users = {{ users|tojson|safe }}
       for(var i=0; i<users.length; i++){
           console.log(users[i].name)
       }
      </script>
      
      ```html
      {{ v|upper }}
      说明: 把v中的所有字符转换为大写
      {{ v|urlencode }}
      说明: 将v进行URL编码
      {{ v|urlize(trim_url_limit=None, nofollow=False) }}
      说明: 转换v为可点击的url连接地址,trim_url_limit设置url显示的长度,超出长度用…填充
      {{ v|wordcount }}
      说明: 获取s中的单词的数量
    2. 除了内置过滤器还支持自定义过滤器,非常简单可通过app.add_template_filter(func, name)函数或@@app.template_filter(name)修饰器来自定义过滤器,两种方法内部调用的都是app.jinja_env.filters[name] = func实现的,但并不推荐直接设置jinja2环境,所以还是推荐前两种方法
      ```python
      @monitor.app_template_filter('sub')
      def sub_filter(inputs, start, end, step):
       return inputs[start:end:step]
      
      ```html

      全局函数

    3. 如果说过滤器是一个变量转换函数,测试器是一个变了测试函数,那么全局函数就可以是任何函数,可以在任一场景使用,没有输入和输出值限制,全局函数可参考,http://docs.jinkan.org/docs/jinja2/templates.html#builtin-globals
      range([start], stop[, step])
      说明: 返回一个包含整等差级数的列表
      dict(**items)
      说明: 方便的字典替代品{‘foo’: ‘bar’}等价于dict(foo=bar)
      lipsum(n=5, html=True, min=20, max=100)
      说明: 在模版中生成lipsum乱数假文,默认会生成5段html,每段20到100词之间,如果html被禁用,则会返回常规文本,在测试布局时生成简单内容时很有用
      joiner(sep)
      说明: 初始化一个分割符,第一次调用返回空字符,以后调用返回分割符,常用于分割循环中的内容
      ```html
      <ul>
       {%- set sep = joiner('|') -%}
       {%- for title in ['资产管理', '主机分组', '主机信息'] -%}
           {%- set s = '<i>' ~ sep() ~ '</i>' if sep() -%}
           {{ s|safe }}
           <li><a href="#">{{ title }}</a></li>
       {% endfor %}
      </ul>
      
      说明: 在模版中生成乱数假文,默认会生成5段html,每段在20到100词之间,如果HTML被禁用,会返回常规文本,在测试布局时生成简单内容很有用
      cycler(*items)
      说明: 周期性的从若干个值的循环,周期实例.next()返回当前项并跳到下一个,.reset()重置周期计到第一个项,.current返回当前项
      
      {%- set cur_datas = range(1, 101, 1) -%}
      {%- set row_class = cycler('light', 'dark') -%}
      <table style="margin: 0 auto;border-collapse: collapse" width="600px" border="1">
       {%- for tr in cur_datas|slice(10) -%}
           <tr class="{{ row_class.next() }}">
               {%- for td in tr -%}
                   <td>{{ td }}</td>
               {%- endfor -%}
           </tr>
       {%- endfor -%}
      </table>
      
      join(sep=',')
      说明: 连接多个项
      
      {{ range(10)|join(',') }}
      
      ```html
    4. 除了内置全局函数还支持自定义全局函数,非常简单,通过app.add_template_global(func, name)函数或@app.template_global(name)修饰器来自定义全局函数,两种方法内部调用的都是app.jinja_env.globals[name] = func实现的,但并不推荐直接设置jinja2环境,所以还是推荐前两种方法
      ```python
      @monitor.app_template_global('endswith')
      def ends_width(s, suffix):
      return s.endswith(suffix)
      
      # 转义相关
      1.单行可用{{ '{{}}' }}作为原始字符串实现转义,多行可使用raw(如展示Jinja2语法的实例)
      
      <li>{{ '{{' }}</li>
      {% raw %}
      {% for item in seq %}
          <li>{{ item }}</li>
      {% endfor %}
      {% endraw %}
      
    模版继承
    1. Jinja2最强大的部分就是模版继承,模版继承允许你构建一个包含你站点公共元素的基本模版’骨架’,并定义子模版可以覆盖的块
      <!DOCTYPE html>
      <html {% block  html_attribs %}{%- endblock -%}>
      {%- block html -%}
       <head>
       {%- block head -%}
           {% block meta %}
           {%- endblock -%}
           {% block styles %}
           {%- endblock -%}
       {%- endblock -%}
       </head>
       <body {%- block body_attribs -%}{%- endblock -%}>
       {%- block body -%}
           {%- block scripts -%}
           {%- endblock -%}
       {%- endblock -%}
       </body>
      {%- endblock -%}
      </html>
      
      {%- extends 'base.html' -%}
      {%- block head -%}
       {{ super() }}
       {#- 头部区 -#}
      {%- endblock -%}
      {%- block body -%}
       {{ super() }}
       {#- 主体区 -#}
      {%- endblock -%}
      {%- block styles -%}
       {{ super() }}
       {#- 样式区 -#}
      {%- endblock -%}
      {%- block scripts -%}
       {{ super() }}
       {#- 脚本区 -#}
      {%- endblock -%}
      
清明追思家国永念
【flask】针对UEditor编写api

Comments

暂无评论,还不快来坐沙发...

Leave a Reply