Micro means Flask aims to keep the core simple but extensible.
Flask won't make any decisions for you(database, template, form validation, authorization, uploading file, etc), everything is up to you. so flask can be anything you need and nothing you don't.
conventions:
static files and templates are stored in application's subdirectories within the application's Python source code tree, named static and templates, and this can be changed if you want to.
class Flask is designed to inherit, so you can customize your application behavior.
Thread-locals in Flask:
One of the design decisions in Flask was that simple tasks should be simple; they should not take a lot of code and yet they should not limit you. Because of that, Flask has few design choices that some people might find surprising or unorthodox. For ex- ample, Flask uses thread-local objects internally so that you don’t have to pass objects around from function to function within a request in order to stay threadsafe. This approach is convenient, but requires a valid request context for dependency injection or when attempting to reuse code which uses a value pegged to the request. The Flask project is honest about thread-locals, does not hide them, and calls out in the code and documentation where they are used.
Always build web application with CAUTION:
Flask and Jinja2 protect you from XSS
Installation:
Flask depends on Werkzeug and Jinja2. Werkzeug is a toolkit fo WSGI, the standard Python interface between web applications and variety of servers for both development and deployment.
virtualenv:
a really nice tool.
in virtualenv mode:
pip install Flask
or system-wide installation:
sudo pip install Flask
Quick start:
hello-word:
from flask import Flask
##__name__ is needed for Flask to know where is current module or package is,
##and where to find static and templates.
app = Flask(__name__)
@app.route("/")
def hello_world():
return "Hello world"
if __name__ == "__main__":
app.run()
now access 192.168.1.1:5000 you will get "Hello world". but the application is only accessible in your computer, use app.run(host=0.0.0.0) to tell the operation system to listen on all public IPs.
Debug Mode:
debug mode let Flask restart the web application automatically when detects source code changes.
two ways to implement:
1. app.debug = True
2. app.run(debug=True)
Routing: to build beautiful URLs.
@app.route("/")
@app.route("/projects/"): access /projects will to /projects/, projects looks like a directory
@app.route("/about"): access /about/ will 404, about behaves like a file
@app.route("/post/<post_id>/"): access post_id as dynamic argument
@app.route("/user/<int:user_id>/"): receive int user_id, and user_id as the function argument, int is converter
@app.route("/user/<path:url>"): url can be abc, or ab/c, path converter accepts slashes.
existing converters:
1. int:
2. float:
3. path: like the default converter but accepts slashes.
URL Building:
url_for方法能输出指定controller方法对应的url,为什么不在template里直接使用硬编码的url而要使用url_for方法呢?
1. url_for比硬编码的url更具有描述性(更好记)(可我没发现),而且,当需要修改一个url时,只需在controller定义接口的地方一处修改即可
2. url_for会为你处理转义
3. 如果你的应用程序是某个大应用的一个模块,url_for会自动为你处理,比如豆瓣小组,单独运行会是"/", 但如果放在豆瓣这个大的application下,url_for会生成"douban.com/group/"
代码:
with app.test_request_context():
print url_for('projects')
print url_for('url', url='ab/c')
print url_for('url', url='ab/robert json')
输出:
/projects/
/people/ab/c
/people/ab/robert%20json
HTTP Methods:
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method=="GET":
#show_login_form()
pass
elif request.method=="POST":
#check_login_form()
pass
methods:
GET: get information from body
HEAD: trigger get action, but only interested in header
POST: put data in server(and server trigger store action only once)
PUT: similar with POST, but can trigger POST many times, so it' ok even the transmission breaks.
DELETE: delete data
OPTIONS: this method is implemented automatically by Flask, for the browser to figure out which method(s) does the url supports.
Static File:
create a directory named static in your package or next to your modul, to generate urls for static files, use url_for:
url_for("static", filename="main.css")
the file has to be stored in fs as static/main.css
Rendering Templates:
generating HTML from python is not funny, but cumbersome because you have to make sure your application's secure.
use render_template and pass the template name and the context variables:
@app.route("/hello/<user_name>")
def hello(user_name):
return render_template('hello.html', user_name=user_name)
templates/hello.html:
<!doctype html>
hello, {{user_name}}
inside the template you can access the request/session/g object as well as get_flashed_messages() function.
use template, use template inheritance.
Accessing request data:
in Flask you can access the request data with global request object.
how's that possible?
proxy!
when the web server receives a request, it spawn a new thread, when flask begin to deal with the request, it binds the current application and WSGI environments to that context(thread).
with flask, even in unittest it's easy for you to create a request:
with app.test_context_request('/hello', method='POST'):
assert request.path == '/hello'
assert request.method == 'POST'
the request object:
to get form data:
request.form['username']
but if the key doesn't exist in the form, Flask wil raise KeyError, Flask defaults it to 404.
to access parameters in url?key=value form, use request.args.get('key', default_value)
file uploading:
first set enctype="multipart/form-data"
use request.files to access the files, and request.files['form_name'] to access the specified file.
the file has filename attribute, but it can be forged, so use werkzeug.secure_filename(filename)
@app.route('/upload', methods=["GET", "POST"])
def upload_resume():
message = ''
if request.method=="POST":
file1 = request.files['file1']
if not file1:
message = 'file not found'
else:
file1.save("/Users/liaofeng/python/flask/uploads/" +
secure_filename(file1.filename))
message = 'file saved'
return render_template('upload.html', message=message)
<!doctype html>
{% if message %}
{{message}}
{% endif %}
<form action="/upload" method="POST" enctype="multipart/form-data">
select file: <input type="file" name="file1" />
<input type="submit" value="submit"/>
</form>
cookie:
access cookie: request.cookies, it's a dictionary
set cookie: response.set_cookie('name', 'value')
how to get response object:
1. if a controller method returns a string, Flask will automatically creates the response object.
2. use flask.make_response(response_string) method to explicitly create a response object.
but sometimes you might want to set cookie at the point response object doesn't exist, in this condition, use Deferred Request Callbacks pattern.
Redirect:
from flask import redirect
return redirect(url_for('login'))
abort a request earlier with a error code:
from flask import abort
abort(404) # notice: no return exists
this_will_never_execute()
or use errorhandle:
@app.errorhandle(404)
def page_not_found(error):
return render_template('page_not_found_error.html'), 400
404 is the http response code, it defaults to 200
Response:
with the string returned from a controller method, it will be used as the response body, with http response code 200 and text/html mimetype.
if the return is a tuple, show be in form (response_body, code, headers)
Sessions:
store some information for some use from one request to the next.
session is implemented on top of cookie and set the cookie cryptographically,
is means use can look the cookie content but can't modify it unless he knows the secret key for signing.
from flask import Flask, request, render_template, session, redirect, url_for
app = Flask(__name__)
app.debug = True
app.secret_key = '\xbbM\xfe\t@q\x11\xeb\xf2Y\x089\x8f\x07\xa6\x82q\x1e\x85RD\x92 \x93'
@app.route('/')
def index():
return "user in session: %s" % session.get('username', 'none')
@app.route('/login', methods=["GET", "POST"])
def login():
if 'username' in session:
return redirect(url_for('index'))
if request.method=="GET":
return render_template("login.html")
else:
session['username'] = request.form['username']
return redirect(url_for('index'))
@app.route('/logout')
def logout():
if 'username' in session:
session.pop('username')
return redirect(url_for('login'))
if __name__=='__main__':
app.run()
how to generate secret key:
import os
os.urandom(24)
note: make sure the cookie and session below the browser's maximum capacity.
Message flashing:
Logging:
app.logger.debug()
app.logger.warning()
app.logger.error()
that's all, thank you.