此章和教程出入不大,可直接看原教程
- 之前写的代码耦合太重,python也可以像js模块化一样模块化程序结构
模块化
- 使用 蓝图(蓝本),我们之前的程序都有一个Flask的实例
app = Flask(__name__)
- 这个app变量可以定义路由,蓝图就是可以将路由分门别类,然后在组合在一起
- 需要重新设置一下目录结构:
.
├── app
│ ├── admin
│ │ ├── errors.py
│ │ ├── forms.py
│ │ ├── __init__.py
│ │ └── views.py
│ ├── __init__.py
│ ├── main
│ │ ├── forms.py
│ │ ├── __init__.py
│ │ └── views.py
│ ├── models.py
│ ├── static
│ └── templates
│ ├── 404.html
│ ├── 500.html
│ ├── admin
│ │ ├── login.html
│ │ └── register.html
│ ├── base.html
│ ├── index.html
│ └── user.html
├── config.py
├── manage.py
- Flask 程序一般都保存在名为app的包中.
- config.py 保存着配置
- manage.py 用于启动项目
说明一下python 的包(package),从目录结构上看,python的package有两部分组成: 文件夹和init.py 文件. 正是因为init.py 的存在 python编译器才会把那个文件夹当作是一个python的包来看待. 而那个 init.py 的效果就是, 能够有一个与包名字相同的文件. 什么意思呢?
比如 我们有一个名字为 main 的包, 那么
from main import *
这行代码中,从main包中import所有的东西,你想啊,main是个包,import进来是啥??? 其实阿,import进来的是init.py中的内容.
- 把原先那个blog.py中的东西复制一下就好了:
- config.py:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = 'a string'
# 数据库配置
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
SQLALCHEMY_COMMIT_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = False
@staticmethod
def init_app(app):
pass
仿照狗书,创建一个程序工厂函数. 设计模式中有一个模式叫做:工厂模式,比如你需要一个东西,但这个东西你需要配置很多,这样你就可以用到工厂模式,在把配置(比如汽车厂,把各个零件组装起来)的活交给工厂,那么工厂出来的产品就是好的产品.这样可以降低程序的耦合度,怎么理解呢,如果这个产品是坏的,那么你也不需要到处去程序的代码,只需要去那个工厂的程序中去寻找bug就好了
程序工厂函数:
说一下工厂函数,我们之前单个文件开发程序很方便,但却有个很大的缺点,因为程序在全局作用中创建,所以无法动态修改配置.运行脚本时,实例已经创建,再修改配置为时已晚,解决问题的方法就是延迟创建程序实例,把创建过程移到可显式调用的工厂函数中.
- app/init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
Config.init_app(app)
db.init_app(app)
return app
这段代码,我们的db实例,先于app创建,app的创建只能调用了create_app()函数之后才创建.
蓝图:
Flask 中的蓝图为这些情况设计:
- 把一个应用分解为一个蓝图的集合。这对大型应用是理想的。一个项目可以实例化一个应用对象,初始化几个扩展,并注册一集合的蓝图。
- 以 URL 前缀和/或子域名,在应用上注册一个蓝图。 URL 前缀/子域名中的参数即成为这个蓝图下的所有视图函数的共同的视图参数(默认情况下)。
- 在一个应用中用不同的 URL 规则多次注册一个蓝图。
- 通过蓝图提供模板过滤器、静态文件、模板和其它功能。一个蓝图不一定要实现应用或者视图函数。
- 初始化一个 Flask 扩展时,在这些情况中注册一个蓝图。
蓝图问题,最后总结:
- 在蓝本中定义的路由处于休眠状态,直到蓝本注册到程序上后,路由才真正成为程序的一部分
创建蓝本:
- app/main/init
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
蓝图是通过实例化Blueprint类对象来创建的。这个类的构造函数接收两个参数:蓝图名和蓝图所在的模块或包的位置。与应用程序一样,在大多数情况下,对于第二个参数值使用Python的name变量即可。
应用程序的路由都保存在app/main/views.py模块内部,而错误处理程序则保存在app/main/errors.py中。导入这些模块可以使路由、错误处理与蓝图相关联。重要的是要注意,在app/init.py脚本的底部导入模块要避免循环依赖,因为view.py和errors.py都需要导入main蓝图
注册蓝图:
- app/init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
Config.init_app(app)
db.init_app(app)
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
#通过register_blueprint方法将蓝图注册进来
return app
- app/main/errors.py:
from flask import render_template
from . import main
@main.app_errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@main.app_errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
- app/main/views.py:
from flask import render_template
from flask import session
from flask import redirect
from flask import url_for
from flask import request
from . import main
from ..models import User
from .. import db
@main.route('/', methods=['GET', 'POST'])
def index():
if 'name' in request.args:
name = request.args['name']
return render_template('user.html', username=name)
else:
return render_template('index.html')
@main.route('/register', methods=['POST'])
def register():
username = request.form['username']
password = request.form['password']
if username:
user = User.query.filter_by(name=username)
if user is None:
##添加进数据库
user = User(name=username)
##如果没有app.config['SQLALCHEMY_COMMIT_TEARDOWN'] = True
##这句后面还要加上db.session.commit()才会提交到数据库
db.session.add(user)
session['known'] = False
else:
session['known'] = True
session['name'] = username
name=session.get('name')
return '你的用户名是 %s' % name
- manage.py:
from flask_script import Manager
from app import create_app, db
app = create_app()
manager = Manager(app)
if __name__ == '__main__':
manager.run()
- app/models.py:
from . import db
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
# 如果你学过数据库的话就知道我们一般通过id来作为主键,来找到对应的信息的,通过id来实现唯一性
name = db.Column(db.String(64), unique=True)
def __repr__(self):
return 'users表: id为:{}, name为:{}'.format(self.id, self.name)
- 在终端输入:
python manage.py shell
>>> from manage import *
>>> db
>>> db.drop_all()
>>> db.create_all()
>>> exit()
- 这里把表重新创建了一下,因为表已经移动位置了.
然后:
python manage.py runserver
然后就可以正常运行了