整个流程与Django类似,需要用到Flask—Mail
安装:pip install Flask-Mail
示例
from flask import Flask
from flask_mail import Mail, Message
app = Flask(__name__)
#配置邮件:服务器/端口/传输层安全协议/邮箱名/密码
app.config.update(
DEBUG = True,
MAIL_SERVER='smtp.qq.com',
MAIL_PROT=465,
MAIL_USE_TLS = True,
MAIL_USERNAME = '[email protected]',
MAIL_PASSWORD = 'xxxx',
)
mail = Mail(app)
@app.route('/')
def index():
# sender 发送方,recipients 接收方列表
msg = Message("This is a test ",sender='[email protected]', recipients=['[email protected]','[email protected]'])
#邮件内容
msg.body = "Flask test mail"
#发送邮件
mail.send(msg)
print "Mail sent"
return "Sent Succeed"
if __name__ == "__main__":
app.run()
app = Flask(__name__)
的app与Django中的app并不是同个概念,它其实相当于的Django中的project,而蓝图简单来说就是用于实现单个应用的视图、模块文件、静态文件的集合我们可以像Django一样,把main.py中关于各模块的视图函数等文件都分别放进对应的应用模块的文件夹中
然后在main.py中,导入对应的视图函数
from orders import get_order
from users import register
但运行时,你会看到一个不能导入的异常错误
其原因其实类似于进程中的死锁,在main.py中,执行导入时,它会去对应的模块寻找视图函数,找到后,对应的模块又会执行,而模块里又需要从mian.py导入app对象,这样两个文件的执行都相当于卡在了开头,谁都执行不下去,僵持住了,这就是循环导入的问题
其一种解决方法就是错开,推迟一方的导入,让另一方先完成
用装饰器来解决循环导入的问题
装饰器除了在函数定义前用,也可以在函数定义后用
@app.route('/get_goods)')
def get_goods():
pass
######################
def get_goods():
pass
app.route('/get_goods)')(get_goods)
app.route('/get_goods)')
相当于一个函数,要被定义的函数可以当作参数传给它这时,最后一条语句就可以扔进主程序了,对应的模块不需要导入app,循环导入的问题自然就解决了
用上面的方法虽然解决了循环导入的问题,但是模块与主程序的耦合度还是相当的高
这时就可以使用蓝图来解决问题
使用
创建蓝图对象及视图函数
from flask import Blueprint
# 1. 创建蓝图对象
app_goods = Blueprint("goods", __name__) # goods为自定义的名字
# 2. 创建蓝图的视图函数
@app_goods.route("/get_goods")
def get_goods():
return "get goods page"
在主程序中注册蓝图
# 注册蓝图
from goods import app_goods
app.register_blueprint(app_goods)
以目录的形式定义蓝图
在Django中,每个页面可能会有一个前缀,如:/goods/get_goods
,在Flask中也可以实现,只要在注册时加多一个参数url_prefix
app.register_blueprint(app_goods, url_prefix="/goods")
这时,方便代码的管理与维护,我们可以在项目中为每个应用模块单独创建一个应用包,其中可以存放该应用模块的模板文件、静态文件、视图函数等
在包的__init__.py
文件中
from flask import Blueprint
# 创建蓝图
app_goods = Blueprint("goods", __name__)
# 在__init__.py被执行时,把视图函数也加载进来,让蓝图与应用程序知道有视图函数的存在,让其产生关联
from .views import get_goods
这时,在主程序注册蓝图导入模块时,就应该是从应用包中导入
from goods import app_goods
蓝图模板目录的处理
这时如果把模板文件放在应用模块文件下的模板文件夹中,是无法让视图函数找到的,需要在__init__.py
中注册时指定template_folder
app_goods = Blueprint("goods", __name__, template_folder="templates")
这时,视图函数寻找模板文件时的顺序为:项目目录中的模板文件夹>蓝图中的模板文件夹
单元测试的相当于与一些**断言(assert)**代码
assert后面是一个表达式,如果返回真,则断言成功,程序继续执行,如果返回假,则断言失败,抛出AssertionError异常,程序中止
测试其实有点像爬虫,让测试程序向程序的接口发送请求,然后看其返回的数据是否与预期的一致
常用的断言方法
断言 | 作用 |
---|---|
assertEqual | 如果两个值相等,则pass |
assertNotEqual | 如果两个值不相等,则pass |
assertTrue | 判断bool值为True,则pass |
assertFalse | 判断bool值为False,则pass |
assertIsNone | 不存在,则pass |
assertIsNotNone | 存在,则pass |
单元测试的基本写法
新建一个文件来写测试程序,在该文件中定义一个测试类,继承于unittest.TestCase
import unittest
class TestClass(unitest.TestCase):
'''测试xxx'''
def test_xxx(self): # 写测试代码的方法一定要以test开头,否则不会被识别
pass
if __name__ == '__main__':
unittest.main() # 用unittest来启动
setUp()
与tearDown()
def setUp(self)
:该函数会在进行测试前先被执行,可以把初始化的东西扔进去,如:client = app.test_client()
def tearDown(self)
:该函数会在测试代码执行完成后被执行登录测试案例
import unittest
from login import app
import json
class TestLogin(unittest.TestCase):
"""定义测试案例"""
def setUp(self):
"""在执行具体的测试方法前,先被调用"""
# 可以使用python的http标准客户端进行测试
# urllib urllib2 requests
# 使用flask提供的测试客户端进行测试
self.client = app.test_client()
def test_empty_name_password(self):
"""测试模拟场景,用户名或密码不完整"""
# 使用客户端向后端发送post请求, data指明发送的数据,会返回一个响应对象
response = self.client.post("/login", data={})
# respoonse.data是响应体数据
resp_json = response.data
# 按照json解析
resp_dict = json.loads(resp_json)
# 使用断言进行验证
self.assertIn("code", resp_dict)
code = resp_dict.get("code")
self.assertEqual(code, 1)
# 测试只传name
response = self.client.post("/login", data={"name": "admin"})
# respoonse.data是响应体数据
resp_json = response.data
# 按照json解析
resp_dict = json.loads(resp_json)
# 使用断言进行验证
self.assertIn("code", resp_dict)
code = resp_dict.get("code")
self.assertEqual(code, 1)
def test_wrong_name_password(self):
"""测试用户名或密码错误"""
# 使用客户端向后端发送post请求, data指明发送的数据,会返回一个响应对象
response = self.client.post("/login", data={"name": "admin", "password": "python"})
# respoonse.data是响应体数据
resp_json = response.data
# 按照json解析
resp_dict = json.loads(resp_json)
# 使用断言进行验证
self.assertIn("code", resp_dict)
code = resp_dict.get("code")
self.assertEqual(code, 2)
if __name__ == '__main__':
unittest.main()
发送邮件测试
import unittest
from apps import app
class TestCase(unittest.TestCase):
# 创建测试环境,在测试代码执行前执行
def setUp(self):
self.app = app
# 激活测试标志
app.config['TESTING'] = True
self.client = self.app.test_client()
# 在测试代码执行完成后执行
def tearDown(self):
pass
# 测试代码
def test_email(self):
resp = self.client.get('/')
print resp.data
self.assertEqual(resp.data,'Sent Succeed')
数据库测试
import unittest
from author_book import Author, db, app
class TestDatabase(unittest.TestCase):
"""测试数据库的案例"""
def setUp(self):
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:[email protected]:3306/flask_test"
db.drop_all()
db.create_all()
def test_author(self):
"""测试添加作者的案例"""
author = Author(name="itcast", email="[email protected]")
db.session.add(author)
db.session.commit()
ret_author = Author.query.filter_by(name="itcast").first()
self.assertIsNotNone(ret_author)
self.assertEqual(ret_author.name, "itcast")
def tearDown(self):
"""在所有测试方法执行后,被调用"""
# 清除记录的测试任务
db.session.remove()
# 清除数据库数据
db.drop_all()
if __name__ == '__main__':
unittest.main()
测试模式
在此模式下,要测试的程序出现异常,在测试端也能知道错误异常出现在哪里
app.testing = True
开发时,我们都是使用的Flask自带的web服务器,但在实际生产环境中,其性能是无法满足要求的
可以采用Gunicorn做wsgi容器,来部署程序
区分几个概念:
web开发中,部署方式大致类似。简单来说,前端代理使用Nginx主要是为了实现分流、转发、负载均衡,以及分担服务器的压力。Nginx部署简单,内存消耗少,成本低。Nginx既可以做正向代理,也可以做反向代理
安装:pip install gunicorn
使用
gunicorn -w 4 -b 127.0.0.1:5001 --access-logfile ./logs/log main:app
-w
:work 指定开启的进程数
-b
:指定绑定到哪个IP和端口
--access-logfile
:把用户的请求信息放进后面指定的日志文件,该日志文件需自己去创建
main:app
:主程序所在的文件:和Flask实例名
守护进程:之前程序运行都是在终端,终端退出程序就挂了,所以这是需要让程序以后台的方式去运行,即以守护进程的方式去运行,在上面命令启动是加多一个参数 -D
gunicorn -w 4 -b 127.0.0.1:5001 -D --access-logfile ./logs/log main:app
配置nginx的配置文件(/usr/local/nginx/
)
upstream flask{
server IP:port
server xxxxx:xxxx
}
server {
# 监听80端口
listen 80;
# 本机
server_name localhost;
# 默认请求的url
location / {
# 请求转发到gunicorn服务器
# proxy_pass http://127.0.0.1:5001;
proxy_pass http://flask;
# 设置请求头,并将头信息传递给服务器端
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
upstream
:相当于指明nginx可以识别的ip和端口
flask
:为下面ip组定义的名字,下面请求转发指明ip时,就可以把ip组放后面,然后nginx会把请求均衡的分配给里面的ip服务器
END~