Flask 极致细节:0. VS Code 手动新建Flask项目
提示:此博客包含:在Win10平台,通过Visual Studio Code新建Flask项目;Anaconda3与/或virtualenv新建虚拟环境;安装Flask;运行Flask最基础程序,以及每一个细节的解释。
代码链接:https://pan.baidu.com/s/1wH1BjtUKbT2xj1ctTThQvw
密码:1zyl
文章目录
1. VS Code 从零新建Flask项目
一般的Flask教学,我们会在Pycharm Professional上创建Flask项目,而且可以一键新建项目。但我习惯使用VS Code,而且Pycharm Pro 需要付费,我又懒得尝试破解,所以这里我们使用VS Code。
下述步骤虽然看起来繁琐,但随着项目的慢慢扩大,我们将发现,这些步骤都是必不可少的。比如在虚拟环境下安装各种依赖包,将包的名称与版本包含在requirement.txt
中,自己手动新建相关的文件夹和文件,并理解其中每一个细节的含义,自己手写每一行代码。打好基础,对于今后项目的理解都大有裨益。
步骤
- 自己选择一个文件夹:VS Code, 在
File
下,点击Open Folder
。这里我就新建和选择了一个空的文件夹; - 新建文件夹:
static
,templates
; - 打开VS Code的Terminal,安装
virtualenv
并创建虚拟环境venv1
(具体步骤参见章节虚拟环境的安装与使用
); - 新建
requirements.txt
文件。此文件记录所有需要安装的包以及版本。此处,我们添加:flask==2.0.2
(我选择falsk的版本是2.0.2)。 - 我们进入虚拟环境:在terminal中输入
.\venv1\Scripts\activate
,然后安装包:pip3 install -r requirements.txt
。 - 新建
app.py
文件(服务启动)。
项目结构
当我们走完上述步骤后,Flask最基础的项目结构就完成了,罗列如下:
--项目名
|---static (静态)
|---templates (模板)
|---app.py (运行/启动)
|---venv1 (虚拟环境)
|---requirements.txt (所有安装包以及版本)
|---config.py (参数配置文件)
|---readme.md (说明文档)
对于一个web项目,往往我们会遵循 mvc
- model [模型,当用户在页面上点击了一个按钮,控制器通过模型将数据存入对应的数据库]
- view [视图,展示的页面]
- controller [控制器,如果用户点击了视图中的按钮,那么就相当于触发了一个事件,就会通过控制器,系统就需要决定把这个事件交给谁去处理]
对于Python的项目,我们会遵循 mtv
- model [模型]
- template [模板,比如网页,html]
- view [视图,类似于web的控制器,起控制作用,里面写的就是我们的python代码]
2. 虚拟环境的安装与使用
此章节将介绍virtualenv
以及Anaconda3
的虚拟环境的安装与使用。目前我们并不需要使用Anaconda3
的虚拟环境,但如果你感兴趣,也可以进行安装使用。
virtualenv
在terminal中安装virtualenv
:pip3 install virtualenv
.
然后,我们在选定的路径下安装虚拟环境:virtualenv [venv]
。[venv]
指的是虚拟环境的名称。完成此指令后,我们就能在此路径下看到一个名为[venv]
的文件夹。
进入虚拟环境:.\[venv]\Scripts\activate
;
退出虚拟环境:deactivate
;
进入虚拟环境后,在terminal中输入pip list
,便会显示在此虚拟环境中已经安装的包。默认会有三个包会被安装,显示如下:
Package Version
---------- -------
pip 21.3.1
setuptools 58.3.0
wheel 0.37.0
当我们安装完flask后(pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
),当我们再次输入pip list
,我们会发现flask以及相关的依赖以及对应的版本,显示如下:
Package Version
click 8.0.3
colorama 0.4.4
Flask 2.0.2
itsdangerous 2.0.1
Jinja2 3.0.3
MarkupSafe 2.0.1
pip 21.3.1
setuptools 58.3.0
Werkzeug 2.0.2
wheel 0.37.0
Anaconda3
目前我们不需要安装Anaconda3。但是,如有必要,请按照以下步骤操作在Win10下安装Anaconda3。在Windows 10平台中安装anaconda3不要忘记将Anaconda添加到系统路径环境变量中。也可以通过单击添加路径环境系统环境变量->环境变量
在Win10中。
在 user variables中双击 Path
. 添加 C:\Users\[用户名]\anaconda3\
, C:\Users\[用户名]\anaconda3\Scripts
, C:\Users\[用户名]\anaconda3\Library\bin
, C:\Users\[用户名]\anaconda3\Library\mingw-w64\bin
. [用户名]
指的是自己电脑的用户名。
然后打开VSCode,安装Python
扩展。在终端powershell中,尝试键入’conda’和/或’python-V’,以查看是否已经可以使用Anaconda。
我们还可以通过输入’conda create-n venvexp python=3.9’来创建一个独立的Anaconda venv。您可以通过键入’conda info-e’列出所有conda-venv,并通过键入’activate venvexp’激活。
3. 主程序:app.py
结果
在app.py
中,输入如下最基础的代码:
from flask import Flask
import settings
app = Flask(__name__)
# Flask 是一个类,我们可以点进去看详细的描述。app是Flask这个类创建出来的对象。
# __name__是获取当前文件的名字,我们可以尝试print(__name__),就会看见结果是:__main__。
#print(app.config) # 可以通过app.config查看所有参数。
app.config.from_object(settings)
@app.route('/test') # 路由
def hello_world(): # 在此路由下的视图(函数)
return 'Hello World!'
if __name__ == '__main__':
#app.run() # 启动flask内部服务器,主机地址和端口号选取默认值
app.run(port=3300,host="127.0.0.13") # 启动flask内部服务器,主机地址和端口号可自定义
当我们进入虚拟环境(在terminal中输入virtualenv venv1
),然后在主路径下输入python app.py
,你会在terminal中发现:
(base) (venv1) PS D:\yichao\learning\flask\app1> python .\app.py
* Serving Flask app 'app' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [30/Dec/2021 11:09:36] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [30/Dec/2021 11:09:37] "GET /favicon.ico HTTP/1.1" 404 -
细节
app.py
代码一共就这么几行,这里我们进行逐一阅读理解。
- 导入Flask可能会出现的问题
from flask import Flask
如果我们在输入from flask import Flask
的时候发现,flask
下面有波浪线,VS Code提示Import "flask" could not be resolved
。这说明对于VS Code来说,环境也没有改到我们刚才新建的虚拟环境。观察VS Code左下角,会显示当前的运行环境以及python版本,左键点击,然后你就能看到我们虚拟环境venv1
了。如下图所示。
- 实例化Flask类
app = Flask(__name__)
很明显,Flask 是一个类,我们可以点进去看详细的描述。app是Flask这个类创建出来的对象。第一个问题是,__name__
是什么意思。如果我们添加一行print(__name__)
,我们就会看到输出是main
。这就说明,__name__
是获取当前application的名字。
接下来,我们不妨进入Flask这个类,看看里面具体有什么。
下面是对于flask对象的描述:
The flask object implements a WSGI application and acts as the central
object. It is passed the name of the module or package of the
application. Once it is created it will act as a central registry for
the view functions, the URL rules, template configuration and much more.
The name of the package is used to resolve resources from inside the
package or the folder the module is contained in depending on if the
package parameter resolves to an actual python package (a folder with
an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file).
下面是关于第一个参数的描述,以及相关的代码:
The idea of the first parameter is to give Flask an idea of what
belongs to your application. This name is used to find resources
on the filesystem, can be used by extensions to improve debugging
information and a lot more.
So it's important what you provide there. If you are using a single
module, `__name__` is always the correct value. If you however are
using a package, it's usually recommended to hardcode the name of
your package there.
代码中你就会发现,在初始化的时候,其他的值都有默认值,只有import_name
没有。这也就是为什么实例化Flask类的时候需要输入这个__name__
值。
def __init__(
self,
import_name: str,
static_url_path: t.Optional[str] = None,
static_folder: t.Optional[t.Union[str, os.PathLike]] = "static",
static_host: t.Optional[str] = None,
host_matching: bool = False,
subdomain_matching: bool = False,
template_folder: t.Optional[str] = "templates",
instance_path: t.Optional[str] = None,
instance_relative_config: bool = False,
root_path: t.Optional[str] = None,
):
- APP Run
app.run()
问题1:为什么我们运行了这行代码后,在terminal中就会出现上个章节记录的这些信息?为了方便阅读,我也黏贴到了下面:
(base) (venv1) PS D:\yichao\learning\flask\app1> python .\app.py
* Serving Flask app 'app' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [30/Dec/2021 11:09:36] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [30/Dec/2021 11:09:37] "GET /favicon.ico HTTP/1.1" 404 -
问题2:我们需要知道,什么是WSGI
服务器?百度上的解释是:Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。自从WSGI被开发出来以后,许多其它语言中也出现了类似接口。
。也就是说,我们现在在使用的flask是一种Web框架,除此之外,Django,Tornado都是web框架,那么这些web框架需要和web服务器对接,我们就需要建立一个通用的接口进行连接和通讯。
所以,当我们运行了app.run()
后,系统自动地开始连接服务器。
问题3:http://127.0.0.1:5000/
这个IP地址和端口号是怎么来的?flask框架里面有一个内置的服务器。由于我们这个代码里面什么都没有,app.run()
后,系统默认连接并开启flask中内置的服务器。我们打开app.run()
源码:
:param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to
have the server available externally as well. Defaults to
``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable
if present.
:param port: the port of the webserver. Defaults to ``5000`` or the
port defined in the ``SERVER_NAME`` config variable if present.
所以说,这个本地的IP号以及断号是可以手动定义的,但如果不定义,那么默认IP地址为172.0.0.1
,端口号为5000
。我们也可以自行定义,如app.run(port=3300,host=192.165.2.13)
。
问题4:我们如何对app的参数进行配置/设置?我们在代码中加入print(app.config)
,然后运行程序,我们就能看到和app有关的所有配置参数(字典),如下:
<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>
我们可以直接通过字典赋值的方式对参数进行修改,比如app.config['ENV'] = 'development'
,app.config['DEBUG'] = "True"
。我们也可以将我们需要修改的所有参数都单独放入一个文件config.py
:
ENV = 'development'
DEBUG = True
在主程序中引用:
app.config.from_object(settings)
- 路由和视图函数
@app.route('/')
def hello_world():
return 'Hello World!'
如果我们将上面这段删除,重新跑一遍程序,我们会发现,http://127.0.0.1:5000/
这个链接依旧存在,但当我们点击进去后便会看到:Not Found The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
如果我们把上面这段改为:
@app.route('/test')
def hello_world():
return 'Hello World!'
然后再运行此程序。在http://127.0.0.1:5000/
下,依然显示Not Found
信息。但是如果我们输入http://127.0.0.1:5000/test
,我们就能看到Hello World
信息了。这就是路由模块的作用。如果路由模块里面包含的是\
,那么系统将在http://127.0.0.1:5000/
执行hello_world
这个函数。但如果没有上面这两段代码,虽然app.py
运行后,server启动,但里面没有东西,所以会报错。
路由的请求和响应:(网页端F12查看路由请求和响应)
浏览器地址输入的内容:http://0.0.0.0:8080/test ---->请求给到服务器 ---->服务器去app找有没有这个路由 ----> 如果有,执行路由匹配函数 ----> return 返回值 ----> 通过Response对象返回到客户端的浏览器。
路由请求:request(http协议)
- 请求行:通常包含请求地址,比如
http://0.0.0.0:8080/test
,以及请求方法是什么(get, post) - 请求头:key:value
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: max-age=0
Connection: keep-alive
Host: 192.168.1.6:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62
- 请求体:一般get请求没有请求体,post有。
路由响应:response
- 响应行:状态码:200 ok,404
- 响应头:
Content-Length: 18
Content-Type: text/html; charset=utf-8
Date: Thu, 30 Dec 2021 10:21:25 GMT
Server: Werkzeug/2.0.2 Python/3.8.5
- 响应体:Hello World! 2022!