社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
《Flask Web开发:基于Python的Web应用开发实战》
virtualenv venv
venvScriptsactivate.bat
(venv) $ pip freeze >requirements.txt
(venv) $ pip install -r requirements.txt
pip list --outdated
pip install --upgarade <Package1> <PackageN>
git tag 列出所有打tag的分支
git checkout <tag_name> 切换到tag
git reset --hard 不保留修改
.gitignore:指定哪些文件或目录不作同步,比如 ./venv/,*.pyc,数据库文件.sqlite3, .mysql# -*- coding: utf-8 -*-
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
修饰器是Python语言的标准特性,可以使用不同的方式修改函数的行为。惯常用法是使用修饰器把函数注册为事件的处理程序。@app.route('/user/<name>')
def user(name):
return '<h1>Hello, %s!</h1>' %name
@app.route('/user/<int:id>') # 不能有空格!
def ...
app.run(debug=True, port=7777)
app.url_map
Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
HEAD、Options、GET是请求方法,由路由进行处理。Flask为每个路由都指定了请求方法,这样不同的请求方法发送到相同的URL上时,会使用不同的视图函数进行处理。HEAD和OPTIONS方法由Flask自动处理response = make_response('<h1>This document carries a cookie!</h1>', 200)
response.set_cookie('answer', '42')
return response
@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)
模板变量<p>A value from a dictionary: {{ mydict['key'] }}.</p>
<p>A value from a list: {{ mylist[3] }}.</p>
<p>A value from a list, with a variable index: {{ mylist[myintvar] }}.</p>
<p>A value from an object's method: {{ myobj.somemethod() }}.</p>
Hello, {{ name|capitalize }
<html>
<head>
{%block head %}
<title>{%block title %}{%endblock %} - My Application</title>
{%endblock %}
</head>
<body>
{%block body %}
{%endblock %}
</body>
</html>
extends指令声明这个模板衍生自base.html。在extends指令之后,基模板中的3个块被重新定义,模板引擎会将其插入适当的位置。注意新定义的head块,在基模板中其内容不是空的,所以使用super()获取原来的内容(向已经有内容的块中添加新内容)。{%extends "base.html" %}
{%block title %}Index{%endblock %}
{%block head %}
{{ super() }}
<style>
</style>
{%endblock %}
{%block body %}
<h1>Hello, World!</h1>
{%endblock %}
使用Flask-Bootstrap{% block head %}
{{super()}}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap-theme.min.css">
{% endblock %}
。。。
{% block scripts %}
{{super()}}
<script src="//cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script src="//cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
{{ moment.include_moment() }}
{{ moment.lang("zh-CN") }}
{% endblock %}
2016-10-20: 以上本地加速证明是有误的!会重复下载本地和官方Bootstrap 的 css/js
解决:
/app/__init__.py
from flask_bootstrap import Bootstrap, WebCDN, ConditionalCDN, BOOTSTRAP_VERSION, JQUERY_VERSION, HTML5SHIV_VERSION, RESPONDJS_VERSION
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
def change_cdn_domestic(tar_app):
static = tar_app.extensions['bootstrap']['cdns']['static']
local = tar_app.extensions['bootstrap']['cdns']['local']
def change_one(tar_lib, tar_ver, fallback):
tar_js = ConditionalCDN('BOOTSTRAP_SERVE_LOCAL', fallback,
WebCDN('//cdn.bootcss.com/' + tar_lib + '/' + tar_ver + '/'))
tar_app.extensions['bootstrap']['cdns'][tar_lib] = tar_js
libs = {'jquery': {'ver': JQUERY_VERSION, 'fallback': local},
'bootstrap': {'ver': BOOTSTRAP_VERSION, 'fallback': local},
'html5shiv': {'ver': HTML5SHIV_VERSION, 'fallback': static},
'respond.js': {'ver': RESPONDJS_VERSION, 'fallback': static}}
for lib, par in libs.items():
change_one(lib, par['ver'], par['fallback'])
change_cdn_domestic(app)
# 。。。
return app
另外:本地加速 moment.js
这个文件也在国外服务器,访问很慢,而且 size=160KB
/app/templates/base.html
{% block scripts %} {{ super() }} {{ moment.include_moment(local_js="/static/moment-with-locales.min.js") }} {{ moment.lang('zh-CN') }} {% endblock %}
需要单独下载 https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment-with-locales.min.js 到本地目录 /app/static/
自定义错误页面
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
url_for() 链接辅助函数{%block scripts %}
{{ super() }}
{{ moment.include_moment() }}
<!--使用中文,默认是英语的-->
{{ moment.lang("zh-CN") }}
{%endblock %}
from flask_wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class NameForm(Form):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
password = PasswordField('Password', validators=[DataRequired()], render_kw={"placeholder": u"密码"})
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
MySQLdb 中文乱码的处理:
conn = MySQLdb.connect(host='localhost', user='root', passwd='XXX', db='app_englishgo', charset = 'utf8')
显示:title.encode('gbk')
接收输入:unicode(request.form['title'])
Relationship 关系型数据库
class Role(db.Model):
# ...
users = db.relationship('User', backref='role') # 面向对象视角
class User(db.Model):
# ...
role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) # 定义外键
engine = create_engine('sqlite:///C:\Temp\testsqlalchemy.db', encoding='utf8', convert_unicode=True, echo=True)
# echo:显示出内部过程及SQL语句。debug或学习时打开
# encoding:防止乱码
'mysql://uid:pwd@localhost/mydb?charset=utf8'
集成Python shell
每次启动 shell 会话都要导入数据库实例和模型。为避免重复导入,我们可以做些配置,让 Flask-Script 的 shell 命令自动导入特定的对象。为 shell 命令注册一个 make_context 回调函数
新数据库迁移 flask-migrate
由于模型中经常会新加一行或几行column (比如用来保存账户的确认状态),此时要修改 models.py,并执行一次新数据库迁移
MySQL Workbench 自动产生EER,可以清楚地看到各个表格之间关系:一对多 Foreign_Key、Index等
1) config.py:
class DevelopmentConfig(Config):
DEBUG = True
# SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
SQLALCHEMY_DATABASE_URI = 'mysql://USER:PASSWORD@localhost:3306/flaskr'
2) python manage.py deploy
3) MySQL Workbench: Database -> Reverse Engineer -> 选择 connection -> database -> 一路 Next
app.config['MAIL_SERVER'] = 'smtp.163.com'
app.config['MAIL_PORT'] = 25
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]'
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <flasky@example.com>'
app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN')
mail = Mail(app)
def sendmail(mail):
msg = Message('test subject', sender='ezhqing@163.com', recipients = ['XXX@qq.com'])
msg.body = 'text body'
msg.html = '<b>HTML</b> body'
with app.app_context():
mail.send(msg)
千万不要把账户密令直接写入脚本,特别是当你计划开源自己的作品时。让脚本从本机环境中导入敏感信息(venv) $ set MAIL_USERNAME=<your mail username>
(venv) $ set MAIL_PASSWORD=<mail password>
|-flasky
|-app/ Flask 程序一般都保存在名为 app 的程序包中
|-templates/ templates 和 static 文件夹是程序包的一部分
|-static/
|-main/ 程序包中创建了一个子包,用于保存蓝本。/main/__init__.py脚本的末尾导入views.py & errors.py,避免循环导入依赖
|-__init__.py 程序工厂函数 create_app(),注册蓝本
|-errors.py 错误处理路由. 注册程序全局的错误处理程序,必须使用 app_errorhandler
|-forms.py 表单对象
|-views.py 路由。蓝本中的全部端点会加上一个命名空间,如 url_for('main.index')
|-__init__.py
|-email.py 电子邮件支持函数
|-models.py 数据库模型
|-migrations/ 数据库迁移脚本
|-tests/ 单元测试
|-__init__.py 文件可以为空,因为 unittest 包会扫描所有模块并查找测试
|-test*.py
|-venv/ 虚拟环境
|-requirements.txt 列出了所有依赖包,便于在其他电脑中重新生成相同的虚拟环境
|-config.py 存储配置。开发、测试和生产环境要使用不同的数据库
|-manage.py 用于启动程序以及其他的程序任务
<ul class="nav navbar-nav navbar-right">
{% if current_user.is_authenticated %}
<li><a href="{{ url_for('auth.logout') }}" title = "邮件 {{ current_user.email[:9]+'...' }}">Log Out {{ current_user.username }}</a></li>
{% else %}
<li><a href="{{ url_for('auth.login') }}">Log In</a></li>
{% endif %}
</ul>
user = User.query.filter_by(email=form.email.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user, form.remember_me.data)
return redirect(request.args.get('next') or url_for('main.index'))
>>> from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
>>> s = Serializer(app.config['SECRET_KEY'], expires_in = 3600)
>>> token = s.dumps({ 'confirm': 23 })
>>> token
'eyJhbGciOiJIUzI1NiIsImV4cCI6MTM4MTcxODU1OCwiaWF0IjoxMzgxNzE0OTU4fQ.ey ...'
>>> data = s.loads(token)
>>> data
{u'confirm': 23}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img height="24px" src="{{ url_for('static', filename='avatar/')}}{{ current_user.avatar_hash }}">
def gravatar(self, size=100, default='identicon', rating='g'):
import random
return '%.3d.jpg' % random.randint(1, XXX)
<div class="page-header">
<img class="img-rounded profile-thumbnail" src="{{ url_for('static', filename='avatar/')}}{{ current_user.avatar_hash }}"">
<li class="post">
<div class="post-thumbnail">
<a href="{{ url_for('.user', username=post.author.username) }}">
<img height="40px" class="img-rounded profile-thumbnail" src="{{ url_for('static', filename='avatar/')}}{{ post.author.avatar_hash }}">
<div class="comment-thumbnail">
<a href="{{ url_for('.user', username=comment.author.username) }}">
<img height="40px' class="img-rounded profile-thumbnail" src="{{ url_for('static', filename='avatar/')}}{{ post.author.avatar_hash }}">
python manage.py shell
>>> User.generate_fake()
>>> Post.generate_fake(200)
@manager.command
def deploy():
"""Run deployment tasks."""
from flask.ext.migrate import upgrade
from app.models import Role, User
# migrate database to latest revision
upgrade()
# create user roles
Role.insert_roles()
# create self-follows for all users
User.add_self_follows()
# 测试:User.generate_fake(100)
# Post.generate_fake(500)
Write a Tumblelog Application with Flask and MongoEngine
这是MongoDB官方文档中的一个教程,也是学习Flask开发的一个很好案例,尤其适合Flask+MongoDB开发的应用场景
The Hitchhiker’s Guide to Python!
这个资料虽然不直接与Flask有关,但对初学者,绝对有学习的价值
GitHub - humiaozuzu/awesome-flask: A curated list of awesome Flask resources and plugins
https://spacewander.github.io/explore-flask-zh/7-blueprints.html
什么是蓝图?
一个蓝图定义了可用于单个应用的视图,模板,静态文件等等的集合。举个例子,想象一下我们有一个用于管理面板的蓝图。这个蓝图将定义像/admin/login和/admin/dashboard这样的路由的视图。它可能还包括所需的模板和静态文件。你可以把这个蓝图当做你的应用的管理面板,管它是宇航员的交友网站,还是火箭推销员的CRM系统。
蓝图的杀手锏是将你的应用组织成不同的组件。假如我们有一个微博客,我们可能需要有一个蓝图用于网站页面,比如index.html和about.html。然后我们还需要一个用于在登录面板中展示最新消息的蓝图,以及另外一个用于管理员面板的蓝图。站点中每一个独立的区域也可以在代码上隔绝开来。最终你将能够把你的应用依据许多能完成单一任务的小应用组织起来。
Flask.register_blueprint()
注册它。pip install flask-sqlalchemy
pip install flask-login
...
from flask.ext.bootstrap import Bootstrap
bootstrap = Bootstrap(app)
然后 /template/目录下创建自己的 html,{% extends "bootstrap/base.html" %}
模板:Libsite-packagesflask_bootstraptemplatesbootstrap
Bootstrap 导航栏 自定义配色:
{% block styles %}from flask_debugtoolbar import DebugToolbarExtension
toolbar = DebugToolbarExtension()
toolbar.init_app(app)
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!