Flask实战-留言板-安装虚拟环境、使用包组织代码

Flask实战

留言板

创建项目目录messageboard,从GreyLi的代码中把Pipfile和Pipfile.lock文件拷贝过来,这两个文件中定义了虚拟环境中需要安装的包的信息和位置,进入messageboard目录使用pipenv创建虚拟环境,这会同时安装所有依赖(--dev选项用来包括开发依赖), 安装完成后激活虚拟环境。

 

安装虚拟环境

pipenv install –dev

 Flask实战-留言板-安装虚拟环境、使用包组织代码

 

激活:

flask shell

Flask实战-留言板-安装虚拟环境、使用包组织代码

 

查看虚拟环境中安装的包:

Flask实战-留言板-安装虚拟环境、使用包组织代码

 

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

 

上一篇:解决用SHA256算法做私钥签名时,遇到“指定的算法无效”的问题


下一篇:记录k8s下配置ssl安全连接版rabbitmq