内容提要

在学习“狗书”《Flask Web开发:基于Python的Web应用开发实战》的过程中,一直遇到各种各样的坑。该书的第一部分是“Flask简介”,主要介绍的一些基础知识。第二部分是“实例:社交博客程序”,讲的是如何搭建一个社交博客的框架。目前我学到第二部分,回过头来看,觉得按照程序运行的实际逻辑来说明程序的原理,并在这个过程中分析遇到的各种“坑”,也更能让人理解(至少让我自己理解)。

正文

  1. 程序包结构

flask文件夹结构

其中:
app为程序包,Flask程序保存在这个包中
migrations文件夹包含数据库迁移脚本
tests包保存单元测试
requirements文件夹中记录程序的依赖
config.py是程序的配置文件
manage.py是程序的运行文件,用于启动程序即程序的其他任务


app包结构

其中:
auth为保存专门用于认证的auth蓝本
main为保存main蓝本的包
static文件夹用于保存静态文件,例如HTML代码中引用的图片、 JavaScript 源码文件和 CSS
templates用于保存网页的模板


蓝本文件夹结构(以auth文件夹为例,main蓝本等不再赘述)

蓝本的细节就不在此赘述,在flask框架中用到蓝本,可以对不同的程序功能使用不同的蓝本,这是保证程序整齐有序的办法。(想想把所有功能都写在一起会多么混乱)。这里说明一下到蓝本的程序运行原理:app/auth/views.py 模块引入蓝本,然后使用蓝本的 route 修饰器定义与认证相关的路由,然后再渲染views中设定的网页模板。看起来是不是如果程序能运行到蓝本这一步,我们就可以对网页进行操作了。
2. 运行说明
在运行程序的时候,我们在虚拟环境下,通过如下命令来完成。由此可见,程序的运行是由manage.py来开始的。

(venv) $ python manage.py runserver

那么,我们来看看这个manage.py吧,期待不期待?兴奋不兴奋?

#!/usr/bin/env python
import os
COV = None
if os.environ.get('FLASK_COVERAGE'):import coverageCOV = coverage.coverage(branch=True, include='app/*')COV.start()if os.path.exists('.env'):print('Importing environment from .env...')for line in open('.env'):var = line.strip().split('=')if len(var) == 2:os.environ[var[0]] = var[1]from app import create_app, db
from app.models import User, Follow, Role, Permission, Post, Comment
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommandapp = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)def make_shell_context():return dict(app=app, db=db, User=User, Follow=Follow, Role=Role,Permission=Permission, Post=Post, Comment=Comment)
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)@manager.command
def test(coverage=False):"""Run the unit tests."""if coverage and not os.environ.get('FLASK_COVERAGE'):import sysos.environ['FLASK_COVERAGE'] = '1'os.execvp(sys.executable, [sys.executable] + sys.argv)import unittesttests = unittest.TestLoader().discover('tests')unittest.TextTestRunner(verbosity=2).run(tests)if COV:COV.stop()COV.save()print('Coverage Summary:')COV.report()basedir = os.path.abspath(os.path.dirname(__file__))covdir = os.path.join(basedir, 'tmp/coverage')COV.html_report(directory=covdir)print('HTML version: file://%s/index.html' % covdir)COV.erase()@manager.command
def profile(length=25, profile_dir=None):"""Start the application under the code profiler."""from werkzeug.contrib.profiler import ProfilerMiddlewareapp.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[length],profile_dir=profile_dir)app.run()@manager.command
def deploy():"""Run deployment tasks."""from flask_migrate import upgradefrom app.models import Role, User# migrate database to latest revisionupgrade()# create user rolesRole.insert_roles()# create self-follows for all usersUser.add_self_follows()if __name__ == '__main__':manager.run()

下面我们按照顺序去理一下,看看manage.py到底是如何运行的。

import os
COV = None
if os.environ.get('FLASK_COVERAGE'):import coverageCOV = coverage.coverage(branch=True, include='app/*')COV.start()

?这段代码是关于程序测试覆盖度方面的,我们可以先行略去,测试本身并不影响程序的运行。

if os.path.exists('.env'):print('Importing environment from .env...')for line in open('.env'):var = line.strip().split('=')if len(var) == 2:os.environ[var[0]] = var[1]

?这段代码的作用是从.env文件中导入环境变量。具体的大家可以去搜索一下这个文件,在程序配置中,有些信息是在环境变量中设置的。但是此处我们也略过细节。

from app import create_app, db
from app.models import User, Follow, Role, Permission, Post, Comment
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand

?各种导入,从app包中导入create_app工厂函数和数据库db。从app.models模块中导入User, Follow, Role, Permission, Post, Comment等类。在flask_script扩展中导入Manager, Shell, 从flask_migrate扩展中导入Migrate, MigrateCommand。什么意思呢?自己去搜索一下。
此处有一个坑:在原文中flask的扩展采用from flask.ext.script import Manager, Shell的导入方式,但是实际用的时候却会报错,从报错的信息中可以知道flask.ext.script已经弃用了,改为flask_script即可。其他的扩展也是一样。

>>> from flask.ext.script import Manager
__main__:1: ExtDeprecationWarning: Importing flask.ext.script is deprecated, use flask_script instead.

让我们继续吧!

app = create_app(os.getenv ('FLASK_CONFIG') or 'default')

这一句是创建工厂函数的实例,create_app是由from app import create_app, db导入的。
这里有一个坑需要注意一下,from app import create_app, db中的app是我们创建的一个包,其放置于顶层文件夹中,其中包括一个__init__.py文件。而语句app = create_app(os.getenv (‘FLASK_CONFIG’) or ‘default’)的app则是给create_app函数创建的实例的一个命名,这两个的含义是不一样的。对于我这种小白,刚刚开始也是迷惑了一阵子。
现在我们来看一下具体实现的方法。
os.getenv(‘FLASK_CONFIG’) or ‘default’),这个’FLASK_CONFIG’我们是我们设置的环境变量,通过os.getenv我们可以获得这个环境参数。因为这是对工厂函数create_app进行实例化,因此我们得到的环境参数就作为create_app的参数,如果没有设置’FLASK_CONFIG’这个环境变量,那么就将’default’这个默认值赋给工厂函数。那么这个设置的’FLASK_CONFIG’环境变量或这个’default’到底是什么呢?这需要到工厂函数中看一下。
既然create_app是从app包中导入的,那么让我们一起来看看这个包里面都有什么吧!打开app下面的__init__.py文件,看到没有create_app藏在这里呢。

此处插播工厂函数广告,为不影响文章的总体逻辑,特做分割


app / __ init __.py

from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_pagedown import PageDown
from config import config
#依旧是各种导入,这里应该不难理解,在工厂函数中导入了flask及其扩展,也通过from config import config导入了程序配置文件config.py中的config属性bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
pagedown = PageDown()login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
#这里是创建导入的Flask扩展的实例。def create_app(config_name): app = Flask(__name__) #创建Flask的实例app.config.from_object(config[config_name])config[config_name].init_app(app)bootstrap.init_app(app)mail.init_app(app)moment.init_app(app)db.init_app(app)login_manager.init_app(app)pagedown.init_app(app)#创建flask扩展的实例if not app.debug and not app.testing and not app.config['SSL_DISABLE']:from flask_sslify import SSLifysslify = SSLify(app)from .main import main as main_blueprintapp.register_blueprint(main_blueprint)from .auth import auth as auth_blueprintapp.register_blueprint(auth_blueprint, url_prefix='/auth')from .api_1_0 import api as api_1_0_blueprintapp.register_blueprint(api_1_0_blueprint, url_prefix='/api/v1.0')#注册蓝图到工厂函数中return app

广告结束,继续manage.py


我们来分析

app = create_app(os.getenv ('FLASK_CONFIG') or 'default')

在create_app中有如下的语句:

def create_app(config_name):app = Flask(__name__)app.config.from_object(config[config_name])

这里的os.getenv (‘FLASK_CONFIG’) or 'default’作为参数被传递给app.config.from_object(config[config_name]),其中的app是Flask的一个实例。
又一个坑:app.config中的config是Flask类的一个属性,与我们的配置文件config.py不是同一个东西,也不是config.py中的config属性。而config[config_name]得config是配置文件config.py中导入的,我们在config.py文件中看一下:

config = {'development': DevelopmentConfig,'testing': TestingConfig,'production': ProductionConfig,'heroku': HerokuConfig,'unix': UnixConfig,'default': DevelopmentConfig
}

可以发现,FLASK_CONFIG应该被设置成字典的键,根据你需要运行的模式选取’development’, ‘testing’, ‘production’, ‘heroku’, ‘unix’, ‘default’中的一个。当然,如果不在环境变量中设置的话,就默认选取的’default’。那么from_object返回的是该健对应值。例如设置’default’返回的就是’DevelopmentConfig’。
到了这里我们知道app.config.from_object(config[config_name])实际上就是app.config.DevelopmentConfig,这个config.DevelopmentConfig就是config.py中的DevelopmentConfig类。那么看看这个DevelopmentConfig:

class DevelopmentConfig(Config):DEBUG = TrueSQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')

这个DevelopmentConfig的父类是Config类,继承Config中的所有属性和方法。这样app.config.DevelopmentConfig就将DevelopmentConfig设定的配置绑定到了app——即Flask的实例上去。
当然,我们这里是以DevelopmentConfig为例,其他的配置是一样的原理。
接着执行create_app中的语句是:

config[config_name].init_app(app)

这里的init_app是在DevelopmentConfig类或其他运行环境配置类中的一个初始化方法,但值得注意的是,DevelopmentConfig类中并没有直接写入这个方法,而是继承Config类。
在config类中有一个静态方法,不执行任何操作。

@staticmethoddef init_app(app):pass

因此在DevelopmentConfig中,这个初始化实际上并没有进行,直接pass。这是因为这里根本不需要初始化,之前执行的配置就完全够了。
但是值得注意的是,在ProductionConfig中是有重写这个方法,因此会根据重写的方法对app进行初始化配置。

bootstrap.init_app(app)mail.init_app(app)moment.init_app(app)db.init_app(app)login_manager.init_app(app)pagedown.init_app(app)

?这时我们已经完成了实例化Flask,并对实例app进行了初始化配置。对于我们用到的flask扩展也进行同样的初始化配置。
坑:此处的init_app方法是各扩展中自带的方法,并不是刚才我们用到的在config.py中自己写的init_app方法,不过功能差不太多。但有时候让人挺迷惑的。

if not app.debug and not app.testing and not app.config['SSL_DISABLE']:from flask_sslify import SSLifysslify = SSLify(app)

?这里是启用安全HTTP的flask扩展flask_sslify,先略去。

from .main import main as main_blueprintapp.register_blueprint(main_blueprint)from .auth import auth as auth_blueprintapp.register_blueprint(auth_blueprint, url_prefix='/auth')from .api_1_0 import api as api_1_0_blueprintapp.register_blueprint(api_1_0_blueprint, url_prefix='/api/v1.0')

?刚才说了那么多,无非是完成了实例化和配置初始化,但是我们的对网页处理的视图函数以及网页本身的内容在哪里处理呢?
视图函数我们是交给蓝本来处理的,此处是将蓝本注册到工厂函数中去。
到此有没有发现,在manage.py中大部分事情都让工厂函数干去了,首先是创建实例,然后初始化配置,最后把工作交给蓝本去干。
根据实现的功能不同分成不同的蓝本。比如在这个程序中,认证就放在auth这个蓝本中。蓝本去分配其中的路由。通过蓝本中的视图文件去操作form和template,对数据库的操作也是在视图文件中实现。原理就是这个原理,细节就不多说了。
接下来的代码:

manager = Manager(app)
migrate = Migrate(app, db)

这是方便程序在脚本下操作的flask_script中的Manage类的实例和数据库迁移的实例。
可能大家还有一个疑问:数据库是什么时候创建的,这其实在对工厂函数初始化配置的时候,通过调用config.py中的配置已经完成了。

总结

通过运行manage.py的过程分析,发现总体框架特点如下:
manage.py——公司的总经理
工厂函数——项目经理
config.py——公司财务总监
蓝本——各技术部门经理
视图文件——广大苦逼工程师
template/form——生产工具(枪/炮)

总经理:干
项目经理:好, 财务总监,这粮和钱…, 部门经理们,我给你们说个事呗
财务总监:有的,你有我有全都有。都在这里,别客气
技术部门经理:好的。那啥,小明,小红,抄家伙
工程师:…(端着枪扛着炮就冲出去了)

《Flask Web开发:基于Python的Web应用开发实战》笔记(原创)相关推荐

  1. 《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(上)

    目录 前言 第1章 安装 第2章 程序的基本结构 第3章 模板 第4章 Web表单 第5章 数据库 第6章 电子邮件 第7章 大型程序的结构 前言 学习Python也有一个半月时间了,学到现在感觉还是 ...

  2. 《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(下)

    目录 前言 第8章 用户认证 第9章 用户角色 第10章 用户资料 第11章 博客文章 第12章 关注者 第13章 用户评论 第14章 应用编程接口   前言 第1章-第7章学习实践记录请参见:< ...

  3. flask web开发:基于python的web应用开发实战_在知乎上学 Python Web 开发篇

    通知: 1. 最近我们将进行2期学习小组,面向完全零基础的Python入门学习小组已经开始,第一次任务的讨论将于明晚(15号)进行,现在上车还来得及 2. 另有一期数据可视化小组将于年前启动,第一次讨 ...

  4. 《Flask Web开发——基于Python的Web应用开发实践》代码使用方法

    git clone https://github.com/miguelgrinberg/flasky.git 然后书上遍地的git checkout 1a这样的命令,怎么回事呢? 先把上面的命令在一个 ...

  5. CANanlystII 基于python的二次开发实践

    前期,我已经编写过一篇<CANanlystII 基于linux的二次开发实践>这篇博客承接上一篇博客,所以背景知识和测试场景,就不再赘述. 背景知识和测试场景,可以查阅如下: CANanl ...

  6. 学习《Flask Web开发:基于Python的Web应用开发实战》分享

    学习<Flask Web开发:基于Python的Web应用开发实战>分享一直在说学习Python,对同事,对朋友,都说我正在学习Python,这无形给自己一定的压力,促使自己要去学习,进步 ...

  7. Flask Web开发:基于Python的Web应用开发实战

    <Flask Web开发:基于Python的Web应用开发实战> 虽然简单的网站(Flask+Python+SAE)已经上线,但只是入门.开发大型网站,系统地学习一遍还是有必要的. 201 ...

  8. 《FlaskWeb开发:基于Python的Web应用开发实战》笔记

    开源库的cdn加速 可以在这里直接搜索复制script链接 https://www.bootcdn.cn/ requirements.txt文件的生成与使用 生成requirements文件:$ pi ...

  9. w3af 基于Python的Web应用扫描器

    全称"Web Application Attack and Audit Framework"--Web应用程序攻击审计框架.W3af是一个基于Python的Web应用扫描器.W3a ...

最新文章

  1. 机器学习中算法的性能评估
  2. go kegg_零基础 GO 与 KEGG 分析,手把手教你用多种途径实现!
  3. ssh报错 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
  4. 怎样在IDEA上将WebService接口打包部署到服务器
  5. HttpClient常用的一些常识
  6. 4.线性和卷积——不同种类的噪声、中值滤波器揭破和实战_5
  7. python 标签字体大小_这文档动画,怎么用 Python 实现的?
  8. JAVE amr转换mp3
  9. 爬虫抓包,模拟提交、Fiddler和Postman结合Chrome的使用
  10. word公式编辑器软件
  11. python打开pcap文件_python 抓包保存为pcap文件并解析的实例
  12. java连连看代码_Java版连连看
  13. 【2022省选模拟】叮叮车——卡特兰数、数位DP
  14. Android自定义IM聊天界面
  15. storyboard 苹果启动图_iOS 13使用LaunchScreen.storyboard适配各尺寸启动图
  16. Linux的目录挂载详解
  17. 2019杭电多校 第七场 Kejin Player 6656(求期望值)
  18. 2.1安装前的准备工作---安装Red Hat Linux
  19. 蚂蚁森林师生执念种上万棵树
  20. IE和Firefox兼容性

热门文章

  1. 各国晶体三极管型号命名方法
  2. 一个全国500强企业的老板是如何关爱员工的
  3. 使用sftp在客户端与服务器之间进行文件传输
  4. ubuntu 1204 server xp 硬盘安装
  5. 妈妈也曾是少女!Binking玫瑰海藻银耳羹,还妈妈一个粉红的梦!
  6. EasyUI上传图片,前台预览,后台读取
  7. Python宣传自己很快的orjson真的比ujson快吗(orjson与ujson速度比较)
  8. Android kotilin 滴+震动提示
  9. Python语法必备篇——Python字符串 学习【文末送书】
  10. 读取U盘 PID,VID,SN等信息