目录
一、项目简介
任务清单管理系统采用
B
/
S
架构,基于
Linux
平台开发。采用轻量级的
Web
服务器
Nginx
, 其后端实现建议采用基于 Python
语言的
Flask
开源
Web
框架,进而增强扩展性。数据库采用关系型数据库
Mariadb
,前端的技术栈使用
Bootstrap
框架。该系统面向学生或者企业员工,提供任务添加、任务删除、任务完成标记, 任务搜索 ,可视化操作、数据实时展现等功能,目的在于轻松查看自己和他人的工作安排,合理规划手头任务。
二、项目功能
就像一般的
Todo List
应用一样, 实现了以下功能:
- 管理数据库连接
- 列出现有的待办事项
- 创建新的待办事项
- 检索单个待办事项
- 编辑待办事项或标记待办事项
- 删除待办事项
三、技术分析
1、为什么选择Flask?
- Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。
- Flask也被称为 “microframework” ,因为它使用简单的核心,用 extension 增加其他功能。
- Flask没有默认使用的数据库、窗体验证工具。
- 因此Flask是一个使用Python编写的轻量级Web应用框架。轻巧易扩展,而且够主流,有问题不怕找不到人问,最适合这种轻应用了。
2、为什么选择 Mariadb ?
- MariaDB 数据库管理系统是 MySQL的 一个分支,主要由开源社区在维护,采用 GPL 授权许可MariaDB 的目的是完全兼容 MySQL ,包括 API 和命令行,使之能轻松成为 MySQL 的代替品。
- MariaDB 虽然被视为 MySQL 数据库的替代品,但它在扩展功能、存储引擎以及一些新的功能改进方面都强过 MySQL 。而且从 MySQL 迁移到 MariaDB 也是非常简单的.
3、为什么选择Bootstrap?
- Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架。Bootstrap 是基于 HTML、CSS、JavaScript 的。具有移动设备优先、浏览器支持良好、容易上手、响应式设计等。
四、Flask开发大型项目结构
1、项目结构
多文件
Flask
程序的基本结构
,
如下图所示
:
![](https://i-blog.csdnimg.cn/blog_migrate/211ee321e76fc0e2b35d17aaf10da17e.png)
•
requirements.txt
列出了所有依赖包
,
便于在其他电脑中重新生成相同的虚拟环境
;
•
config.py
存储配置
;
•
manage.py
用于启动程序以及其他的程序任务。
2、配置文件选项
程序经常需要设定多个配置。一般分为开发、测试和生产环境
,
它们使用不同的数据库
,
不会彼此影响。
# config.py
"""
存储配置;
"""
import os
# 获取当前项目的绝对路径;
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
"""
所有配置环境的基类, 包含通用配置
"""
SECRET_KEY = os.environ.get('SECRET_KEY') or 'westos secret key' SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = True FLASKY_MAIL_SUBJECT_PREFIX = '[西部开源]' FLASKY_MAIL_SENDER = '976131979@qq.com'
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
""" 开发环境的配置信息 """
# 启用了调试支持,服务器会在代码修改后自动重新载入,并在发生错误时提供一个相当有用的调试 器。
DEBUG = True
MAIL_SERVER = 'smtp.qq.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME') or '976131979'
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD') or '密码'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data- dev.sqlite') class TestingConfig(Config):
""" 测试环境的配置信息 """
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data- test.sqlite')
class ProductionConfig(Config):
""" 生产环境的配置信息 """
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
3、程序工厂函数
1、为什么需要程序工厂函数?
- 在单个文件中开发程序很方便,但却有个很大的缺点,因为程序在全局作用域中创建,所以无法动态修改配置。
- 运行脚本时,程序实例已经创建,再修改配置为时已晚。这一点对单元测试尤其重要,因为有时为了提高测试覆盖度,必须在不同的配置环境中运行程序。
- 这个问题的解决方法是延迟创建程序实例,把创建过程移到可显式调用的工厂函数中。
- 这种方法不仅可以给脚本留出配置程序的时间,还能够创建多个程序实例。
2、如何实现程序工厂函数?
创建扩展类时不向构造函数传入参数, 在之前创建的扩展对象上调用
init_app()
可以完成初始化过程。
不使用程序工厂函数
:
app = Flask(__name__)
bootstrap = Bootstrap(app)
mail = Mail(app)
使用程序工厂函数
:
bootstrap = Bootstrap()
mail = Mail()
def create_app():
app = Flask(__name__)
bootstrap.init_app(app)
mail.init_app(app)
return app
app/__init__.py
文件详细代码如下
:
"""
程序工厂函数, 延迟创建程序实例
"""
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_sqlalchemy import SQLAlchemy
from config import config
bootstrap = Bootstrap()
mail = Mail()
db = SQLAlchemy()
def create_app(config_name='development'):
""" 默认创建开发环境的app对象 """
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
mail.init_app(app)
db.init_app(app)
# 附加路由和自定义的错误页面
# .........后续还需完善, 补充视图和错误页面
return app
4、蓝图: 组件化开发
1)什么是蓝图
?
- Flask蓝图提供了模块化管理程序路由的功能,使程序结构清晰、简单易懂。
- 假如说我们要为某所学校的每个人建立一份档案,一个很自然的优化方式就是这些档案如果能分类管理,就是说假如分为老师、学生、后勤人员等类别,那么后续查找和管理这些档案就方便清晰许多。
- Flask的蓝图就提供了类似“分类”的功能。
2)为什么使用蓝图
?
- 将不同的功能模块化
- 构建大型应用
- 优化项目结构
- 增强可读性,易于维护
3)应用蓝图三部曲
?
蓝图的创建
:
app/auth/__init__.py
# 'auth'是蓝图的名称
# __name__是蓝图所在路径
auth =Blueprint('auth',__name__)
from . import views
蓝图对象上注册路由
,
指定静态文件夹
,
注册模版过滤器
:
app/auth/views.py
@auth.route('/')
def auth_home():
return 'auth_home'
注册蓝图对象
app/__init__.py
# url_prefix: 指定访问该蓝图中定义的视图函数时需要添加的前缀, 没有指定则不加; app.register_blueprint(auth,url_prefix='/auth')
访问网址
http://127.0.0.1:5000/auth/
可以查看到
auth_home
的内容。
任务清单蓝图的应用
auth
蓝图
![](https://i-blog.csdnimg.cn/blog_migrate/38b56243e4e630eea069cd561c524290.png)
蓝图的创建
:
app/auth/__init__.py
from flask import Blueprint
# 实例化一个 Blueprint 类对象可以创建蓝本, 指定蓝本的名字和蓝本所在的包或模块
auth = Blueprint('auth', __name__)
# 把路由和错误处理程序与蓝本关联, 一定要写在最后, 防止循环导入依赖;
from . import views, errors
蓝图对象上注册路由
,
指定静态文件夹
,
注册模版过滤器
:
app/auth/views.py
from . import auth
@auth.route('/login')
def login():
return 'login'
@auth.route('/logout')
def logout():
return 'logout'
注册蓝图对象
app/__init__.py
def create_app(config_name='development'):
"""
默认创建开发环境的app对象
"""
# ...... # 附加路由和自定义的错误页面
from app.auth import auth as auth_bp
app.register_blueprint(auth_bp) # 注册蓝本
from app.todo import todo as todo_bp
app.register_blueprint(todo_bp, ) # 注册蓝本
return app
5、启动脚本
顶级文件夹中的
manage.py
文件用于启动程序。
"""
用于启动程序以及其他的程序任务。
"""
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager, Shell
from app import create_app, db
app = create_app('default')
manager = Manager(app)
migrate = Migrate(app, db)
def make_shell_context():
return dict(app=app, db=db)
# 初始化 Flask-Script、Flask-Migrate 和为 Python shell 定义的上下文。 manager.add_command("shell", Shell(make_context=make_shell_context)) manager.add_command('db', MigrateCommand)
if __name__ == '__main__':
manager.run()
基于
Unix
的操作系统中可以通过下面命令执行脚本
:
# python3 manage.py runserver --help 获取详细使用参数
python3 manage.py runserver
效果如下
:
![](https://i-blog.csdnimg.cn/blog_migrate/eae6aa07e6f6a891ded87c50199392e6.png)
依次访问网址, 如果访问可以显示相关信息, 则成功。
- http://127.0.0.1:5000/login
- http://127.0.0.1:5000/logout
- http://127.0.0.1:5000/todo/add/
- http://127.0.0.1:5000/todo/delete/
6、依赖包文件
程序中必须包含一个 requirements.txt 文件,用于记录所有依赖包及其精确的版本号。
pip freeze > requirements.txt
创建一个和当前环境相同的虚拟环境
,
并在其上运行以下命令
:
pip install -r requirements.txt
7、单元测试
1、什么是单元测试?
单元测试也称之为
“
模块测试
”
,是对程序设计中的最小单元
——
函数进行测试的一种方法,所谓测试,就是验证我们自己编写的方法能不能够得到正确的结果,即用方法得到的结果与真实结果进行比对,这就称之为测试。
2、如何实现单元测试?
python
中有特别多的单元测试框架和工具,
unittest
,
testtools
,
subunit
,
coverage
,testrepository ,
nose, mox
,
mock
,
fixtures
,
discover
等等,先不说如何写单元测试,光是怎么运行单元测试就有N
多种方法。
unittest
,作为标准
python
中的一个模块,是其它框架和工具的基
础。
3、unittest 核心概念及关系
TestCase
的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(
setUp
)
,执行测试代码
(
run
)
,以及测试后环境的还原
(
tearDown
)
。元测试( unit test
)
的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。TestSuite 是多个测试用例的集合,
TestSuite
也可以嵌套
TestSuite
。
TestLoader
是用来加载
TestCase
到
TestSuite
中的,其中有几个
loadTestsFrom__()
方法,就是从各个地方寻找 TestCase
,创建它们的实例,然后
add
到
TestSuite
中,再返回一个TestSuite 实例。TextTestRunner 是来执行测试用例的,其中的
run(test)
会执行
TestSuite/TestCase
中的run(result)方法。TextTestResult 保存测试的结果,包括运行了多少测试用例,成功了多少,失败了多少等信
息。
![](https://i-blog.csdnimg.cn/blog_migrate/b16e649bf2f83c4ee19f46f0a8ce19ad.png)
测试范例
# tests/test_number.py i
mport random
import unittest
"""
单独执行测试用例: python3 -m unittest test_number.py
"""
class TestSequenceFunctions(unittest.TestCase):
"""
setUp() 和 tearDown() 方法分别在各测试前后运行,并且名字以 test_ 开头的函数都作为测试 执行。
"""
def setUp(self):
self.seq = list(range(10))
def test_shuffle(self):
# make sure the shuffled sequence does not lose any elements
random.shuffle(self.seq)
self.seq.sort()
self.assertEqual(self.seq, list(range(10)))
# should raise an exception for an immutable sequence
self.assertRaises(TypeError, random.shuffle, (1, 2, 3))
def test_choice(self):
element = random.choice(self.seq)
self.assertTrue(element in self.seq)
def test_sample(self):
with self.assertRaises(ValueError):
random.sample(self.seq, 20)
for element in random.sample(self.seq, 5):
self.assertTrue(element in self.seq)
def tearDown(self):
del self.seq
单独执行测试用例
:
python3 -m unittest test_number.py
运行结果如下
: