Flask实战
留言板
创建项目目录messageboard,从GreyLi的代码中把Pipfile和Pipfile.lock文件拷贝过来,这两个文件中定义了虚拟环境中需要安装的包的信息和位置,进入messageboard目录使用pipenv创建虚拟环境,这会同时安装所有依赖(--dev选项用来包括开发依赖), 安装完成后激活虚拟环境。
安装虚拟环境
pipenv install –dev
激活:
flask shell
查看虚拟环境中安装的包:
Pipfile:用来下载依赖包的
[[source]] url = "https://pypi.python.org/simple" verify_ssl = true name = "pypi" [dev-packages] watchdog = "*" faker = "*" [packages] bootstrap-flask = "*" flask-moment = "*" flask-sqlalchemy = "*" python-dotenv = "*" flask-wtf = "*" flask = "*"
Pipfile.lock: 用来下载依赖包的
{ "_meta": { "hash": { "sha256": "90ecde6aebc889b8de105fb6b1394a6900ce33cf1be970cba63c0f6d56b158df" }, "pipfile-spec": 6, "requires": {}, "sources": [ { "name": "pypi", "url": "https://pypi.python.org/simple", "verify_ssl": true } ] }, "default": { "bootstrap-flask": { "hashes": [ "sha256:1f7462261b8104687807ce74b397270e7ade07c491ad7d53f215940d8433d756", "sha256:ce5cf19c46b8d385923dc2f9ca76b92fc08c1a8d7dcb5d177325a85a94d71045" ], "index": "pypi", "version": "==1.0.9" }, "click": { "hashes": [ "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" ], "version": "==6.7" }, "flask": { "hashes": [ "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48", "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05" ], "index": "pypi", "version": "==1.0.2" }, "flask-moment": { "hashes": [ "sha256:71a601fcd5be4742227251641cb706c109680b54c5fb25c5d2ed96e576ec3b4d", "sha256:af7ccd599d85e751ff1f7661904daa51df9950e9bc9bd4ccf174bd38ccbc401f" ], "index": "pypi", "version": "==0.6.0" }, "flask-sqlalchemy": { "hashes": [ "sha256:3bc0fac969dd8c0ace01b32060f0c729565293302f0c4269beed154b46bec50b", "sha256:5971b9852b5888655f11db634e87725a9031e170f37c0ce7851cf83497f56e53" ], "index": "pypi", "version": "==2.3.2" }, "flask-wtf": { "hashes": [ "sha256:5d14d55cfd35f613d99ee7cba0fc3fbbe63ba02f544d349158c14ca15561cc36", "sha256:d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac" ], "index": "pypi", "version": "==0.14.2" }, "itsdangerous": { "hashes": [ "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" ], "version": "==0.24" }, "jinja2": { "hashes": [ "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" ], "version": "==2.10" }, "markupsafe": { "hashes": [ "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" ], "version": "==1.0" }, "python-dotenv": { "hashes": [ "sha256:4f3582904d08dac5ab4c9aa44cb17ce056c9a35e585cfda6183d80054d247307", "sha256:cb8cd327109898c7725f76c5256a081e8a9efe72ebbf127f8d1221ceb7f38bf2" ], "index": "pypi", "version": "==0.10.0" }, "sqlalchemy": { "hashes": [ "sha256:72325e67fb85f6e9ad304c603d83626d1df684fdf0c7ab1f0352e71feeab69d8" ], "version": "==1.2.10" }, "werkzeug": { "hashes": [ "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" ], "version": "==0.14.1" }, "wtforms": { "hashes": [ "sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61", "sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1" ], "version": "==2.2.1" } }, "develop": { "argh": { "hashes": [ "sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3", "sha256:e9535b8c84dc9571a48999094fda7f33e63c3f1b74f3e5f3ac0105a58405bb65" ], "version": "==0.26.2" }, "faker": { "hashes": [ "sha256:0e9a1227a3a0f3297a485715e72ee6eb77081b17b629367042b586e38c03c867", "sha256:b4840807a94a3bad0217d6ed3f9b65a1cc6e1db1c99e1184673056ae2c0a4c4d" ], "index": "pypi", "version": "==0.8.17" }, "ipaddress": { "hashes": [ "sha256:64b28eec5e78e7510698f6d4da08800a5c575caa4a286c93d651c5d3ff7b6794", "sha256:b146c751ea45cad6188dd6cf2d9b757f6f4f8d6ffb96a023e6f2e26eea02a72c" ], "markers": "python_version == '2.7'", "version": "==1.0.22" }, "pathtools": { "hashes": [ "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0" ], "version": "==0.1.2" }, "python-dateutil": { "hashes": [ "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0", "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8" ], "version": "==2.7.3" }, "pyyaml": { "hashes": [ "sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b", "sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf", "sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a", "sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3", "sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1", "sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1", "sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613", "sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04", "sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f", "sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537", "sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531" ], "version": "==3.13" }, "six": { "hashes": [ "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" ], "version": "==1.11.0" }, "text-unidecode": { "hashes": [ "sha256:5a1375bb2ba7968740508ae38d92e1f889a0832913cb1c447d5e2046061a396d", "sha256:801e38bd550b943563660a91de8d4b6fa5df60a542be9093f7abf819f86050cc" ], "version": "==1.2" }, "watchdog": { "hashes": [ "sha256:7e65882adb7746039b6f3876ee174952f8eaaa34491ba34333ddf1fe35de4162" ], "index": "pypi", "version": "==0.8.3" } } }
使用包组织代码
把所有的代码都放在app.py里会导致可读性降低,不方便管理,我们需要更好的代码组织方式。
Flask对项目的组织方式没有要求,对于小型项目,你完全可以把代码都放在一个主模块里,随着项目越来越大,更好的处理方式是将单一的模块升级为包(package),把不同部分的代码分模块存放。
在python中,每一个有效的python文件(.py)都是模块。每一个包含__init__.py文件的文件夹都被视作包,包让你可以使用文件夹来组织模块。__init__.py文件通常被称作构造文件。文件可以为空,也可以用来放置包的初始化代码。当包或包内的模块被导入时,构造文件将被自动执行。
messageboard程序的核心组件都放到一个包中,这个包称为程序包,包的程序通常使用程序名称,即messageBoard,有时为了方便管理也会使用app作为包名称。除了程序代码,一个基本的Flask项目还包括其他必要的组件,下面列一下程序包主要组件及其功能说明:messageBoard/ --程序包
messageBoard/__init__.py --构造文件,包含程序实例
messageBoard/templates/ --模板
messageBoard/static/ --静态文件,其中又包含js和css文件夹
messageBoard/views.py --视图函数
messageBoard/forms.py --表单
messageBoard/errors.py --错误处理
messageBoard/models.py -- 数据库模型
messageBoard/commands.py -- 自定义flask命令
messageBoard/settings.py -- 配置文件
在后面的开发中,各类代码都会按照类别存储在对应的模块中。这里的模块并不是固定的,如果需要组织其他代码,那么可以自己创建对应的模块。比如,创建一个callbacks.py脚本来存储各种注册在程序实例上的处理函数。相对的,如果不需要创建自定义命令,那么也可以不创建commands.py脚本。
配置文件
在flask中,配置不仅可以通过config对象直接写入,还可以从文件中读取。在messageBoard中,把配置移动到一个单独的文件中,将其命名为settings.py(也常被命名为config.py)。当在单独的文件中定义配置时,不再使用config对象添加配置,而是直接以键值对的方式先写出,和保存环境变量的.flaskenv文件非常相似。
messageBoard/settings.py:配置文件
#encoding=utf-8 import os from messageBoard import app dev_db = 'sqlite:///' + os.path.join(os.path.dirname(app.root_path), 'data.db') #sqlite绝对路径格式 SECRET_KEY = os.getenv('SECRET_KEY', 'secret string') SQLALCHEMY_TRACK_MODIFICATIONS = False # 不追踪对象的修改 SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URI', dev__db)
除了从python脚本导入配置,Flask还提供了其他方式,比如使用from_json()方法从JSON文件中导入,或是使用from_object()方法从python对象导入。
上面的配置中,由于配置文件被放到了程序包内,为了定位到位于项目根目录的数据库文件,使用os.path.dirname(app.root_path)获取上层目录,app.root_path属性存储程序实例所在的路径。数据库URI和秘钥都会首先从环境变量获取。
在创建程序实例后,使用config对象的from_pyfile()方法即可加载配置,传入配置模块的文件名作为参数:
messageBoard/messageBoard/__init__.py:
messageBoard/messageBoard/__init__.py: app = Flask(__name__) app.config.from_pyfile('settings.py')
创建程序实例
使用包组织程序代码后,创建程序实例、初始化扩展等操作可以在程序包的构造文件(__init__.py)中实现,如下所示:
messageBoard/__init__.py: 创建程序实例、初始化扩展
#encoding=utf-8 from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask('messageBoard') app.config.from_pyfile('settings.py') app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True db = SQLAlchemy(app) from messageBoard import views, errors, commands
在单个脚本中创建程序实例时,我们传入__name__变量作为Flask类构造方法的import_name参数值。因为Flask通过这个值来确认程序路径,当使用包组织代码时,为了确保其他扩展或测试框架获得正确的路径值,我们最好以硬编码的形式写出包名称作为程序名称,即messageBoard。
除了直接写出包名称,你也可以从__name__变量获取包名称,即app=Flask(__name__.split(‘.’)[0])。
当我们启动程序时,首先被执行的是包含程序实例的脚本,即构造文件。但注册在程序实例上的各种处理程序均存放在其他脚本中,比如视图函数存放在view.py中、错误处理函数则存放在errors.py中。为了能使用程序实例app注册的视图函数,错误处理函数,自定义命令函数等和程序实例关联起来,我们需要在构造文件中导入这些模块。因为这些模块也需要从构造文件中导入程序实例,所以为了避免循环依赖,这些导入语句在构造文件的末尾定义。
从构造文件中导入变量时不需要注明构造文件的路径,只需要从包名称导入,比如导入在构造文件中定义的程序实例APP,可以使用from messageBoard import app。
Flask在通过FLASK_APP变量定义的魔窟开中寻找程序实例。所以在启动程序前,我们需要给.flaskenv(需要安装python-dotenv)中的环境变量FLASK_APP重新赋值,这里仅写出包名称即可:
messageBoard/.flaskenv:
FLASK_APP=messageBoard