Flask Web Development读书笔记(上)

Flask Web Development读书笔记(上)_第1张图片

Flask Web Development读书笔记(上)

meta

拥有25年开发经验的高级软件工程师,目前为广播公司开发视频软件。他常在个人博客(blog.miguelgrinberg.com)上撰写各类博文,内容主要涉及Web开发、机器人技术、摄影,偶尔也会有一些影评。
全书分成三部分:- 基础知识导论- 一个完整的例子串讲- 最后一公里的测试调优部署

preface-why Flask

在python众多的web开发框架中,有两个现象级的-Django和Flask。
两个框架风格迥异,但都带动了庞大的生态圈。
Django提供一站式解决方案,所以相应的模块也就比较多。
Flask作为一种micro framework,一开始提供一个坚实的基础,然后接入一个第三方的生态系统,这是一种不同的策略或者说境界。本书的结构是从零开始,逐渐扩展成一个成熟的项目,而不是零散的知识介绍,因此对入门应该是比较合适的。
以python相关的数据库ORM框架来说,主要有两种,一种是Django的ORM,另外一种是SqlAlchemy。就是说除了Django以外,其他各种web框架的ORM最流行的SqlAlchemy,也有很多其他的选择,比如DynamoDB和MongoDB等等。
Flask的灵活是值得称道的,从最简单的单模块开始就能够构建web应用,也可以在大型应用中使用蓝图(blueprint)。这比Django无论大小应用都先来一堆很大的框架要灵活。

from flask import Flask 
app = Flask(__name__) 
 
@app.route("/") # take note of this decorator syntax
def hello(): 
    return "Hello World!" 
 
if __name__ == "__main__":
    app.run()

而Django一开始引导一个项目的时候,文件结构如下:

hello_django 
├── hello_django 
│ ├── __init__.py 
│ ├── settings.py 
│ ├── urls.py 
│ └── wsgi.py 
├── howdy 
│ ├── admin.py 
│ ├── __init__.py 
│ ├── migrations 
│ │ └── __init__.py 
│ ├── models.py 
│ ├── tests.py 
│ └── views.py 
└── manage.py

Django把一个项目分成各自独立的应用,而Flask认为一个项目应该是一个包含一些视图和模型的单个应用。也可以在Flask里复制出像Django那样的项目结构,但那不是默认的。
下面是一个使用了蓝图的Flask项目文件结构

|-flasky
  |-app/
      |-templates/
      |-static/
      |-main/
          |-__init__.py
          |-errors.py
          |-forms.py
          |-views.py
     |-__init__.py
     |-email.py
     |-models.py
  |-migrations/
  |-tests/
      |-__init__.py
      |-test*.py
  |-venv/
  |-requirements.txt
  |-config.py
  |-manage.py

templates模板

Flask默认使用Jinja2的模板,但也可以通过配置来使用其他的语言。
下面是一个for循环,{{}}是占位符标记,web后端,通过这个template文件和后端动态产生的数据data混合以后渲染出一个html文件。

{% for item in inventory %}
{{ item.render() }}
{% else %}

No items found

Try another search, maybe?

{% endfor %}

模板继承
先创建一个base.html,这个base.html抽取了所有模板文件的公共部分。
然后在每个具体的html文件中引用这个base.html,语法如下:

{% extends 'base.html' %}

有点类似于面向对象里面的继承extends。

Flask扩展

0.Flask-Bootstrap:集成Twitter开发的一个开源框架Bootstrap。
一开始没有使用Flask-Bootstrp,我们之间在html代码里面引用bootstrap的css文件和js文件,base.html如下所示:




    {% if title %}
        {{ title }} - microblog
    {% else %}
        microblog
    {% endif %}
    
    

    
    
    
    {% if g.locale != 'en' %}
        
    {% endif %}
    


{% block content %}{% endblock %}

再看使用Flask-Bootstrap以后的base.html

{% extends "bootstrap/base.html" %}

{% block title %}Flasky{% endblock %}

{% block head %}
{{ super() }}



{% endblock %}

{% block navbar %}

{% endblock %}

{% block content %}
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %} {% block page_content %}{% endblock %}
{% endblock %} {% block scripts %} {{ super() }} {{ moment.include_moment() }} {% endblock %}

1.Flask-Script:为Flask程序添加一个命令行解析器
2.Flask-Moment:本地化日期和时间
3.Flask-WTF:Web表单
4.Flask-Mail:邮件
5.Flask-SQLAlchemy:使用ORM框架SqlAlchemy
6.Flask-Login:Flask的认证扩展
• Flask-Login:管理已登录用户的用户会话。
• Werkzeug:计算密码散列值并进行核对。
• itsdangerous:生成并核对加密安全令牌。
示例14-17 app/api_1_0/posts.py:文章资源GET 请求的处理程序

@api.route('/posts/')
@auth.login_required
def get_posts():
   posts = Post.query.all()
   return jsonify({ 'posts': [post.to_json() for post in posts] })
@api.route('/posts/')
@auth.login_required #route入口,要求现有登录
def get_post(id):
    post = Post.query.get_or_404(id)
    return jsonify(post.to_json())

7.Flask-HTTPAuth:认证用户(提供REST服务的时候需要)
程序当前的登录功能是在Flask-Login 的帮助下实现的,可以把数据存储在用户会话中。默认情况下,Flask 把会话保存在客户端cookie 中,因此服务器没有保存任何用户相关信息,都转交给客户端保存。这种实现方式看起来遵守了REST 架构的无状态要求,但在REST Web 服务中使用cookie 有点不现实,因为Web 浏览器之外的客户端(比如移动端app)很难提供对cookie 的支持。鉴于此,使用cookie 并不是一个很好的设计选择。
因为REST 架构基于HTTP 协议,所以发送密令的最佳方式是使用HTTP 认证,基本认证和摘要认证都可以。在HTTP 认证中,用户密令包含在请求的Authorization 首部中。
HTTP 认证协议很简单,可以直接实现,不过Flask-HTTPAuth 扩展提供了一个便利的包装,可以把协议的细节隐藏在修饰器之中,类似于Flask-Login 提供的login_required 装饰器。
示例14-6 app/api_1_0/authentication.py:
初始化Flask-HTTPAuth

from flask.ext.httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
@auth.verify_password
def verify_password(email, password):
   if email == '':
      g.current_user = AnonymousUser()
      return True
   user = User.query.filter_by(email = email).first()
   if not user:
      return False
   g.current_user = user
   return user.verify_password(password)

上面代码是基本的方法,但是在实际使用中还需要考虑更多的安全因素。
基于令牌的认证
每次请求时,客户端都要发送认证密令。为了避免总是发送敏感信息,我们可以提供一种基于令牌的认证方案。
使用基于令牌的认证方案时,客户端要先把登录密令发送给一个特殊的URL,从而生成认证令牌。一旦客户端获得令牌,就可用令牌代替登录密令认证请求。出于安全考虑,令牌有过期时间。令牌过期后,客户端必须重新发送登录密令以生成新令牌。令牌落入他人之手所带来的安全隐患受限于令牌的短暂使用期限。这是通常的模式,不是Flask框架特有的或者Flask框架某个扩展的特性,所以在这里不再细说。
示例14-18 app/api_1_0/posts.py:文章资源POST 请求的处理程序

@api.route('/posts/', methods=['POST'])
@permission_required(Permission.WRITE_ARTICLES)
def new_post():
   post = Post.from_json(request.json)
   post.author = g.current_user
   db.session.add(post)
   db.session.commit()
   return jsonify(post.to_json()), 201, \
       {'Location': url_for('api.get_post', id=post.id, _external=True)}

示例14-19 app/api_1_0/decorators.py:permission_required 修饰器
里面使用了functools的装饰器wraps,它使得wrapper看起来和wrapped一样(内置类属性'__module__', '__name__', '__doc__','__dict__')

from functools import wraps
from flask import g
from .errors import forbidden
def permission_required(permission):    
    def decorator(f):        
        @wraps(f)        
        def decorated_function(*args, **kwargs):            
              if not g.current_user.can(permission):                
                  return forbidden('Insufficient permissions')            
              return f(*args, **kwargs)        
        return decorated_function    
    return decorator

该书中使用HTTPie测试Web服务.

想加入更多乐读创业社的活动,请访问网站→http://ledu.club
或关注微信公众号选取:

你可能感兴趣的:(Flask Web Development读书笔记(上))