Web前端—05Flask、Jinja2入门

文章目录

  • 一、flask简介
    • 1、什么是Flask?
    • 2、flask第一个网站
    • 3、设置动态路由
  • 二、http请求方法的实现
    • 1、 http请求常见的方式:GET POST
    • 2、 模板渲染
    • 3、 重定向和错误(redirect, error)
    • 4、get方法实现登录
    • 5、post方法实现登录
    • 6、自定义错误页面
    • 7、flask日志记录
  • 四、消息闪现
    • 1、什么是消息闪现?
    • 2、应用场景
    • 3、实现步骤
  • 五、综上实现用户登录系统
  • 五、Jinja2模板引擎
    • 1、什么是Jinja2模板引擎
    • 2、Jinja2语法
      • (1)变量显示语法:
      • (2)变量内置过滤器:
      • (3)如何自定义过滤器
      • (4)宏操作
      • (5)include包含操作
      • (6)模板继承

一、flask简介

1、什么是Flask?

首先看一下用户通过浏览器访问网站的页面信息,经过了哪些?
Web前端—05Flask、Jinja2入门_第1张图片

  • Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供IMAP / POP3 / SMTP服务。接收的请求量大,10K。
  • uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。http协议和nginx进行交流,WSGI协议和后端的python代码,wed框架进行交流。
  • Framework即架构,它是一个语言开发软件,提供了软件开发的框架,使开发更具工程性、简便性和稳定性。常用的就是django和falsk框架,falsk用于小项目,django用于大项目。

Flask是一个Web框架,就是提供一个工具库和技术来允许你构建一个Web应用程序.这个Web应用程序可以是一些Web页面,博客, wiki ,基于 Web 的日历应用或商业网站。

Flask依赖模块:
web服务网关接口(Python Web Server Gateway Interface,缩写为WSGI)

  • Werkzeug 一个WSGI工具包, 是为python语言定义的web服务器和web应用程序或框架之间的一种简单而通用的接口,其他语言也有类似的接口)
  • jinja2模板引擎。前端的操作。

Flask的优势:
Flask属于微框架( micro-framework )这一类别,微架构通常是很小的不依赖外部库的框架.
框架很轻量
更新时依赖小
专注于安全方面的bug

2、flask第一个网站

两个名词:
路由

@app.route()   #访问的路径

试图函数

def 函数名()   #给用户返回的页面信息
from flask import Flask

# 实例化,__name__:确定flask项目所在位置
app = Flask(__name__)

#实现首页:http://访问的ip:端口/
#路由:确保访问的路径,执行哪个函数
@app.route('/')  # 路由
def index():     # 试图函数 ,给用户返回页面信息,名字不能重复
    return '这是网站的首页'

@app.route('/login/')
def login():
    return '正在登录'

@app.route('/logout/')
def logout():
    return '正在登出'


if __name__ == '__main__':
    #运行flak项目,默认ip和端口是127.0.0.1:5000
    #如何特色化指定?host=0.0.0.0 开放本机所有的ip port=9990 端口必须是整形数
    #debug=True 是否开启调试,测试环境中开启,生产环境一定要关闭
    app.run(host='0.0.0.0', port=9990, debug=True)

3、设置动态路由

from flask import Flask, request  # 请求信息存在request

app = Flask(__name__)

"""
动态路由方式一: http://www.csdn.org/
"""
#路由是一个变量
@app.route('//')  #接收了一个变量信息 userid
def userinfo(userid):
    return "查看用户%s的详细博客" %(userid)

@app.route('/welcome/')
def welcome(username):
    return "欢迎访问%s用户的主页" %(username)

"""
动态路由方式二: https://movie.douban.com/top250?start=25&filter=
                有对应的 key value值
"""
@app.route('/top250')
def top250():
    users = ['user%s' %(i) for i in range(100)]
    # request 存储用户请求页面的所有头部信息
    print('客户端的用户代理:',request.user_agent)
    print('请求页面的头部信息:',request.headers)
    print('客户端ip:',request.remote_addr)
    print('客户端请求的参数详细信息:',request.args)
    print('客户端http请求的方法:',request.method)  # http请求方法 get:会在地址栏显示,不安全,有大小限制 opst
    # 获取用户请求的URL地址里的key对应的value值
    start = int(request.args.get('start'))  #返回的时字符串,转为整形
    user = request.args.get('user')
    # return 'top 250 显示数据:%s条 用户名:%s' %(start,user)
    import json
    return json.dumps(users[start:start+10])  #返回的是列表,转为字符串

if __name__ == '__main__':
    app.run()

二、http请求方法的实现

1、 http请求常见的方式:GET POST

  • (1) url可见性:
    get: 参数url可见
    post: 参数不可见

  • (2) 数据传输:
    get: 通过拼接url进行传递参数
    post: 通过body体传输参数

  • (3) 缓存性:
    get请求是可以缓存的
    post请求不可以缓存

  • (4) 后退页面的反应:
    get请求页面后退时,不产生影响
    post请求页面后退时,会重新提交请求

  • (5) 传输数据的大小:
    get一般传输数据大小不超过2-4k(根据浏览器不同,限制不一样,但相差不大)
    post请求传输数据可以无限大

  • (6) 安全性:原则上
    get安全性较低
    post安全性较高

2、 模板渲染

例如: hello {{name}} 一个模板,取决于用户给返回的值
若用户返回的信息为:name = westos
就将name=westos填入 —> hello westos

flask和django一样都配备了Jinja2模板引擎,可以使用 render_template() 方法来渲染模板

3、 重定向和错误(redirect, error)

使用redirect()函数把用户重定向到其他地方
使用abort()函数,放弃请求并返回错误代码

http状态码:404 200 304 302 500
4开头 客户端错误
5开头 服务端错误

4、get方法实现登录

from flask import Flask, render_template, request, redirect

app = Flask(__name__)
@app.route('/')
def index():
    return "

主页

"
@app.route('/login/') def login(): """ 一般情况下,不会直接把html文件内容直接返回, 而是将html文件保存到当前的templates目录中 通过render_template方法调用; 默认情况下,flask在程序文件夹中的templates子文件夹中寻找模板 """ return render_template('login.html') # 返回了login.html,去login.html里看,用户输入了这些信息后,提交到action="/login2/ login2中 # 再通过login2函数来判断用户名和密码 # 处理登录逻辑,判断用户名和密码 @app.route('/login2/') def login2(): #获取用户输入的用户名 username = request.args.get('username', None) password = request.args.get('password', None) # 逻辑处理,判断用户名和密码是否正确 if username == 'root' and password == 'root': #重定向到指定路由 #如果登录成功,进入主页 return redirect('/') else: #return '登录失败' #如果登录失败,重定向到登录界面,重新登录 return redirect('/login/') if __name__ == '__main__': app.run()

templates目录中的login.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>用户登录</h1>

<form action="/login2/" method="get">
    <input type="text" name="username" placeholder="username"><br/>
    <input type="password" name="password" placeholder="password"><br/>
    <input type="submit" value="登录">

</form>

</body>
</html>

Web前端—05Flask、Jinja2入门_第2张图片

5、post方法实现登录

from flask import Flask, render_template, request, redirect

app = Flask(__name__)
@app.route('/')
def index():
    return "

主页

"
# 默认路由只支持get方法,如何指定为post方法: @app.route('/login/', methods=['get', 'post']) def login(): """ 1. 用户访问网址:http://xxxx/login ,返回登录的html页面, 34行 这里是通过get方法访问的, 2. 返回了html页面,用户填写信息 根据
提交到login,方法是post 3. 执行post提交的逻辑 """ if request.method == 'post': # post请求提交的数据如何获取? request.form.get; get请求如何获取:request.args.get # post提交的数据是放在了一个表单里 print(request.form) username = request.form.get('username', None) password = request.form.get('password', None) if username == 'root' and password == 'root': # 如果登录成功,进入主页,重定向 return redirect('/') else: # render_template()模板渲染,给html传递变量,传的是errMessages return render_template('login_post.html', erromessage="用户名或者密码错误") else: return render_template('login_post.html') if __name__ == '__main__': app.run()

templates目录中的login_post.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>用户登录</h1>

<form action="/login/" method="post">
    <input type="text" name="username" placeholder="username"><br/>
    <input type="password" name="password" placeholder="password"><br/>
    <input type="submit" value="登录">
</form>

<p style="color: red">{{ erromessage }}</p>

</body>
</html>

6、自定义错误页面

  • 为什么要自定义错误页面?
    如果在浏览器的地址栏中输入了不可用的路由,那么会显示一个状态码为404的错误页面
    这个页面太难看

  • 如何自定义错误页面?
    像常规路由一样,flask允许程序使用基于模板的自定义错误页面 errorhandler()
    最常见的错误代码有两个:
    404:用户端请求位置页面或路由时显示
    500:有未处理的异常时显示

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/welcome/')
def welcome():
    return render_template('welcome.html')

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'),404

@app.errorhandler(500)
def internal_server_error(e):
    return render_template('500.html'),500

if __name__ == '__main__':
    app.run()

templates目录中的404.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1 style="color: green">你寻找的网站迷路了</h1>

</body>
</html>

templates目录中的500.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 style="color: green">请耐心等待,稍后再试</h1>

</body>
</html>

7、flask日志记录

from flask import Flask
import logging

app = Flask(__name__)

#日志系统配置,设置文件存放位置
handler = logging.FileHandler('app.log',encoding='utf-8')
#设置日志文件存储格式
logging_format = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
#将日志文件处理对象和日志格式绑定
handler.setFormatter(logging_format)
#设置日志级别
handler.setLevel('DEBUG')
#将日志格式和app绑定
app.logger.addHandler(handler)

@app.route('/')
def index():
    app.logger.debug('hello')
    app.logger.error('error')
    app.logger.exception('exception')
    return 'index'
if __name__ == '__main__':
    app.run(debug=True)

静态文件存储位置:
在开发过程中,Flask静态文件位于应用的/static目录中

调用方式:
静态文件的位置是:static/style.css
{{ url_for(‘static’, filename = ‘style.css’)}}

四、消息闪现

1、什么是消息闪现?

消息闪现就是只展示一次的数据 / 参数
Flask提供了一个非常简单的方法来使用闪现系统向用户反馈信息。
闪现系统使得在一个请求结束的时候记录一个信息,然后仅仅在下一个请求中访问这个数据
Flask消息闪现可以给用户更好的体验

2、应用场景

  • 上传图片代码,加入消息闪现功能
  • 进入首页只刷一次的广告
  • 用户登录成功的闪现信息
  • 用户登录失败的闪现信息
  • 用户注册成功的闪现信息
  • 用户注册失败的闪现信息

3、实现步骤

  • 引入一个函数flash,用于存储展示的信息
from flask import flash
  • 使用flash保存,实际上是暂时帮我们保存在session里面存储时需要加密
app.config['SECRET_KEY'] = 'westos'  # 加密盐
  • Flask后台中存储闪现信息
flash('要展示的闪现信息')
  • 前端页面获取闪现信息
{{ get_flashed_messages() }}

五、综上实现用户登录系统

from flask import Flask, request, render_template, redirect, session, flash

app = Flask(__name__)
app.config['SECRET_KEY'] = 'westos'  # 加密盐

users = [{'username':'root', 'password':'root'}]

#会话:session
#缓存:cookie

# 主页
@app.route('/')
def hello_world():
    return render_template('index.html')

# 登录页面
@app.route('/login/',methods=['GET','POST'])
def login():
    if request.method == 'GET':
        return  render_template('login.html')
    else:
        # 获取POST提交的数据
        username = request.form.get('username')
        password = request.form.get('password')
        for user in users:
            if user['username'] == username and user['password'] == password:
                #加入到会话,存储用户的登录信息,可以认为是字典对象
                session['usernme'] = username
                #print(session)
                flash('登录成功')    # 返回的是一个列表
                return redirect('/')
            else:
                flash('登录失败')
                return render_template('login.html', erromessage = "登录失败"
)

# 登出页面
@app.route('/logout/')
def logout():
    #将session中的用户信息删除
    session.pop('username')
    flash('注销成功')
    return redirect('/login/')

# 注册页面
@app.route('/register/', methods=['GET','POST'])
def register():
    """
    1. GET方法:  直接返回注册页面
    2. POST方法: 注册用户
                注册的用户名是否已经存在,如果存在,重新注册,
                如果不存在,存储用户名和密码到数据库中
    """
    if request.method == 'GET':
        return render_template('register.html')
    else:
        username = request.form.get('username')
        password = request.form.get('password')
        for user in users:
            # 注册的用户名是否已经存在,如果存在,重新注册,
            if user['username'] == username:
                flash('注册失败,用户名已经存在')
                return redirect('/register/')
            else:
                #如果不存在,存储用户名和密码到数据库中
                users.append(dict(username=username, password=password))
                flash('用户注册成功,请登录')
                return redirect('/login/')

f __name__ == '__main__':
    app.run()

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<p style="color: red">{{ get_flashed_messages() }}</p>
<!--获取缓存中的用户名信息 : session,request,g,get_flashed_messages()可以直接从前台获取后台信息-->
<p style="font-size: 12px">用户{{ session.username }}登录成功</p>
<hr>
<h1 style="color: green">首页</h1><br>

</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>用户登录</h1>

<form action="/login/" method="post">
    <!--    required=""  : 表示不能为空-->
    <input type="text" name="username" placeholder="用户名" required=""><br/>
    <input type="password" name="password" placeholder="密码" required=""><br/>
    <input type="submit" value="登录">
</form>
<a style="font-size: 12px" href="/register/">立即注册</a>
<!--获取闪现信息,通过函数get_flashed_messages()-->
<p style="color: red">{{ get_flashed_messages() }}</p>

</body>
</html>

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>用户登录</h1>

<form action="/register/" method="post">
    <input type="text" name="username" placeholder="用户名" required=""><br/>
    <input type="password" name="password" placeholder="密码" required=""><br/>
    <input type="submit" value="注册">
</form>
<p style="font-size: 12px">已有帐号?</p><a style="font-size: 12px" href="/login/">立即登录</a>
<p style="color: red">{{ get_flashed_messages() }}</p>

</body>
</html>
from flask import session, flash, redirect, request
from functools import wraps

#实现:去访问两个路由,先登录才能访问,用装饰器装饰

def is_login(fun):
    """判断用户是否登录成功"""
    @wraps(fun)    # 不改变被装饰函数的属性:名字,帮助文档
    # 试图函数的名称不能一样,当用这个装饰器去装饰多个函数的时候,返回的都是wrapper,名字就重复了
    def wrapper(*args, **kwargs):
        """
        判断session中是否有用户信息
        如果有:登录成功
        如果没有:登录失败,跳转到登录界面
        """
        if session.get('username'):
            return fun(*args, **kwargs )
        else:
            flash('用户必须登录才能访问该网页%s' %(request.path)) #获取用户请求的url地址后面的路径
            return redirect('/login/')
    return wrapper

五、Jinja2模板引擎

1、什么是Jinja2模板引擎

Jinja2是一个现代的,设计者友好的,仿照Django模板的python模板语言。
它速度快,被广泛使用,并且提供了可选的模板执行环境保证安全。

  • python的Web开发中,业务逻辑(实质就是试图函数的内容)和页面逻辑(html文件)是分开的,使得代码的可读性增强,代码容易理解和维护
  • 模板渲染:在html文件中,通过动态赋值,将重新翻译好的html文件(模板引擎生效)返回给用户的过程
  • 其他的模板引擎:Mako,Template,Jinja2
  • flask中默认的模板引擎是Jinja2

2、Jinja2语法

(1)变量显示语法:

{{ 变量名 / 函数调用 }}

(2)变量内置过滤器:

类似于python中的一些方法
完整的过滤器查看位置:http://jinja.pocoo.org/docs/templates/#builtin-filters

常用的过滤器:

  • safe 渲染值时不转义
  • capitalize 把值的首字母转换成大写,其他字母转换成小写
  • lower 把值转换成小写
  • upper 把值转换成大写
  • title 把值中每个单词的首字母都转换成大写
  • trim 把值的首尾空格去掉
  • striptags 渲染之前把值中所有的html标签都删掉
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    text= """
    

hello world

"""
return render_template('jinja2.html', text=text) if __name__ == '__main__': app.run()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--safe过滤器,展示原本的html,不将它进行转义-->
{{ text | safe }}
<!--把值中所有的html标签都删掉,只展示文本-->
{{ text | striptags }}

</body>
</html>

(3)如何自定义过滤器

for循环

{% for i in li %}
   xxxx
{% endfor %}

例如:

{% for user in users %}
 <tr>
    <td>{{ user.username }}</td>
    <td>{{ user.password }}</td>
</tr>
{% endfor %}

if语句

{% if 条件 %}
   xxxx
{% elif 条件 %}
   xxxx
{% else %}
   xxxx
{% endif %}

例如:

<!--如果用户登录,显示注销-->
<!--如果没有登录,显示登录和注册 -->
<!--如何判断是否登录:session.get('username')-->
{% if session.get('username')%}
    <li><a href="logout">注销</a>/li>
{% else %}
    <li><a href="login">登录</a>/li>
    <li><a href="register">注册</a>/li>
{% endif %}

(4)宏操作

可以理解为函数

如何定义宏:
相当于python中定义函数,利用关键字macro

{ % macro 宏名称(参数) %}
xxxx
{% endmacro %}

如何调用宏:

{{ 宏名称() }}
{# 声明jinjn2的宏 #}
{% macro fun(id) %}
    <h1>hello {{ id }}</h1>
{% endmacro %}
{#调用宏#}
{{ fun(1) }}
{{ fun(2) }}
</body>
</html>

(5)include包含操作

直接包含,将一个html文件放到另一个html文件里

{% include "文件名.html" %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .left{
            width: 20%;
            border: 1px solid red;
            float: left;
        }
        .right{
            width: 79%;
            border: 1px solid green;
            float: left;
        }
    </style>
</head>
<body>
<div class="left">
    { % include 'left.html' % }
</div>
<div class="right">
    right
</div>

</body>
</html>

left.html文件

<ul>
    <li>新闻</li>
    <li>财经</li>
    <li>八卦</li>
</ul>

(6)模板继承

一般网站的导航栏和底部不会变化,为了避免重复编写导航栏信息
就是将相同的部分抽象出来,将不同的内容填充进去

如何定义模板:block
相当于挖了个洞,将来要将不同的内容填充进去

{% block 名称 %}
{% endblock %}

模板文件:jinja2_base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %}</title>
</head>
<body>

<div style="width: 100px; height: 200px">这里是导航栏</div>
{% block body %}
{% endblock %}
<div style="width: 100px; height: 200px">这里是底部</div>

</body>
</html>

其中{% block title %}和{% block body %}是要填充的部分

如何继承:extends

{% extends '模板文件.html' %}
{% block 名称 %}
要填充的内容
{% endblock %}
{% extends 'jinja2_base.html' %}

{% block title %}主页{% endblock %}

{% block body %}

# 声明jinjn2的宏 #}
{% macro fun(id) %}
    <h1>hello {{ id }}</h1>
{% endmacro %}
{#调用宏#}
{{ fun(1) }}
{{ fun(2) }}
    
{% endblock %}

你可能感兴趣的:(Web前端)