Flask创建项目

文章目录

    • 项目目录规划参考方案:
    • 目录结构总结
    • Models设计
    • Views设计
    • Template设计
    • Form类的作用
    • HTTP请求方法介绍
    • 常见插件介绍
      • flask_login
        • 安装
        • 配置
        • 测试
        • 用户模型
        • 例如:限制必须登录才能访问settings视图
      • flask_bcrypt
      • flask_bootstrap
      • flask_sqlalchemy
      • flask_debugtoolbar
        • 安装
        • 用法
        • 配置
      • flask_script
      • flask_migrate
      • flask_wtf
        • 功能
        • 快速入门
          • 创建表单
          • 验证表单

项目目录规划参考方案:

 mkdir LaiKe
 cd LaiKe/
 # server(server端目录)
 mkdir -p project/server
 touch project/server/models.py
 touch project/server/settings.py
 # client(client端目录)
 mkdir -p project/client
 mkdir -p project/client/static
 mkdir -p project/client/templates
 t
 ##### main 模块 ########
 # server -> main
 mkdir -p project/server/main
 touch project/server/main/__init__.py
 touch project/server/main/views.py
 # client > main
 mkdir -p project/client/templates/main
 touch -p project/client/templastes/main/home.html
 
 ####### User 模块 #######
 # server -> user
 mkdir -p project/server/user
 mkdir -p project/server/user/__init__.py
 mkdir -p project/server/user/forms.py
 mkdir -p project/server/user/views.py
 # client -> user
 mkdir -p project/client/templates/user
 touch -p project/client/templastes/user/login.html
 touch -p project/client/templastes/user/register.html
 touch -p project/client/templastes/user/members.html
 
 ######## entry模块 ##########
 #server -> entry (词条)
 mkdir -p project/server/entry/
 touch  project/server/entry/__init__.py
 touch  project/server/entry/forms.py
 touch  project/server/entry/views.py
 # client -> entry
 mkdir -p project/client/templates/entry
 touch -p project/client/templastes/entry/login.html
 touch -p project/client/templastes/entry/register.html
 touch -p project/client/templastes/entry/members.html

目录结构总结

  • app的创建:初始化的时,可以通过template_folder指定模板位置,通过static_folder指定静态资源位置

  • 蓝图的构建:通过Blueprint为每个模块定义一个蓝图对象,然后通过app.register_blueprint将各个模块通过蓝图对象集成到app中;

  • 插件的应用:一个插件通常就是一个类,插件其实就是对flask创建的app的包装,即把app当做构造方法的参数。例如:

    toolbar = DebugToolbarExtension(app)
    bootstrap = Bootstrap(app)
    login_manager = LoginManager(app)
    db = SQLAlchemy(app)  # ORM插件
    
  • Model的设计:ORM

  • Views的设计:Flask中的Views包含路由和视图函数。设计风格:RESTful 风格、非RESTful风格、前后端分离接口风格、模板风格。

Models设计

import datetime

from project.server import db, bcrypt, app


class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    email = db.Column(db.String(255), unique=True, nullable=False)
    password = db.Column(db.String(255), nullable=False)
    registered_on = db.Column(db.DateTime, nullable=False)
    admin = db.Column(db.Boolean, nullable=False, default=False)
    entries = db.relationship('Entry', backref='creator')
    def __init__(self, email, password, admin=False):
        self.email = email
        self.password = bcrypt.generate_password_hash(
            password, app.config.get('BCRYPT_LOG_ROUNDS')
        )
        self.registered_on = datetime.datetime.now()
        self.admin = admin

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def get_id(self):
        return self.id

    def __repr__(self):
        return ''.format(self.email)



class Entry(db.Model):
    __tablename__ = 'entry'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True,nullable=True)
    title = db.Column(db.String(100), unique=True, nullable=False)
    content = db.Column(db.String(500), nullable=False)
    author_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

Views设计

Views中同时包含路由和负责处理请求的视图函数。每个模块都仅有一个views.py,用来定义该模块涉及的所有用来处理请求的视图函数,每个视图函数都和一个url绑定。

传统风格:

 # project/server/entry/views.py
from flask import render_template, Blueprint

entry_blueprint = Blueprint('entry', __name__, url_prefix='/entry')


@entry_blueprint.route('/list/')
def entries():
    return render_template('entry/entries.html')


@entry_blueprint.route('/add/')
def add_entry():
    return render_template('entry/add.html')


@entry_blueprint.route('/delete//')
def delete_entry(entry_id):
    return render_template('entry/delete.html')


@entry_blueprint.route('/modify//')
def modify(entry_id):
    return render_template('entry/modify.html')

RESTful风格:

entry_blueprint = Blueprint('entry', __name__)
# 1. 对所有资源的操作
@entry_blueprint.route('/entries/',methods=['GET','POST'])
def entries():
    pass
# 2. 对某个资源的操作
@entry_blueprint.route('/entry//',methods=['GET','PUT','DELETE'])
def entry():
    pass

Template设计

模板的设计综合使用继承和组合的思想,最大限度地减少重复代码。

继承:{% extends ‘_base.html’ %}

组装:{% include ‘header.html’ %}、{% include ‘footer.html’ %}

Form类的作用

Form类:把对form表单的操作对象化

ORM类:把对关系表的操作对象化

HTTP请求方法介绍

HTTP 方法(也经常被叫做“谓词”)告知服务器,客户端想对请求的页面 些什么。下面的都是非常常见的方法:

  • GET

    浏览器告知服务器:只 获取 页面上的信息并发给我。这是最常用的方法。

  • HEAD

    浏览器告诉服务器:欲获取信息,但是只关心 消息头 。应用应像处理 GET 请求一样来处理它,但是不分发实际内容。在 Flask 中你完全无需 人工 干预,底层的 Werkzeug 库已经替你打点好了。

  • POST

    浏览器告诉服务器:想在 URL 上 发布 新信息。并且,服务器必须确保 数据已存储且仅存储一次。这是 HTML 表单通常发送数据到服务器的方法。

  • PUT

    类似 POST 但是服务器可能触发了存储过程多次,多次覆盖掉旧值。你可 能会问这有什么用,当然这是有原因的。考虑到传输中连接可能会丢失,在 这种 情况下浏览器和服务器之间的系统可能安全地第二次接收请求,而 不破坏其它东西。因为 POST它只触发一次,所以用 POST 是不可能的。

  • DELETE

    删除给定位置的信息。

  • OPTIONS

    给客户端提供一个敏捷的途径来弄清这个 URL 支持哪些 HTTP 方法。 从 Flask 0.6 开始,实现了自动处理。

有趣的是,在 HTML4 和 XHTML1 中,表单只能以 GET 和 POST 方法提交到服务器。但是 JavaScript 和未来的 HTML 标准允许你使用其它所有的方法。此外,HTTP 最近变得相当流行,浏览器不再是唯一的 HTTP 客户端。

常见插件介绍

flask_login

https://flask-login.readthedocs.io/en/latest

Flask-Login为Flask提供了会话管理,如:登录、退出登录、7天内自动登录等。

Flask-Login provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering your users’ sessions over extended periods of time.

主要功能:

  • Store the active user’s ID in the session, and let you log them in and out easily.

    把 用户ID保存在session中,使登录和退出登录非常简单。(通过从session中保存用户ID或删除用户ID达到登录和退出登录的效果)

  • Let you restrict views to logged-in (or logged-out) users.

    为views的执行添加限制,eg:通过login_required装饰器来限制某个视图函数必须是登录状态下才能访问。

  • Handle the normally-tricky “remember me” functionality.

    提供【记住我】功能。

  • Help protect your users’ sessions from being stolen by cookie thieves.

    保护用户会话信息,防止被盗。

  • Possibly integrate with Flask-Principal or other authorization extensions later on.

    将来可能会和Flask-Princial等认证插件集成。

安装

pip install flask-login

配置

login_manager = LoginManager()
login_manager.init_app(app)

测试

@login_manager.user_loader
def load_user(user_id):
    """
    user_id如果不正确,该user_id将从session移除,return None
    """
    return User.get(user_id)

用户模型

用户模型必须实现下面的属性和方法:

The class that you use to represent users needs to implement these properties and methods:

  • is_authenticated

    功能:判断用户是否已经验证通过,如果是认证通过的,is_authenticated的返回值是True。

    This property should return True if the user is authenticated, i.e. they have provided valid credentials. (Only authenticated users will fulfill the criteria of login_required.)

  • is_active

    如果是一个活跃的user,将返回True。

    This property should return True if this is an active user - in addition to being authenticated, they also have activated their account, not been suspended, or any condition your application has for rejecting an account. Inactive accounts may not log in (without being forced of course).

  • is_anonymous

    如果是一个匿名用户,将返回True。

    This property should return True if this is an anonymous user. (Actual users should return False instead.)

  • get_id()

    获得user的id

    This method must return a unicode that uniquely identifies this user, and can be used to load the user from the user_loader callback. Note that this must be a unicode - if the ID is natively an int or some other type, you will need to convert it to unicode.

To make implementing a user class easier, you can inherit from UserMixin, which provides default implementations for all of these properties and methods. (It’s not required, though.)

@app.route('/login', methods=['GET', 'POST'])
def login():
    # Here we use a class of some kind to represent and validate our
    # client-side form data. For example, WTForms is a library that will
    # handle this for us, and we use a custom LoginForm to validate.
    form = LoginForm()
    if form.validate_on_submit():
        # Login and validate the user.
        # user should be an instance of your `User` class
        login_user(user)

        flask.flash('Logged in successfully.')

        next = flask.request.args.get('next')
        # is_safe_url should check if the url is safe for redirects.
        # See http://flask.pocoo.org/snippets/62/ for an example.
        if not is_safe_url(next):
            return flask.abort(400)

        return flask.redirect(next or flask.url_for('index'))
    return flask.render_template('login.html', form=form)

例如:限制必须登录才能访问settings视图

Views that require your users to be logged in can be decorated with the login_required decorator:

@app.route("/settings")
@login_required
def settings():
    pass

flask_bcrypt

一个密码加密的插件

pw_hash = bcrypt.generate_password_hash('hunter2')
bcrypt.check_password_hash(pw_hash, 'hunter2') # returns True

flask_bootstrap

和bootstrap集成的插件。

flask_sqlalchemy

参考文档:http://www.pythondoc.com/flask-sqlalchemy/quickstart.html

常见情况下对于只有一个 Flask 应用,所有您需要做的事情就是创建 Flask 应用,选择加载配置接着创建 SQLAlchemy 对象时候把 Flask 应用传递给它作为参数。

一旦创建,这个对象就包含 sqlalchemysqlalchemy.orm 中的所有函数和助手。此外它还提供一个名为 Model 的类,用于作为声明模型时的 delarative 基类:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)

    def __init__(self, username, email):
        self.username = username
        self.email = email

    def __repr__(self):
        return '' % self.username

为了创建初始数据库,只需要从交互式 Python shell 中导入 db 对象并且调用 SQLAlchemy.create_all() 方法来创建表和数据库:

>>> from yourapplication import db
>>> db.create_all()

Boom, 您的数据库已经生成。现在来创建一些用户:

>>> from yourapplication import User
>>> admin = User('admin', '[email protected]')
>>> guest = User('guest', '[email protected]')

但是它们还没有真正地写入到数据库中,因此让我们来确保它们已经写入到数据库中:

>>> db.session.add(admin)
>>> db.session.add(guest)
>>> db.session.commit()

访问数据库中的数据也是十分简单的:

>>> users = User.query.all()
[, ]
>>> admin = User.query.filter_by(username='admin').first()

SQLAlchemy 连接到关系型数据库,关系型数据最擅长的东西就是关系。因此,我们将创建一个使用两张相互关联的表的应用作为例子:

from datetime import datetime


class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(80))
    body = db.Column(db.Text)
    pub_date = db.Column(db.DateTime)

    category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
    category = db.relationship('Category',
        backref=db.backref('posts', lazy='dynamic'))

    def __init__(self, title, body, category, pub_date=None):
        self.title = title
        self.body = body
        if pub_date is None:
            pub_date = datetime.utcnow()
        self.pub_date = pub_date
        self.category = category

    def __repr__(self):
        return '' % self.title


class Category(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '' % self.name

首先让我们创建一些对象:

>>> py = Category('Python')
>>> p = Post('Hello Python!', 'Python is pretty cool', py)
>>> db.session.add(py)
>>> db.session.add(p)

现在因为我们在 backref 中声明了 posts 作为动态关系,查询显示为:

>>> py.posts

它的行为像一个普通的查询对象,因此我们可以查询与我们测试的 “Python” 分类相关的所有文章(posts):

>>> py.posts.all()
[]

flask_debugtoolbar

安装

简单地使用 pip 来安装:

$ pip install flask-debugtoolbar

用法

设置调试工具栏是简单的:

from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension

app = Flask(__name__)

# the toolbar is only enabled in debug mode:
app.debug = True

# set a 'SECRET_KEY' to enable the Flask session cookies
app.config['SECRET_KEY'] = ''

toolbar = DebugToolbarExtension(app)

当调试模式开启的时候,工具栏会自动地给添加到 Jinja 模板中。在生产环境中,设置 app.debug = False 将会禁用工具栏。

该扩展也支持 Flask 应用的工厂模式,先单独地创建工具栏接着后面为应用初始化它:

toolbar = DebugToolbarExtension()
# Then later on.
app = create_app('the-config.cfg')
toolbar.init_app(app)

配置

工具栏支持多个配置选项:

名称 描述 默认值
DEBUG_TB_ENABLED 启用工具栏? app.debug
DEBUG_TB_HOSTS 显示工具栏的 hosts 白名单 任意 host
DEBUG_TB_INTERCEPT_REDIRECTS 要拦截重定向? True
DEBUG_TB_PANELS 面板的模板/类名的清单 允许所有内置的面板
DEBUG_TB_PROFILER_ENABLED 启用所有请求的分析工具 False, 用户自行开启
DEBUG_TB_TEMPLATE_EDITOR_ENABLED 启用模板编辑器 False

要更改配置选项之一,在 Flask 应用程序配置中像这样设置它:

app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False

flask_script

The Flask-Script extension provides support for writing external scripts in Flask. This includes running a development server, a customised Python shell, scripts to set up your database, cronjobs, and other command-line tasks that belong outside the web application itself.

Flask-Script works in a similar way to Flask itself. You define and add commands that can be called from the command line to a Manager instance:

# manage.py

from flask_script import Manager

from myapp import app

manager = Manager(app)

@manager.command
def hello():
    print "hello"

if __name__ == "__main__":
    manager.run()

Once you define your script commands, you can then run them on the command line:

python manage.py hello
> hello

flask_migrate

官方文档:https://flask-migrate.readthedocs.io/en/late

初始化资源(create a migration repository)

$ flask db init

生成migration:(Generate an initial migration)

$ flask db migrate  #  相当于Django中的  makemigrations

把migration迁移到数据库(apply the migration to the database)

$ flask db upgrade

注意:每次修改model后,要重新依次执行migrate和upgrade命令。(Then each time the database models change repeat the migrate and upgradecommands.)

查看所有可以使用的命令:

flask db --help

把db命令集成到flask-script中:

# manage.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
migrate = Migrate(app, db)

manager = Manager(app)
manager.add_command('db', MigrateCommand)

集成之后,执行命令的方式如下:

$ python manage.py db init
$ python manage.py db migrate
$ python manage.py db upgrade
$ python manage.py db --help

flask_wtf

参考文档:http://www.pythondoc.com/flask-wtf/

功能

  • 集成 wtforms。
  • 带有 csrf 令牌的安全表单。
  • 全局的 csrf 保护。
  • 支持验证码(Recaptcha)。
  • 与 Flask-Uploads 一起支持文件上传。
  • 国际化集成。

快速入门

创建表单

Flask-WTF 提供了对 WTForms 的集成。例如:

from flask_wtf import Form
from wtforms import StringField
from wtforms.validators import DataRequired

class MyForm(Form):
    name = StringField('name', validators=[DataRequired()])

Note

从 0.9.0 版本开始,Flask-WTF 将不会从 wtforms 中导入任何的内容,用户必须自己从 wtforms 中导入字段。

另外,隐藏的 CSRF 令牌会被自动地创建。你可以在模板这样地渲染它:

<form method="POST" action="/">
    {{ form.csrf_token }}
    {{ form.name.label }} {{ form.name(size=20) }}
    <input type="submit" value="Go">
form>

尽管如此,为了创建有效的 XHTML/HTML, Form 类有一个 hidden_tag 方法, 它在一个隐藏的 DIV 标签中渲染任何隐藏的字段,包括 CSRF 字段:

<form method="POST" action="/">
    {{ form.hidden_tag() }}
    {{ form.name.label }} {{ form.name(size=20) }}
    <input type="submit" value="Go">
form>
验证表单

在你的视图处理程序中验证请求:

@app.route('/submit', methods=('GET', 'POST'))
def submit():
    form = MyForm()
    if form.validate_on_submit():
        return redirect('/success')
    return render_template('submit.html', form=form)

注意你不需要把 request.form 传给 Flask-WTF;Flask-WTF 会自动加载。便捷的方法 validate_on_submit 将会检查是否是一个 POST 请求并且请求是否有效。

你可能感兴趣的:(Flask创建项目)