路由
route() 装饰器把一个函数绑定到对应的 URL 上。
要给 URL 添加变量部分,你可以把这些特殊的字段标记为 , 这个部分将会作为命名参数传递到你的函数。规则可以用 指定一个可选的转换器。
转换器有下面几种:
- int 接受整数
- float 同 int ,但是接受浮点数
- path 和默认的相似,但也接受斜线
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return 'Post %d' % post_id
唯一 URL / 重定向行为
Flask 的 URL 规则基于 Werkzeug 的路由模块。这个模块背后的思想是基于 Apache 以及更早的 HTTP 服务器主张的先例,保证优雅且唯一的 URL。
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
虽然它们看起来着实相似,但它们结尾斜线的使用在 URL 定义 中不同。 第一种情况中,指向 projects 的规范 URL 尾端有一个斜线。这种感觉很像在文件系统中的文件夹。访问一个结尾不带斜线的 URL 会被 Flask 重定向到带斜线的规范 URL 去。
然而,第二种情况的 URL 结尾不带斜线,类似 UNIX-like 系统下的文件的路径名。访问结尾带斜线的 URL 会产生一个 404 “Not Found” 错误。
构造 URL
用 url_for() 来给指定的函数构造 URL。它接受函数名作为第一个参数,也接受对应 URL 规则的变量部分的命名参数。未知变量部分会添加到 URL 末尾作为查询参数。
>>> from flask import Flask, url_for
>>> app = Flask(__name__)
>>> @app.route('/')
... def index(): pass
...
>>> @app.route('/login')
... def login(): pass
...
>>> @app.route('/user/<username>')
... def profile(username): pass
...
>>> with app.test_request_context():
... print url_for('index')
... print url_for('login')
... print url_for('login', next='/')
... print url_for('profile', username='John Doe')
...
/
/login
/login?next=/
/user/John%20Doe
为什么你要构建 URL 而非在模板中硬编码?这里有三个绝妙的理由:
- 反向构建通常比硬编码的描述性更好。更重要的是,它允许你一次性修改 URL, 而不是到处边找边改。
- URL 构建会转义特殊字符和Unicode 数据,免去你很多麻烦。
如果你的应用不位于 URL 的根路径(比如,在 /myapplication 下,而不是 /), url_for() 会妥善处理这个问题。
HTTP 方法
HTTP (与 Web 应用会话的协议)有许多不同的访问 URL 方法。默认情况下,路由只回应 GET 请求,但是通过 route() 装饰器传递 methods 参数可以改变这个行为。这里有一些例子:
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
do_the_login()
else:
show_the_login_form()
HTTP 方法(也经常被叫做“谓词”)告知服务器,客户端想对请求的页面 做 些什么。下面的都是非常常见的方法:
GET
浏览器告知服务器:只 获取 页面上的信息并发给我。这是最常用的方法。
HEAD
浏览器告诉服务器:欲获取信息,但是只关心 消息头 。应用应像处理 GET 请求一样来处理它,但是不分发实际内容。在 Flask 中你完全无需 人工 干预,底层的 Werkzeug 库已经替你打点好了。
POST
浏览器告诉服务器:想在 URL 上 发布 新信息。并且,服务器必须确保 数据已存储且仅存储一次。这是 HTML 表单通常发送数据到服务器的方法。
PUT
类似 POST 但是服务器可能触发了存储过程多次,多次覆盖掉旧值。你可 能会问这有什么用,当然这是有原因的。考虑到传输中连接可能会丢失,在 这种 情况下浏览器和服务器之间的系统可能安全地第二次接收请求,而 不破坏其它东西。因为 POST 它只触发一次,所以用 POST 是不可能的。
DELETE
删除给定位置的信息。
OPTIONS
给客户端提供一个敏捷的途径来弄清这个 URL 支持哪些 HTTP 方法。 从 Flask 0.6 开始,实现了自动处理。
上下文
应用上下文
应用上下文的作用
应用上下问存在的主要原因是,在过去,请求上下文被附加了一堆函数,但是又没有什么好的解决方案。因为 Flask 设计的支柱之一是你可以在一个 Python 进程中拥有多个应用。
使用 current_app 代理对象,它被绑定到当前请求的应用的引用。既然无论如何在没有请求时创建一个这样的请求上下文是一个没有必要的昂贵操作,应用上下文就被引入了。
上下文用法
上下文的一个典型应用场景就是用来缓存一些我们需要在发生请求之前或者要使用的资源。举个例子,比如数据库连接。当我们在应用上下文中来存储东西的时候你得选择一个唯一的名字,这是因为应用上下文为 Flask 应用和扩展所共享。
最常见的应用就是把资源的管理分成如下两个部分:
- 一个缓存在上下文中的隐式资源
- 当上下文被销毁时重新分配基础资源
通常来讲,这将会有一个 get_X() 函数来创建资源 X ,如果它还不存在的话。 存在的话就直接返回它。另外还会有一个 teardown_X() 的回调函数用于销毁资源 X 。
请求上下文
请求上下文内部工作如同一个栈。栈顶是当前活动的请求。
上下文不是很理解,希望后面可以慢慢搞懂
先写配置文件config.py
#coding=utf-8
class Config(object):
@staticmethod
def init_app(app):
pass
class ProdConfig(object):
pass
class DevConfig(Config):
DEBUG=True #启用调试
SQLALCHEMY_DATABASE_URI="mysql+pymysql://web:web@localhost/r"
SQLALCHEMY_TRACK_MODIFICATIONS = False
config={
'development':DevConfig,
'production':ProdConfig,
'default':DevConfig
}
app/_ init _.py
# -*- coding:utf-8 -*-
from flask import Flask
from config import config
from flask_sqlalchemy import SQLAlchemy
db=SQLAlchemy()
def create_app(config_name):
app=Flask(__name__)
app.config.from_object(config[config_name]) #加载配置
config[config_name].init_app(app)
db.init_app(app)
return app
app/models.py
from flask import current_app
from . import db
from datetime import datetime
class User(db.Model):
__tablename__='users'
id=db.Column(db.Integer(),primary_key=True)
username=db.Column(db.String(255),unique=True,index=True)
password=db.Column(db.String(255))
posts=db.relationship('Post',backref='user',lazy='dynamic')
def __init__(self,username):
self.username=username
def __repr__(self):
return "<User '{}'>".format(self.username)
class Post(db.Model):
__tablename__='posts'
id=db.Column(db.Integer(),primary_key=True)
text=db.Column(db.Text())
title=db.Column(db.String(255))
publish_date=db.Column(db.DateTime(),index=True,default=datetime.utcnow)
user_id=db.Column(db.Integer(),db.ForeignKey('users.id'))
def __init__(self,title):
self.title=title
def __repr__(self):
return "<Post '{}'>".format(self.title)
manage.py
# -*- coding:utf-8 -*-
from flask_script import Manager,Shell
from app import create_app,db
from app.models import User,Post
app=create_app('default')
manager=Manager(app)
#manager.add_command("server",Server())
def make_shell_context(): #创建一个Python命令行,并在应用上下文中执行
return dict(app=app,db=db,User=User,Post=Post)
manager.add_command("shell",Shell(make_context=make_shell_context))
if __name__=='__main__':
manager.run()
蓝图实现了应用的模块化,让应用层次清晰,开发者可以更容易的开发和维护项目,蓝图作用于相同的URL前缀,比如/user/:id,/user/profile这样的地址
这里先写main的蓝图
app/main/_ init _.py
#coding=utf-8
'''main蓝图'''
from flask import Blueprint
main=Blueprint('main',__name__)
from . import views,errors
app/_ init _.py 注册蓝图
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
errorhandler(code_or_exception )饰器,用于注册一个给出错误代码的功能。例:
@app.errorhandler(404)
def page_not_found(error):
return 'This page does not exist', 404
您也可以注册处理程序以进行任意异常:
@app.errorhandler(DatabaseError)
def special_exception_handler(error):
return 'Database connection failed', 500
您也可以将函数注册为错误处理程序,而不使用errorhandler()装饰器。以下示例相当于上述示例:
def page_not_found(error):
return 'This page does not exist', 404
app.error_handler_spec[None][404] = page_not_found
app/main/errors.py
from flask import render_template
from . import main
@main.app_errorhandler(404)
def page_not_found(e):
return render_template('404.html'),404
@main.app_errorhandler(500)
def internal_server_error(e):
return render_template('500.html'),500
如果使用errorhandler修饰器,那么只有蓝本中的错误才能触发处理程序,要想注册程序全局的错误处理程序,必须使用app_errorhandler