1 在码云上创建项目
使用码云 https://gitee.com/ 作为在线 git 源代码仓库,免费注册账号
- 在码云上创建项目:
- 创建完成之后,将已有项目clone到本地:
2 创建项目
- 新建项目,虚拟环境选择python3版本,创建manage.py文件
from flask import Flask
app = Flask(__name__)
@app.route('/index')
def index():
return 'index'
if __name__ == '__main__':
app.run()
- 记得设置项目虚拟环境
2 使用 GIT 管理源代码
2.1 本地提交
- 添加所有文件到暂存区
git add .
- 提交到本地仓库并填写注释
git commit -m'立项'
- 选择菜单中的VCS ->Git -> Push 提交,或者输入命令提交
git push
- Push 成功之后 Pycharm 弹出的提示
去码云上刷新当前项目查看Push结果
2.2 回滚代码
- 回滚到上一版本
git reset --hard HEAD~1
- 查看所有提交版本记录
git reflog
- 回到指定版本
git reset --hard 提交id
3 项目基本配置
3.1 Config类
- 先在当前类中定义配置的类,并从中加载配置
app = Flask(__name__)
class BaseConfig(object):
"""工程配置信息"""
DEBUG = True
app.config.from_object(Config)
运行测试
3.2 SQLAlchemy
- 导入数据库扩展,并在配置中填写相关配置
from flask_sqlalchemy import SQLAlchemy
...
class BaseConfig(object):
"""工程配置信息"""
DEBUG = True
# 数据库的配置信息
SQLALCHEMY_DATABASE_URI = "mysql://root:mysql@127.0.0.1:3306/info001"
SQLALCHEMY_TRACK_MODIFICATIONS = False
app.config.from_object(Config)
db = SQLAlchemy(app)
- 如果忘记数据库的配置信息会报错(有提示,不用刻意去记)
- 在终端创建数据库(创建的数据库要与配置的数据库名称相同)
mysql> create database information charset utf8;
运行测试
3.3 Redis
- 创建redis存储对象,并在配置中填写相关配置
import redis
...
class BaseConfig(object):
"""工程配置信息"""
...
# redis配置
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
app.config.from_object(Config)
db = SQLAlchemy(app)
redis_store = redis.StrictRedis(host=Config.REDIS_HOST, port=Config.REDIS_PORT)
运行测试
3.4 CSRF
- 包含请求体的请求都需要开启CSRF
from flask_wtf.csrf import CSRFProtect
...
app.config.from_object(BaseConfig)
...
CSRFProtect(app)
温馨提示:CSRFProtect只做验证工作,cookie中的 csrf_token 和表单中的 csrf_token 需要我们自己实现
补充:
CSRFProtect会对哪些请求进行校验呢?
post,put,patch,delete这些操作都是对服务器的数据有修改操作的,而不是获取数据操作,csrfProtect只会对修改操作做校验。
因为常用的是post,所以只需要记住post即可。
3.5 Session
- 利用 flask-session扩展,将 session 数据保存到 Redis 中
from flask_session import Session
...
class BaseConfig(object):
"""工程配置信息"""
SECRET_KEY = "EjpNVSNQTyGi1VvWECj9TvC/+kq3oujee2kTfQUs8yCM6xX9Yjq52v54g+HVoknA"
...
# flask_session的配置信息
SESSION_TYPE = "redis" # 指定 session 保存到 redis 中
SESSION_USE_SIGNER = True # 让 cookie 中的 session_id 被加密签名处理
SESSION_REDIS = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT) # 使用 redis 的实例
PERMANENT_SESSION_LIFETIME = 60 * 60 * 60 * 24 * 2 # session 的有效期,单位是秒
app.config.from_object(Config)
...
Session(app)
注意:
这里需要使用的扩展:lask-session,需要安装:pip install flask-session
secret_key是随机生成的字符串
secret_key生成方式如下:
-
使用os.urandom生成随机数,然后进行base64编码:
在index视图函数中添加 session["name"] = "Apollo" (session 需要导包(快捷键:Alt+Enter)),运行测试
3.6 Flask-Script与数据库迁移扩展
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
...
manager = Manager(app)
Migrate(app, db)
manager.add_command('db', MigrateCommand)
...
if __name__ == '__main__':
manager.run()
运行测试
提交并push到码云端:
VCS --> Git --> Commit File... / VCS --> Git --> Push
快捷键:提交(Ctrl + k)、push(Ctrl + shift + k)
4 代码抽取
- 目标:将特定逻辑代码抽取到指定的类中,各司其职,以便后续项目维护
4.1 配置文件
- 在与
manage.py
同级目录下创建config.py
文件,用作于项目的配置文件
import redis
class BaseConfig(object):
"""工程配置信息"""
DEBUG = True
SECRET_KEY = "EjpNVSNQTyGi1VvWECj9TvC/+kq3oujee2kTfQUs8yCM6xX9Yjq52v54g+HVoknA"
# 数据库的配置信息
SQLALCHEMY_DATABASE_URI = "mysql://root:mysql@127.0.0.1:3306/info001"
SQLALCHEMY_TRACK_MODIFICATIONS = False
# redis配置
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
# flask_session的配置信息
SESSION_TYPE = "redis" # 指定 session 保存到 redis 中
SESSION_USE_SIGNER = True # 让 cookie 中的 session_id 被加密签名处理
SESSION_REDIS = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT) # 使用 redis 的实例
PERMANENT_SESSION_LIFETIME = 60 * 60 * 60 * 24 * 2 # session 的有效期,单位是秒
4.2 业务逻辑独立
在整个项目文件夹中,除了启动文件 manage.py 和配置文件 config.py 放在根目录,其他具体业务逻辑文件都放在一个单独的文件夹内,与 manage.py 同级
- 创建 info Python Package,与 manager.py 同级
manage.py
只做最基本的启动工作,将app
的创建操作移动到info
的__init__.py
文件中
import redis
from flask import Flask
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import CSRFProtect
from config import BaseConfig
app = Flask(__name__)
# 配置
app.config.from_object(BaseConfig)
# 配置数据库
db = SQLAlchemy(app)
# 配置redis
redis_store = redis.StrictRedis(host=BaseConfig.REDIS_HOST, port=BaseConfig.REDIS_PORT)
# 开启csrf保护
CSRFProtect(app)
# 设置session保存位置
Session(app)
manage.py
的代码为
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from info import db, app
manager = Manager(app)
# 数据库迁移
Migrate(app, db)
manager.add_command("db", MigrateCommand)
@app.route("/")
def index():
return "index"
if __name__ == '__main__':
manager.run()
运行测试
4.3 项目多种配置
一个web程序在开发阶段可能与生产阶段所需要的配置信息可能不一样,所以为了实现此功能,可以给不同情况创建不同的配置类,比如开发阶段使用的配置类名为 DevelopementConfig
,生产阶段使用的配置类名为 ProdutionConfig
- 增加
config.py
文件的配置文件如下
...
class DevelopementConfig(BaseConfig):
"""开发模式下的配置"""
DEBUG = True
class ProductionConfig(BaseConfig):
"""生产模式下的配置"""
pass
接下来思考如何能才更快速的针对不同的布署环境去使用不同的配置
4.4 工厂类方法创建应用实例
要在不同环境下去使用不同的配置,那么可以在 manage.py
文件中给 info
包传入不同的配置信息,让 ihome 去根据传入指定配置去创建 app
,所以可以在 info
的 __init__.py
文件中添加一个工厂方法,根据传入的配置不同创建其对应的应用实例
- 在
config.py
文件中添加以下代码
# 定义配置字典
config = {
"development": DevelopementConfig,
"production": ProductionConfig
}
- 修改
info
文件夹下__init__.py
,添加create_app
的工厂方法
def create_app(config_name):
"""通过传入不同的配置名字,初始化其对应配置的应用实例"""
pass
- 修改
manage.py
文件中的代码
from info import create_app, db
# 创建 app,并传入配置模式:development / production
app = create_app('development')
- 将
__init__.py
文件中创建 app 实例的方法移动到create_app
方法中
from config import config
# 数据库
db = SQLAlchemy()
redis_store = None
def create_app(config_name):
"""通过传入不同的配置名字,初始化其对应配置的应用实例"""
app = Flask(__name__)
# 配置
app.config.from_object(config[config_name])
# 配置数据库
db.init_app(app)
# 配置redis
global redis_store
redis_store = redis.StrictRedis(host=config[config_name].REDIS_HOST, port=config[config_name].REDIS_PORT)
# 开启csrf保护
CSRFProtect(app)
# 设置session保存位置
Session(app)
return app
5 日志
5.1 日志相关概念
- 日志是一种可以追踪某些软件运行时所发生事件的方法
- 软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情
- 一个事件可以用一个可包含可选变量数据的消息来描述
- 此外,事件也有重要性的概念,这个重要性也可以被称为严重性级别(level)
5.2 日志的作用
- 通过log的分析,可以方便用户了解系统或软件、应用的运行情况;
- 如果你的应用log足够丰富,也可以分析以往用户的操作行为、类型喜好、地域分布或其他更多信息;
- 如果一个应用的log同时也分了多个级别,那么可以很轻易地分析得到该应用的健康状况,及时发现问题并快速定位、解决问题,补救损失。
- 简单来讲就是
- 我们通过记录和分析日志可以了解一个系统或软件程序运行情况是否正常,
- 也可以在应用程序出现故障时快速定位问题。
- 不仅在开发中,在运维中日志也很重要
- 日志的作用可以简单总结为以下几点:
- 程序调试
- 了解软件程序运行情况,是否正常
- 软件程序运行故障分析与问题定位
- 如果应用的日志信息足够详细和丰富,还可以用来做用户行为分析
5.3 日志的等级
- 我们先来思考下下面的两个问题:
- 作为开发人员,在开发一个应用程序时需要什么日志信息?在应用程序正式上线后需要什么日志信息?
- 作为应用运维人员,在部署开发环境时需要什么日志信息?在部署生产环境时需要什么日志信息?
- 在软件开发阶段或部署开发环境时,为了尽可能详细的查看应用程序的运行状态来保证上线后的稳定性,我们可能需要把该应用程序所有的运行日志全部记录下来进行分析,这是非常耗费机器性能的
- 当应用程序正式发布或在生产环境部署应用程序时,我们通常只需要记录应用程序的异常信息、错误信息等,这样既可以减小服务器的I/O压力,也可以更加方便的进行故障排查。
-
那么,怎样才能在不改动应用程序代码的情况下实现在不同的环境记录不同详细程度的日志呢?这就是日志等级的作用了,我们通过配置文件指定我们需要的日志等级就可以了。
-
不同的应用程序所定义的日志等级可能会有所差别,分的详细点的会包含以下几个等级:
- FATAL/CRITICAL = 重大的,危险的
- ERROR = 错误
- WARNING = 警告
- INFO = 信息
- DEBUG = 调试
- NOTSET = 没有设置
日志字段信息与日志格式:
输出一条日志时,日志内容和日志级别是需要开发人员明确指定的。对于而其它字段信息,只需要是否显示在日志中就可以了。
日志功能的实现:
Python 自身提供了一个用于记录日志的标准库模块:logging。
5.4 logging 模块
- logging 模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统
- logging 模块是 Python 的一个标准库模块,由标准库模块提供日志记录 API 的关键好处是所有 Python 模块都可以使用这个日志记录功能。
5.5 logging 模块的日志级别
- logging模块默认定义了以下几个日志等级,它允许开发人员自定义其他日志级别,但是这是不被推荐的,尤其是在开发供别人使用的库时,因为这会导致日志级别的混乱。
- DEBUG 最详细的日志信息,典型应用场景是 问题诊断
- INFO 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
- WARNING 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
- ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
- FATAL/CRITICAL 整个系统即将/完全崩溃
- 开发应用程序或部署开发环境时,可以使用 DEBUG 或 INFO 级别的日志获取尽可能详细的日志信息来进行开发或部署调试;
- 应用上线或部署生产环境时,应该使用 WARNING 或 ERROR 或 CRITICAL 级别的日志来降低机器的I/O压力和提高获取错误日志信息的效率。
日志级别的指定通常都是在应用程序的配置文件中进行指定的。
5.6 logging 模块的使用方式介绍
- loggers 提供应用程序代码直接使用的接口
- handlers 用于将日志记录发送到指定的目的位置
- filters 提供更细粒度的日志过滤功能,用于决定哪些日志记录将会被输出(其它的日志记录将会被忽略)
- formatters 用于控制日志信息的最终输出格式
# 设置日志的记录等级
logging.basicConfig(level=logging.DEBUG) # 调试debug级
# 创建日志记录器,指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限
file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024*1024*100, backupCount=10)
# 创建日志记录的格式 日志等级 输入日志信息的文件名 行数 日志信息
formatter = logging.Formatter('%(levelname)s %(filename)s:%(lineno)d %(message)s')
# 为刚创建的日志记录器设置日志记录格式
file_log_handler.setFormatter(formatter)
# 为全局的日志工具对象(flask app使用的)添加日志记录器
logging.getLogger().addHandler(file_log_handler)
5.7 使用logging提供的模块级别的函数记录日志
最简单的日志输出
- 先来试着分别输出一条不同日志级别的日志记录:
import logging
logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")
- 也可以这样写:
logging.log(logging.DEBUG, "This is a debug log.")
logging.log(logging.INFO, "This is a info log.")
logging.log(logging.WARNING, "This is a warning log.")
logging.log(logging.ERROR, "This is a error log.")
logging.log(logging.CRITICAL, "This is a critical log.")
修改配置改变输出内容
logging.basicConfig(level=logging.DEBUG)
切记:设置
Configurations
中的 Working directory 为当前项目
5.8 集成日志到当前项目
- 在
config.py
文件中在不同的环境的配置下添加日志级别
class Config(object):
...
# 默认日志等级
LOG_LEVEL = logging.DEBUG
class ProductionConfig(Config):
"""生产模式下的配置"""
LOG_LEVEL = logging.ERROR
- 在
info
目录下的init.py
文件中添加日志配置的相关方法
def setup_log(config_name):
"""配置日志"""
# 设置日志的记录等级
logging.basicConfig(level=config[config_name].LOG_LEVEL) # 调试debug级
# 创建日志记录器,指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限
file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024 * 1024 * 100, backupCount=10)
# 创建日志记录的格式 日志等级 输入日志信息的文件名 行数 日志信息
formatter = logging.Formatter('%(levelname)s %(filename)s:%(lineno)d %(message)s')
# 为刚创建的日志记录器设置日志记录格式
file_log_handler.setFormatter(formatter)
# 为全局的日志工具对象(flask app使用的)添加日志记录器
logging.getLogger().addHandler(file_log_handler)
- 在
create_app
方法中调用上一步创建的方法,并传入config_name
def create_app(config_name):
...
# 配置项目日志
setup_log(config_name)
app = Flask(__name__)
...
- 在项目根目录下创建日志目录文件夹
logs
,如下:
运行项目,当前项目日志已输出到
logs
的目录下自动创建的 log 文件中
- 在 logs 文件夹下创建 .gitkeep 文件,以便能将 logs 文件夹添加到远程仓库,并在 .gitignore 文件中添加忽略提交生成的日志文件
logs/log*
在 Flask框架 中,其自己对 Python 的 logging 进行了封装,在 Flask 应用程序中,可以以如下方式进行输出 log:
current_app.logger.debug('debug')
current_app.logger.error('error')
当前应用程序的 logger 会根据应用程序的调试状态去调整日志级别
6 注册蓝图
7 数据库表分析及创建
7.1 数据库表迁移
- 确认当前配置的数据库是否存在
mysql> use information;
- 将
constants.py
和models.py
文件拷贝到项目的info
目录下
注:constants.py 是当前项目中要使用的一些常量,预先定义好的,models.py 文件中需要使用到该文件中的一些常量
- 并在
manage.py
中导入models(否则会报错)
form info import models
在迁移的时候以便能读取到对应模型
- 执行数据库迁移
$ python manage.py db init
$ python manage.py db migrate -m"initial"
$ python manage.py db upgrade
- 查看数据库表是否创建完成
mysql> show tables;
- 执行导入初始分类的 SQL 语句
mysql> source info_info_category.sql
注意:生成的迁移文件不需要提交到 git 保存,所以需要在 .gitignore 文件中添加以下内容以便忽略迁移所生成的系列文件:
migrations
7.2 测试数据的添加
- 先添加分类测试数据
mysql> source 路径/information_info_category.sql
- 再添加新闻测试数据
mysql> source 路径/information_info_news.sql