文本用flask写个博客系统,源码带有详细注释,通俗易懂,拿去就能用。点 这里 查看演示。博客效果如下,博客首页:
这个博客麻雀虽小,但五脏俱全。有如下功能:
- 博客文章浏览
- 用户注册
- 用户登录/登出
- 发文章/修改文章/删除文章
为了简单,没有用MySQL数据,使用了Sqlite数据库。博客来自flask官方教程,用来学习前后端开发,基于模板的后端开发,也能够学习python后端框架flask。因为例子够简单,适合初学者入门,当然前提是,你得有python编程基础。好了,项目的源代码已经传到这里了 flaskr,打开连接看readme.md文档,就知道怎么部署这个博客了(部署就是把博客跑起来!)。源代码添加了详细的注释,方便初学的朋友学习食用。
比如flaksr的主文件:
import os
from flask import Flask
# 应用工厂函数
def create_app(test_config=None):
# 设置配置相对于实例文件夹(instance folder),
# flask从实例文件夹读取配置,而不是flask应用的根目录,
# __name__是让flask知道自己的位置以设置一些路径,如应用程序的根目录和静态文件目录
app = Flask(__name__, instance_relative_config=True)
# 设置app默认配置
app.config.from_mapping(
# 保护flask和扩展(extensions)的数据安全
SECRET_KEY='dev',
# sqlite文件保存路径
DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
)
# 加载真正的配置
if test_config is None:
# config.py文件也存放在实例文件夹中
app.config.from_pyfile('config.py', silent=True)
else:
# 加载测试配置
app.config.from_mapping(test_config)
# 创建实例文件夹
try:
os.makedirs(app.instance_path)
except OSError:
pass
# 一个简单的路由
@app.route('/hello')
def hello():
return 'Hello, World!'
# 初始化数据库表
from . import db
db.init_app(app)
# 注册认证(auth)蓝图
from . import auth
app.register_blueprint(auth.bp)
# 注册博客(写博文、看博文等)蓝图
from . import blog
app.register_blueprint(blog.bp)
# 添加一条映射关系:/ -> index,
# 让url_for('index')返回/,让url_for('blog.index')也返回/(即blog.py的@bp.route('/'))
app.add_url_rule('/', endpoint='index')
return app
# 进入flask_tutorial目录,运行app并开启调试模式:
# flask --app flaskr run --debug
# 进入flask_tutorial目录,初始化数据库表:
# flask --app flaskr init-db
还带有pytest测试目录tests
,比如tests/test_auth.py
文件用来测试认证相关的功能:
import pytest
from flask import g, session
from flaskr.db import get_db
def test_register(client, app):
# 测试获取注册页是否符合预期
assert client.get('/auth/register').status_code == 200
# 测试提交表单给注册页
response = client.post(
# 用字典形式传递数据,测试客户端会转换为表单数据给视图函数
'/auth/register', data={'username': 'a', 'password': 'a'}
)
# 如果注册成功,会跳转到登录页,此时响应头中会有Location字段
assert response.headers['Location'] == '/auth/login'
with app.app_context():
assert get_db().execute(
'SELECT * FROM user WHERE username="a"',
).fetchone() is not None
# 使用pytest参数化测试装饰器,
# 第一个参数指定被装饰的test_register_validate_input测试用例会有三个参数,
# 测试用例的client参数是fixture装饰过的函数,它有参数app,app也是被fixture
# 装饰过的函数,因此pytest会依次:调用app->返回值给client->调用client->
# 返回值给test_register_validate_input。
# 第二个参数元组中有三组数据,pytest会分别传递给测试用例,以便把三种情况都测试一遍,
# 利用参数化测试的好处是不用把测试用例test_register_validate_input重复写三遍
@pytest.mark.parametrize(
('username', 'password', 'message'), (
('', '', b'Username is required.'),
('a', '', b'Password is required.'),
('test', 'test', b'already registered'),
)
)
def test_register_validate_input(client, username, password, message):
response = client.post(
'/auth/register',
data={'username': username, 'password': password}
)
# response.data是bytes类型,
# 测试注册失败的各种情况,看响应体中是否包含对应的错误提示信息
assert message in response.data
def test_login(client, auth):
assert client.get('/auth/login').status_code == 200
response = auth.login() # 登录 test/test 用户
assert response.headers['Location'] == '/'
# 在请求之外访问 session 会引发错误,所以使用with上下文
with client:
client.get('/')
assert session['user_id'] == 1
assert g.user['username'] == 'test'
@pytest.mark.parametrize(
('username', 'password', 'message'), (
('a', 'test', b'Incorrect username.'),
('test', 'a', b'Incorrect password.'),
)
)
def test_login_validate_input(auth, username, password, message):
response = auth.login(username, password)
assert message in response.data
def test_logout(client, auth):
auth.login()
with client:
auth.logout()
assert 'user_id' not in session
所有的文件,包括源码文件,测试文件,部署说明都在 这里 了。
部署到云服务器,可以用nginx做web服务器,博客代码跑在wsgi服务器上,比如waitress,或者Gunicorn等等。
nginx配置文件可以这样写,创建一个文件flaskr.conf,内容如下:
server {
listen 80 default_server;
server_name _;
# 把所有http请求永久重定向到https
rewrite ^/(.*) https://$host/$1 permanent;
}
server {
listen 443 ssl;
server_name fk.izhaojie.com;
index index.html;
# f这是最关键的部分,让nginx把请求转发给flask后端,由wsgi服务处理
location / {
proxy_pass http://127.0.0.1:8080;
}
}
完!