python虚拟环境
使用python开发多个项目的时候。不同项目需要不同的第三方库和不同的库版本,比如项目1需要flask1.0、pysql,而项目2需要flask1.2、requests。解决这一问题的方法是 虚拟环境。虚拟环境是一个包含了特定 Python 解析器以及一些软件包的自包含目录,不同的应用程序可以使用不同的虚拟环境,从而解决了依赖冲突问题,而且虚拟环境中只需要安装应用相关的包或者模块,可以给部署提供便利
使用如下命令安装python的虚拟环境三方库
pip install virtualenv
pip install virtualenvwrapper-win
然后使用pycharm专业版创建一个flask项目
项目创建好后可以发现已经有了flask项目的基本框架
每个部分的作用如下
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则只有本机可以访问
debug模式
一共有三种模式 开发、测试、生产,这里设置debug=True
之后,代表开启调试模式。
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8080, debug=True)
没开启调试模式之前,使用app.py启动环境之后,修改代码,web页面不会随之改变。
而开启调试模式后,那么服务 器会在修改应用代码之后自动重启,并且当请求过程中发生错误时还会在浏览器 中提供一个交互调试器。
除了在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)
可以发现这样也是能使配置生效的
但是把配置文件放在app.py启动文件里既影响美观,在更改的时候也可能误修改到启动文件导致出错。我们可以单独写一个配置文件,再在app.py中导入即可
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 ( 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)}'
以下两条规则的不同之处在于是否使用尾部的斜杠。:
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
如果绑定路由时没有斜杠,则访问时也不能加斜杠,否则就会返回404
可以使用 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
添加一个用户名,http://192.168.31.197:8080/hello/admin
对于 web 应用来说对客户端向服务器发送的数据作出响应很重要。在 Flask 中 由全局对象 request 来提供请求信息。
首先导入对象
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
,成功获得参数值并输出
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)
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
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)
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)
/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 有两个作用:一是包含应用工厂;二 是告诉 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
父模板定义网站的顶部菜单、底部这些可以继承的东西。并使用{% 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)
继承是定义所有模板都有的公共部分,而包含只有几个文件需要,只需要在需要的文件里包含即可
{% include '文件夹/模板文件' %}
#定义宏
{% macro 宏名(参数) %}
代码块
{% endmacro %}
#导入宏
{% import '宏文件' as 别名 %}
#使用宏
{{ 别名.宏名(参数) }}
学习自:Flask框架——模板复用(继承、包含、宏)