如何构建大型Flask应用程序
介绍
构建 Python Web 应用程序有许多方法和约定。 尽管某些框架附带了工具(用于搭建)来自动化 - 并简化 - 任务(以及令人头疼的问题),但几乎所有解决方案都依赖于打包/模块化应用程序,因为代码库 [逻辑上] 分布在相关文件和文件夹中。
极简的Web应用开发框架Flask,有自己的——blueprints。
在这篇 DigitalOcean 文章中,我们将了解如何创建应用程序目录,并将其构建为与使用 Flask 蓝图创建的可重用组件一起使用。 这些部分极大地允许(并简化了)应用程序组件的维护和开发。
词汇表
1. Flask:极简的应用程序开发框架
2. 我们在本文中的选择
3. 为烧瓶准备系统
- 准备操作系统
- 设置 Python、pip 和 virtualenv
4. 构建应用程序目录
- 创建应用程序文件夹
- 创建虚拟环境
- 创建应用程序文件
- 安装烧瓶
5. 使用模块和蓝图(组件)
- 模块基础
- 模块模板
6. 创建应用程序(run.py、init.py 等)
7. 创建模块/组件
- 第 1 步:构建模块
- 第 2 步:定义模块数据模型
- 第 3 步:定义模块形式
- 第 4 步:定义应用程序控制器(视图)
- 第 5 步:在“app/init.py”中设置应用程序
- 第 6 步:创建模板
- 第 7 步:查看您的模块在运行中
Flask:极简的应用程序开发框架
Flask 是一个极简主义(或微型)框架,它避免强加处理关键事物的方式。 相反,Flask 允许开发人员使用他们想要并熟悉的工具。 为此,它带有自己的扩展索引,并且已经存在大量工具来处理从登录到日志记录的几乎所有事情。
它不是一个严格的“传统”框架,并且部分依赖于配置文件,坦率地说,当涉及到开始和检查时,这使许多事情变得更容易。
我们在本文中的选择
正如我们在上一节中刚刚结束的那样,Flask 的做事方式涉及使用您最熟悉的工具。 在我们的文章中,我们将使用——也许——在扩展和库方面最常见(也是最明智)的选择(即 数据库提取层)。 这些选择将涉及:
- SQLAlchemy(通过 Flask-SQLAlchemy)
- WTForms(通过 Flask-WTF)
Flask-SQLAlchemy
向 Flask 添加 SQLAlchemy 支持。 快捷方便。
这是一个批准的扩展。
Author: Armin Ronacher PyPI Page: Flask-SQLAlchemy Documentation: Read docs @ packages.python.org On Github: [mitsuhiko/flask-sqlalchemy](https://github.com/mitsuhiko/flask-sqlalchemy)
烧瓶-WTF
Flask-WTF 提供与 WTForms 的简单集成。 此集成包括可选的 CSRF 处理以提高安全性。
这是一个批准的扩展。
Author: Anthony Ford (created by Dan Jacob) PyPI Page: Flask-WTF Documentation: Read docs @ packages.python.org On Github: [ajford/flask-wtf](https://github.com/mitsuhiko/flask-wtf)
为烧瓶准备系统
在开始构建大型 Flask 应用程序之前,让我们准备系统并下载(并安装)Flask 发行版。
注意: 我们将开发一个运行最新版本可用操作系统(即 Ubuntu 13)。 强烈建议您在新系统上测试所有内容 - 特别是如果您正在积极为客户服务。
准备操作系统
为了拥有稳定的服务器,我们必须使所有相关工具和库保持最新并得到良好维护。
为确保我们拥有默认应用程序的最新可用版本,让我们从更新开始。
为基于 Debian 的系统运行以下命令(即 Ubuntu,Debian):
aptitude update aptitude -y upgrade
要获得必要的开发工具,请使用以下命令安装“build-essential”:
aptitude install -y build-essential python-dev python2.7-dev
设置 Python、pip 和 virtualenv
在 Ubuntu 和 Debian 上,默认提供最新版本的 Python 解释器 - 您可以使用它。 它使我们只需要安装有限数量的附加软件包:
- python-dev(开发工具)
- pip(管理包)
- virtualenv(创建隔离的、虚拟的
注意: 此处给出的说明保持简短。 要了解更多信息,请查看我们关于 pip 和 virtualenv 的操作指南文章:常用 Python 工具:使用 virtualenv、使用 Pip 安装和管理包。
点子
pip 是一个包管理器,它将帮助我们安装我们需要的应用程序包。
运行以下命令安装 pip:
curl https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py | python - curl https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python - export PATH="/usr/local/bin:$PATH"
虚拟环境
最好在自己的 环境 中包含 Python 应用程序及其所有依赖项。 可以将环境最好(简单地说)描述为所有内容所在的隔离位置(目录)。 为此,使用了一个名为 virtualenv 的工具。
运行以下命令以使用 pip 安装 virtualenv:
sudo pip install virtualenv
构建应用程序目录
我们将使用 LargeApp 的示例名称作为我们的应用程序文件夹。 在里面,我们将有一个虚拟环境(即 env)以及应用程序包(即 app) 和其他一些文件,例如用于运行测试(开发)服务器的“run.py”和用于保存 Flask 配置的“config.py”。
该结构(作为下面的示例给出)具有高度可扩展性,并且可以利用 Flask 和其他库提供的所有有用工具。 当你看到它时不要害怕,因为我们通过构建它来逐步解释一切。
目标示例结构:
~/LargeApp |-- run.py |-- config.py |__ /env # Virtual Environment |__ /app # Our Application Module |-- __init__.py |-- /module_one |-- __init__.py |-- controllers.py |-- models.py |__ /templates |__ /module_one |-- hello.html |__ /static |__ .. |__ . |__ .. |__ .
创建应用程序文件夹
让我们从创建我们需要的主文件夹开始。
依次运行以下命令来执行任务:
mkdir ~/LargeApp mkdir ~/LargeApp/app mkdir ~/LargeApp/app/templates mkdir ~/LargeApp/app/static
我们目前的结构:
~/LargeApp |__ /app # Our Application Module |__ /templates |__ /static
创建虚拟环境
使用虚拟环境带来了很多好处。 强烈建议您为每个应用程序使用一个新的虚拟环境。 将 virtualenv 文件夹保存在您的应用程序中是保持事物井然有序的好方法。
运行以下命令来创建一个安装了 pip 的新虚拟环境。
cd ~/LargeApp virtualenv env
创建应用程序文件
在这一步中,我们将在继续使用模块和蓝图之前形成基本的应用程序文件。
运行以下命令以创建基本应用程序文件:
touch ~/LargeApp/run.py touch ~/LargeApp/config.py touch ~/LargeApp/app/__init__.py
我们目前的结构:
~/LargeApp |-- run.py |-- config.py |__ /env # Virtual Environment |__ /app # Our Application Module |-- __init__.py |__ /templates |__ /static
安装 Flask 和应用程序依赖项
一切就绪后,开始使用 Flask 进行开发,让我们使用 pip 下载并安装它。
运行以下命令在虚拟环境 env 中安装 Flask。
cd ~/LargeApp env/bin/pip install flask env/bin/pip install flask-sqlalchemy env/bin/pip install flask-wtf
注意:这里我们是在没有激活虚拟环境的情况下下载和安装Flask 。 但是,鉴于我们使用环境本身的 pip,它可以完成相同的任务。 如果您正在使用激活的环境,则可以只使用 pip 代替。
就是这样! 我们现在准备使用蓝图构建一个更大的 Flask 应用程序。
使用模块和蓝图(组件)
模块基础
至此,我们的应用程序结构已设置好,其依赖项已下载并准备就绪。
我们的目标是模块化(即 使用 Flask 的蓝图创建可重用的组件)所有可以逻辑分组的相关模块。
一个例子可以是认证系统。 将其所有视图、控制器、模型和助手放在一个地方,并以允许可重用性的方式进行设置,使得这种结构成为维护应用程序同时提高生产力的好方法。
目标示例模块(组件)结构(在 /app
内部):
# Our module example here is called *mod_auth* # You can name them as you like as long as conventions are followed /mod_auth |-- __init__.py |-- controllers.py |-- models.py |-- .. |-- .
模块模板
为了支持最大程度的模块化,我们将构建“模板”文件夹以遵循上述约定并包含一个新文件夹 - 与模块具有相同或相似的相关名称 - 以包含其模板文件。
目标示例模板目录结构(在 LargeApp
内):
/templates |-- 404.html |__ /auth |-- signin.html |-- signup.html |-- forgot.html |-- .. |-- .
创建应用程序
在本节中,我们将继续前面的步骤,从应用程序 的实际编码开始,然后 开始创建我们的第一个模块化组件(使用蓝图):mod_auth
用于处理所有与身份验证相关的过程(IE 登录、注册等)。
使用 nano 编辑“run.py”
nano ~/LargeApp/run.py
放置内容:
# Run a test server. from app import app app.run(host='0.0.0.0', port=8080, debug=True)
使用 CTRL+X 保存并退出并使用 Y 确认。
使用 nano 编辑“config.py”
nano ~/LargeApp/config.py
放置内容:
# Statement for enabling the development environment DEBUG = True # Define the application directory import os BASE_DIR = os.path.abspath(os.path.dirname(__file__)) # Define the database - we are working with # SQLite for this example SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, 'app.db') DATABASE_CONNECT_OPTIONS = {} # Application threads. A common general assumption is # using 2 per available processor cores - to handle # incoming requests using one and performing background # operations using the other. THREADS_PER_PAGE = 2 # Enable protection agains *Cross-site Request Forgery (CSRF)* CSRF_ENABLED = True # Use a secure, unique and absolutely secret key for # signing the data. CSRF_SESSION_KEY = "secret" # Secret key for signing cookies SECRET_KEY = "secret"
使用 CTRL+X 保存并退出并使用 Y 确认。
创建模块/组件
本节是定义本文核心的第一个主要步骤。 在这里,我们将看到如何使用 Flask 的蓝图来创建模块(即 一个组件)。
这方面的出色之处在于为您的代码提供了 可移植性 和 可重用性 ,以及易于维护 - 您将在未来感谢您,因为这通常是一场斗争回来并理解他们留下的东西。
第 1 步:构建模块
正如我们已经开始做的那样,让我们创建第一个模块的 (mod_auth
) 目录和文件以开始处理它们。
# Create the module directory inside the *app* module mkdir ~/LargeApp/app/mod_auth # Create where module's templates will reside mkdir ~/LargeApp/app/templates/auth # Create __init__.py to set the directory as a Python module touch ~/LargeApp/app/mod_auth/__init__.py # Create module's controllers and models etc. touch ~/LargeApp/app/mod_auth/controllers.py touch ~/LargeApp/app/mod_auth/models.py touch ~/LargeApp/app/mod_auth/forms.py # Create module's templates touch ~/LargeApp/app/templates/auth/signin.html # Create a HTTP 404 Error page touch ~/LargeApp/app/templates/404.html
在这些操作之后,文件夹结构应该是这样的:
~/LargeApp |-- run.py |-- config.py |__ /env # Virtual Environment |__ /app # Our Application Module |-- __init__.py |-- /mod_auth # Our first module, mod_auth |-- __init__.py |-- controllers.py |-- models.py |-- forms.py |__ /templates |-- 404.html |__ /auth |-- signin.html |__ /static
第 2 步:定义模块数据模型
nano ~/LargeApp/app/mod_auth/models.py
放置以下不言自明的 - 示例 - 内容:
# Import the database object (db) from the main application module # We will define this inside /app/__init__.py in the next sections. from app import db # Define a base model for other database tables to inherit class Base(db.Model): __abstract__ = True id = db.Column(db.Integer, primary_key=True) date_created = db.Column(db.DateTime, default=db.func.current_timestamp()) date_modified = db.Column(db.DateTime, default=db.func.current_timestamp(), onupdate=db.func.current_timestamp()) # Define a User model class User(Base): __tablename__ = 'auth_user' # User Name name = db.Column(db.String(128), nullable=False) # Identification Data: email & password email = db.Column(db.String(128), nullable=False, unique=True) password = db.Column(db.String(192), nullable=False) # Authorisation Data: role & status role = db.Column(db.SmallInteger, nullable=False) status = db.Column(db.SmallInteger, nullable=False) # New instance instantiation procedure def __init__(self, name, email, password): self.name = name self.email = email self.password = password def __repr__(self): return '<User %r>' % (self.name)
使用 CTRL+X 保存并退出并使用 Y 确认。
第 3 步:定义模块形式
nano ~/LargeApp/app/mod_auth/forms.py
放置以下不言自明的 - 示例 - 内容:
# Import Form and RecaptchaField (optional) from flask.ext.wtf import Form # , RecaptchaField # Import Form elements such as TextField and BooleanField (optional) from wtforms import TextField, PasswordField # BooleanField # Import Form validators from wtforms.validators import Required, Email, EqualTo # Define the login form (WTForms) class LoginForm(Form): email = TextField('Email Address', [Email(), Required(message='Forgot your email address?')]) password = PasswordField('Password', [ Required(message='Must provide a password. ;-)')])
使用 CTRL+X 保存并退出并使用 Y 确认。
第 4 步:定义应用程序控制器(视图)
nano ~/LargeApp/app/mod_auth/controllers.py
放置以下不言自明的 - 示例 - 内容:
# Import flask dependencies from flask import Blueprint, request, render_template, \ flash, g, session, redirect, url_for # Import password / encryption helper tools from werkzeug import check_password_hash, generate_password_hash # Import the database object from the main app module from app import db # Import module forms from app.mod_auth.forms import LoginForm # Import module models (i.e. User) from app.mod_auth.models import User # Define the blueprint: 'auth', set its url prefix: app.url/auth mod_auth = Blueprint('auth', __name__, url_prefix='/auth') # Set the route and accepted methods @mod_auth.route('/signin/', methods=['GET', 'POST']) def signin(): # If sign in form is submitted form = LoginForm(request.form) # Verify the sign in form if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() if user and check_password_hash(user.password, form.password.data): session['user_id'] = user.id flash('Welcome %s' % user.name) return redirect(url_for('auth.home')) flash('Wrong email or password', 'error-message') return render_template("auth/signin.html", form=form)
使用 CTRL+X
保存并退出,并使用 Y
确认。
第 5 步:在“app/init.py”中设置应用程序
nano ~/LargeApp/app/__init__.py
放置内容:
# Import flask and template operators from flask import Flask, render_template # Import SQLAlchemy from flask.ext.sqlalchemy import SQLAlchemy # Define the WSGI application object app = Flask(__name__) # Configurations app.config.from_object('config') # Define the database object which is imported # by modules and controllers db = SQLAlchemy(app) # Sample HTTP error handling @app.errorhandler(404) def not_found(error): return render_template('404.html'), 404 # Import a module / component using its blueprint handler variable (mod_auth) from app.mod_auth.controllers import mod_auth as auth_module # Register blueprint(s) app.register_blueprint(auth_module) # app.register_blueprint(xyz_module) # .. # Build the database: # This will create the database file using SQLAlchemy db.create_all()
使用 CTRL+X 保存并退出并使用 Y 确认。
第 6 步:创建模板
nano ~/LargeApp/app/templates/auth/signin.html
放置内容:
{% macro render_field(field, placeholder=None) %} {% if field.errors %} <div> {% elif field.flags.error %} <div> {% else %} <div> {% endif %} {% set css_class = 'form-control ' + kwargs.pop('class', '') %} {{ field(class=css_class, placeholder=placeholder, **kwargs) }} </div> {% endmacro %} <div> <div> <legend>Sign in</legend> {% with errors = get_flashed_messages(category_filter=["error"]) %} {% if errors %} <div> {% for error in errors %} {{ error }}<br> {% endfor %} </div> {% endif %} {% endwith %} {% if form.errors %} <div> {% for field, error in form.errors.items() %} {% for e in error %} {{ e }}<br> {% endfor %} {% endfor %} </div> {% endif %} <form method="POST" action="." accept-charset="UTF-8" role="form"> {{ form.csrf_token }} {{ render_field(form.email, placeholder="Your Email Address", autofocus="") }} {{ render_field(form.password, placeholder="Password") }} <div> <label> <input type="checkbox" name="remember" value="1"> Remember Me </label> <a role="button" href="">Forgot your password?</a><span class="clearfix"></span> </div> <button type="submit" name="submit">Sign in</button> </form> </div> </div>
使用 CTRL+X 保存并退出并使用 Y 确认。
注意: 这个模板文件是一个非常简单且不完整的示例,只是为了演示目的而创建的。 强烈建议您阅读 Jinja2 文档 并使用基本文件来构建您网站的模板。
第 7 步:查看您的模块在运行中
在创建了我们的第一个模块之后,是时候看看一切在行动了。
使用 run.py
运行开发服务器:
cd ~/LargeApp env/bin/python run.py
这将启动一个发展(即 测试)服务器托管在端口 8080。
通过转到 URL 访问模块:
http://[your droplet's IP]/auth/signin
尽管您将无法登录,但您可以通过输入一些示例数据或测试其验证器来查看它的运行情况。