Flask学习笔记

文章目录

  • 安装虚拟环境
  • app.py详解
  • settings配置文件
  • HTML 转义
  • 绑定路由
    • 唯一的 URL / 重定向行为
    • 使用不同HTTP 方法(POST、GET)
  • 渲染模板(给模板传参)
  • 操作请求数据
    • 请求对象 request(获取POST或GET传的参)
    • Flask文件上传
  • Cookie的使用
  • 消息闪现
  • 日志
  • .gitignore文件
  • 项目布局
    • \_\_init\_\_.py
  • 定义和操作数据库
    • 连接数据库
  • 模板
    • 继承(extends)
    • 包含(include)
    • 宏(macro)

安装虚拟环境

python虚拟环境

使用python开发多个项目的时候。不同项目需要不同的第三方库和不同的库版本,比如项目1需要flask1.0、pysql,而项目2需要flask1.2、requests。解决这一问题的方法是 虚拟环境。虚拟环境是一个包含了特定 Python 解析器以及一些软件包的自包含目录,不同的应用程序可以使用不同的虚拟环境,从而解决了依赖冲突问题,而且虚拟环境中只需要安装应用相关的包或者模块,可以给部署提供便利

使用如下命令安装python的虚拟环境三方库

pip install virtualenv
pip install virtualenvwrapper-win

Flask学习笔记_第1张图片

然后使用pycharm专业版创建一个flask项目

项目创建好后可以发现已经有了flask项目的基本框架
Flask学习笔记_第2张图片
每个部分的作用如下
Flask学习笔记_第3张图片

app.py详解

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080, debug=True)

0.0.0.0与127.0.0.1区别
这里host设置0.0.0.0的话,外网/局域网也可以访问,而设置127.0.0.1则只有本机可以访问

Flask学习笔记_第4张图片

debug模式
一共有三种模式 开发、测试、生产,这里设置debug=True之后,代表开启调试模式。

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080, debug=True)

没开启调试模式之前,使用app.py启动环境之后,修改代码,web页面不会随之改变。
而开启调试模式后,那么服务 器会在修改应用代码之后自动重启,并且当请求过程中发生错误时还会在浏览器 中提供一个交互调试器。
Flask学习笔记_第5张图片在这里插入图片描述

settings配置文件

除了在app.run()函数里配置之外,还可以使用环境变量进行配置

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080, debug=True)

查看这个app的所有配置变量,print(app.config)

# 结果如下
<Config {'ENV': 'development', 'DEBUG': True, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': d
atetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH
': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_
MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_
SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>

常用配置及含义(配置的名称必须是全大写形式,小写的变量将不会被读取)

ENV:测试环境
production:生产环境,development:开发环境
DEBUG:是否开启debug模式
SECRET_KEY:密钥字符串
JSON_AS_ASCII:是否以ascii编码展示响应报文
JSONIFY_MIMETYPE:响应报文类型

例如,通过修改环境变量修改测试环境和开启debug模式

app.config['ENV'] = 'development'
app.config['DEBUG'] = True

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080)

可以发现这样也是能使配置生效的
Flask学习笔记_第6张图片
但是把配置文件放在app.py启动文件里既影响美观,在更改的时候也可能误修改到启动文件导致出错。我们可以单独写一个配置文件,再在app.py中导入即可
Flask学习笔记_第7张图片

settings.py

# 配置文件
ENV = 'development'
DEBUG = True

app.py
添加如下代码

# 方法1
app.config.from_pyfile("settings.py")
# 方法2
import settings
app.config.from_object(settings.encode())

HTML 转义

当返回 HTML ( Flask 中的默认响应类型)时,为了防止注入攻击,所有用户提 供的值在输出渲染前必须被转义。使用 Jinja (这个稍后会介绍)渲染的 HTML 模板会自动执行此操作。

在下面展示的 escape() 可以手动转义。因为保持简洁的原 因,在多数示例中它被省略了,但您应该始终留心处理不可信的数据。

from markupsafe import escape

@app.route("/")
def hello(name):
    return f"Hello, {escape(name)}!"

绑定路由

官方文档:flask-路由

使用 route() 装饰器来把函数绑定到 URL,先使用route指定路径,然后跟一个函数。
绑定根路径和/hello路径的函数

@app.route('/')
def index():
    return 'Index Page'

@app.route('/hello')
def hello():
    return 'Hello, World'

变量规则
通过把 URL 的一部分标记为 就可以在 URL 中添加变量。标记的 部分会作为关键字参数传递给函数。通过使用 ,可以 选择性的加上一个转换器,为变量指定规则。

如下:
在eval路径下添加一个int类型的变量post_id

@app.route('/eval/')
def hello_world(post_id):  # put application's code here
    return f'Post {escape(post_id)}'

唯一的 URL / 重定向行为

以下两条规则的不同之处在于是否使用尾部的斜杠。:

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

如果绑定路由时没有斜杠,则访问时也不能加斜杠,否则就会返回404
Flask学习笔记_第8张图片

使用不同HTTP 方法(POST、GET)

可以使用 route() 装饰器的 methods 参数来处理不同的 HTTP 方法:

@app.route('/index', methods=["GET", "POST"])
def index():
    if request.method == "POST":
        return "POST"
    elif request.method == "GET":
        return "GET"

渲染模板(给模板传参)

格式如下:

/app.py
/templates
    /hello.html

代码如下
app.py

from flask import Flask
from flask import render_template

app = Flask(__name__)


@app.route('/hello/')
@app.route('/hello/')
def hello(username=None):
    return render_template("hello.html", name=username)


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080)

/templates/hello.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello Pagetitle>
head>
<body>
    {% if name %}
    <h1>hello {{ name }}h1>
    {% else %}
    <h1>hello wordh1>
    {% endif %}
body>
html>

访问 http://192.168.31.197:8080/hello/,返回hello
Flask学习笔记_第9张图片
添加一个用户名,http://192.168.31.197:8080/hello/admin

Flask学习笔记_第10张图片

操作请求数据

对于 web 应用来说对客户端向服务器发送的数据作出响应很重要。在 Flask 中 由全局对象 request 来提供请求信息。

请求对象 request(获取POST或GET传的参)

首先导入对象

from flask import request

表单数据可以使用from获得

# 例如获取post参数username和password
request.form['username']
request.form['password']

实例,获取POST值并输出:

from flask import Flask
from flask import request

app = Flask(__name__)

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        if request.form['username'] and request.form['password']:
            return request.form['username']+request.form['password']


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080)

传参username=POST&password=_TEST,成功获得参数值并输出
Flask学习笔记_第11张图片

GET方式的传参可以使用 args 属性

request.args.get('username')

如下实例,获取值并输出

from flask import Flask
from flask import render_template
from flask import request

app = Flask(__name__)
app.config.from_pyfile("settings.py")


@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'GET':
        if request.args.get('username') and request.args.get('password'):
            return request.args.get('username')+request.args.get('password')


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080)

成功!
Flask学习笔记_第12张图片

from flask import request
request.method   #请求方式
request.form 	#存储的是所有FormData中的所有数据,使用request.form.to_dict()可以转为字典输出
request.args 	#存储的是所有URL中的所有数据,使用request.args.to_dict()可以转为字典输出
request.json 	#Content-Type: application/json 存放在request.json中
request.data 	#当Content-Type无法被解析时,存放原始数据
request.url		#请求地址
request.path 	#url路由地址
request.host 	#主机地址
request.host_url 	#将主机地址转换为http url
request.headers 	#存储的是所有header信息
request.values      #存储的是post提交的form和url中后的键值(例如:?id=666&net=888),一般不使用. 因为存在问题:form表单和url有同名键值时,后者会覆盖前者.
request.files    # 获取文件,获取后保存用request.save
request.remote_addr    # 获取访问者ip

Flask文件上传

import os
from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = "./templates/upload/"
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

app.config.from_pyfile("settings.py")


def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            print(request.form)
            return "[+] file upload success! "+filename+"
back"
return ''' Upload new File

Upload new File

'''
if __name__ == '__main__': app.run(host="0.0.0.0", port=8080)

Cookie的使用

Flask的Cookie使用JWT

要使用cookie,首先要导入session模块

from flask import session

然后指定一个secret_key

app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

指定session里的username等于admin

session['username'] = "admin"

读取session里的username

username = session["username"]

退出登录,使用pop函数删除session里的指定项即可

session.pop('username', None)

最后的代码

from flask import session, Flask, request, redirect, url_for

app = Flask(__name__)

app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {session["username"]}'
    return 'You are not logged in!
点击此处登录'
@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] # return redirect(url_for('index')) if 'username' in session: return f'Logged in as {session["username"]}' return '''

'''
@app.route('/logout') def logout(): # remove the username from the session if it's there session.pop('username', None) return redirect(url_for('index')) if __name__ == '__main__': app.run(host="0.0.0.0",port=8888,debug=True)

消息闪现

使用flash()来发送消息

flash('You were successfully logged in')

使用get_flashed_messages()来获得消息

{% with messages = get_flashed_messages() %}
  {% if messages %}
    <ul class=flashes>
    {% for message in messages %}
      <li>{{ message }}li>
    {% endfor %}
    ul>
  {% endif %}
{% endwith %}

日志

flask日志输出级别有以下几级

CRITICAL:严重
ERROR:这个级别的日志意味着系统中发生了非常严重的问题,必须有人马上处理,比如数据库不可用了,系统的关键业务流程走不下去了等等。
WARN:发生这个级别的问题时,处理过程可以继续,但必须要对这个问题给予额外的关注。允许这种情况存在,但必须及时做跟踪检查。
INFO:一般的使用场景是重要的业务处理已经结束,我们通过这些INFO级别的日志信息,可以很快的了解应用正在做什么。
DEBUG和TRACE:这两个级别的日志是只限于开发人员使用的,一般情况下,我们使用DEBUG足以。
NOTSET

打印日志内容
Flask已经帮我们预配置了一个logger,我们可以使用这个logger来完成我们所有的打印操作。

app.logger.info('this is a string')
app.logger.debug('this is a string')
app.logger.warning('warning log')
# 添加字符串
app.logger.info('the message if %s', info)

设置存储位置和日志格式

handler = logging.FileHandler('flask.log', encoding='UTF-8')   # 设置日志字符集和存储路径名字
logging_format = logging.Formatter(                            # 设置日志格式
    '%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
handler.setFormatter(logging_format)
app.logger.addHandler(handler)

.gitignore文件

Flask学习笔记_第13张图片

项目布局

/YU
├── app/
│   ├── __init__.py
│   ├── db.py
│   ├── schema.sql
│   ├── auth.py
│   ├── blog.py
│   ├── templates/
│   │   ├── base.html
│   │   ├── auth/
│   │   │   ├── login.html
│   │   │   └── register.html
│   │   └── blog/
│   │       ├── create.html
│   │       ├── index.html
│   │       └── update.html
│   └── static/
│       └── style.css
├── client/
│   ├── subdomain_scan/
│   |   ├── amass
│   |   ├── oneforall
│   |   └── ... ...
├── venv/
├── app.py
└── MANIFEST.in

__init__.py

init.py 有两个作用:一是包含应用工厂;二 是告诉 Python flaskr 文件夹应当视作为一个包。

create_app是一个应用工厂函数,作用:

def create_app(test_config=None):
	# 创建Flask实例
	app = Flask(__name__, static_folder='static')
	
	# 加载配置
	app.config.from_object(config)
	
	# 返回实例
	return app

定义和操作数据库

连接数据库

import configparser
import pymysql

cfg = configparser.ConfigParser()
cfg.read('config.ini')

def dbconn():
    #初始化数据库连接
    DB_HOST = cfg.get("DATABASE", "DB_HOST")
    DB_USER = cfg.get("DATABASE", "DB_USER")
    DB_PORT = cfg.get("DATABASE","DB_PORT")
    DB_PASSWD = cfg.get("DATABASE", "DB_PASSWD")
    DB_DATABASE = cfg.get("DATABASE", "DB_DATABASE")
    conn = pymysql.connect(host=DB_HOST, port=int(DB_PORT), user=DB_USER, password=DB_PASSWD, db=DB_DATABASE, charset='utf8')
    cursor = conn.cursor()
    return conn,cursor

模板

继承(extends)

父模板定义网站的顶部菜单、底部这些可以继承的东西。并使用{% block <自定义> %}{% endblock %}预留下空间,例如标题,主要内容等

├── templates
│   ├── base.html
│   └── login.html
├── app.py

首先定义一个父模板,使用block标签预留标题、内容自定义
base.html

DOCTYPE html>
<html lang="en">

<head>
    <title>
        {% block title %}{% endblock %}
    title>
head>

<body>
		{% block content %}{% endblock content %}
body>
html>

子模板:
首先继承base.html,然后通过block名字找到对应的标签block来填充内容,这里定义标题部分为Login,内容部分为登录框

{% extends "base.html" %}

{% block title %} Login {% endblock %}

{% block content %}
    <div>
        <div>
            <input id="username" type="text" autocomplete="off" placeholder="邮箱">
        div>
        <div>
            <input id="password" type="password" autocomplete="off" placeholder="登录密码">
        div>
        <div>
            <button id="submit">登 录button>
        div>
    div>
{% endblock content %}

定义路由文件app.py

from flask import render_template, Flask

app = Flask(__name__)


# 展示父模板页面
@app.route('/')
def load_base():
    return render_template('base.html')


# 展示子模板页面
@app.route('/login')
def index():
    return render_template('login.html')


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8000, debug=True)

访问根目录,什么内容都没有,因为只定义了html基本框架
Flask学习笔记_第14张图片

访问/login,标题为Login,内容为登录框
Flask学习笔记_第15张图片

包含(include)

继承是定义所有模板都有的公共部分,而包含只有几个文件需要,只需要在需要的文件里包含即可

{% include '文件夹/模板文件' %}

宏(macro)

#定义宏
{% macro 宏名(参数) %}
	代码块
{% endmacro %}
 
#导入宏
{% import '宏文件' as 别名 %}
 
#使用宏
{{ 别名.宏名(参数) }}

学习自:Flask框架——模板复用(继承、包含、宏)

你可能感兴趣的:(#,python,flask,python,学习)