结构,如图:python
Flask最上层是app核心对象
,在这个核心对象上能够插入不少蓝图,这个蓝图是不能单独存在的,必须将app做为插板插入app ,在每个蓝图上,能够注册不少静态文件,视图函数,模板 ,一个业务模块能够作为一个蓝图,好比book,以前的book.py 放到了app/web/路径下,就是考虑到了蓝图,app属因而整个Flask应用层,web属因而蓝图mysql
一些初始化操做应该放入到__init__
文件中,好比Flask的核心应用app初始化对象,应该放入到在应用层级app包的 __init__.py
中 ,而蓝图的初始化应该放入到蓝图层的web包__init__.py
中,如图:git
Flask的核心应用app初始化对象文件app/__init__.py
web
# -*- coding: utf-8 -*- from flask import Flask def create_app(): app = Flask(__name__) app.config.from_object('config') # 要返回回去 return app
# -*- coding: utf-8 -*- from app import create_app app = create_app() if __name__ == '__main__': app.run(debug=app.config['DEBUG'])
app/web/book.py
中,记得导入Blueprint# -*- coding: utf-8 -*- from flask import jsonify, Blueprint from helper import is_isbn_key from ShanqiuBook import ShanqiuBook # 蓝图 blueprint,进行初始化,蓝图的名字和参数为蓝图所在的模块名通常用__name__ web = Blueprint ('web',__name__) # 此时这里用的就是web了 @web.route('/book/search/<q>/<page>') def hello(q,page): is_or_key = is_isbn_key(q) if is_or_key == 'isbn': result = ShanqiuBook.search_by_isbn(q) else: result = ShanqiuBook.search_by_keyword(q) return jsonify(result)
app/__init__.py
# -*- coding: utf-8 -*- from flask import Flask def create_app(): app = Flask(__name__) app.config.from_object('config') # 调用一下就能够 register_blueprint(app) return app # 经过这个方法插入到app中 def register_blueprint(app): from app.web.book import web # 注册这个蓝图对象 app.register_blueprint(web)
# -*- coding: utf-8 -*- from flask import jsonify, Blueprint from helper import is_isbn_key from ShanqiuBook import ShanqiuBook # 导入web模块 from . import web @web.route('/book/search/<q>/<page>') def hello(q,page): # 调用方法判断用户是根据什么查的 is_or_key = is_isbn_key(q) if is_or_key == 'isbn': result = ShanqiuBook.search_by_isbn(q) else: result = ShanqiuBook.search_by_keyword(q) return jsonify(result)
# -*- coding: utf-8 -*- # 导入web模块 from . import web @web.route("/user/login") def login(): return "success"
app/web/__init__.py
文件中,定义这个蓝图对象# -*- coding: utf-8 -*- # 蓝图 blueprint,进行初始化 from flask import Blueprint web = Blueprint ('web',__name__) # 这两个导入以后就能够成功的运行对应模块中相关的代码,注意这个位置,这蓝图实例化以后 from app.web import book from app.web import user
/book/search/<q>/<page>
这种格式的,Flask会将<>里的值自动映射成视图函数方法的参数,可是这种格式用着不爽,要把用户输入的参数做为请求参数传入,这个时候就要使用这种格式了http://127.0.0.1:5000/book/search/?q=金庸&page=1
# -*- coding: utf-8 -*- # 导入这个request模块, from flask import jsonify, Blueprint,request from helper import is_isbn_key from ShanqiuBook import ShanqiuBook from . import web # http://127.0.0.1:5000/book/search/?q=金庸&page=1 @web.route('/book/search/') def hello(): # 经过Request对象拿到对应值的信息,可是这个并非py中原始的字典,而是dict的子类immutableDict q = request.args['q'] page = request.args['page'] # ip = request.remote_addr # 经过这个方法把它转换为普通的dict # a = request.args.to_dict() # print(a) is_or_key = is_isbn_key(q) if is_or_key == 'isbn': result = ShanqiuBook.search_by_isbn(q) else: result = ShanqiuBook.search_by_keyword(q) return jsonify(result)
(flask--yQglGu4) E:\py\qiyue\flask>pipenv install wtforms
# -*- coding: utf-8 -*- # 导入须要使用的模块 from wtforms import Form,StringField,IntegerField from wtforms.validators import Length,NumberRange class SearchForm(Form): # 直接调用内置对象 # 参数校验规则: # 1.定义的属性名q,page要与要校验的参数同名 # 2.根据要传入的参数类型选择不一样的Field类进行实例化 # 3.传入一个数组,做为校验规则validators # 4.能够设置默认值 q = StringField(validators=[DataRequired(),Length(min=1,max=30)]) page = IntegerField(validators=[NumberRange(min=1,max=10)],default=1)
# -*- coding: utf-8 -*- from flask import jsonify, Blueprint,request from helper import is_isbn_key from ShanqiuBook import ShanqiuBook from . import web # 导入参数校验 from app.forms.book import SearchForm # http://127.0.0.1:5000/book/search/?q=金庸&page=1 @web.route('/book/search/') def hello(): # 验证层 # 实例化咱们自定义的SearchForm,须要传入一个字典做为要校验的参数 form = SearchForm(request.args) # validate()方法返回True/False来标示是否校验经过 if form.validate(): # 从form中取出校验后的q与page,而且清除空格 q = form.q.data.strip() page = form.page.data is_or_key = is_isbn_key(q) if is_or_key == 'isbn': result = ShanqiuBook.search_by_isbn(q) else: result = ShanqiuBook.search_by_keyword(q) return jsonify(result) else: return jsonify({'msg':'参数校验失败'})
@classmethod def search_by_key(cls, q, count=15, start=0): # count:每页显示的数量 # start:每页的第一条数据的下标 url = cls.search_by_key_url.format(q, count, start) return HTTP.get(url)
这样写很是的不妥sql
在视图函数中接收到的参数是page,代码的封装性,咱们应该把count和start的计算过程放到ShanqiuBook.py的 search_by_key方法中来写数据库
count的值为了方便往后的管理,这个应该放入到配置文件中,以前的配置文件是config.py,在根目录下,而这个应该放入到app目录下,而关于一些比较隐私的配置信息要妥善处理,因此在app目录下创建两个文件,secure.py用来存放私密的配置信息,setting.py用于存放一些不重要的配置信息,以下json
app/secure.pyflask
# -*- coding: utf-8 -*- # 存放比较机密的配置文件,在上传git的时候不该该上传此文件 DEBUG = True
app/setting.pyapi
# -*- coding: utf-8 -*- # 生产环境和开发环境几乎同样的,不怎么机密的配置文件 # 每页显示的数据量 PER_PAGE = 15
start的计算是一个单独的逻辑,应该用封装成一个方法,使用的时候直接调用数组
# 获取每一页的起始下标 @staticmethod def calculate_start(page): # 获取配置信息中的每页显示的数量 return (page -1 ) * current_app.config['PER_PAGE']
重构后的ShanqiuBook.py
-*- coding: utf-8 -*- from httper import httper # 经过这种方式来导入当前的app对象,方便调用配置而文件 from flask import current_app class ShanqiuBook: isbn_url = 'http://t.yushu.im/v2/book/search/isbn/{}' keyword_url = 'http://t.yushu.im/v2/book/search?q={}&count={}&start={}' @classmethod def search_by_isbn(cls,isbn): url = cls.isbn_url.format(isbn) result = httper.get(url) return result @classmethod def search_by_keyword(cls,keyword,page=1): # 每页显示的数据(经过这种方式从配置文件中获取到),每一页的起始下标 url = cls.keyword_url.format(keyword,current_app.config['PER_PAGE'],cls.calculate_start(page)) result = httper.get(url) return result # 获取每一页的起始下标 @staticmethod def calculate_start(page): return (page -1 ) * current_app.config['PER_PAGE']
app/__init__.py
文件中把配置文件添加到app中#-- coding: utf-8 -- from flask import Flask def create_app(): app = Flask(name) # app.config.from_object('config') # 把配置文件装载进来 app.config.from_object('app.secure') app.config.from_object('app.setting') register_blueprint(app) return app def register_blueprint(app): from app.web.book import web app.register_blueprint(web)
在app/models/book.py文件中创建模型,这里使用到sqlalchemy来实现自动化映射,在Flask框架中对这个进行了改良Flask_SQLAlchemy,这个更加人性化,安装(flask--yQglGu4) E:\py\qiyue\flask>pipenv install flask-sqlalchemy
创建模型,这样就创建好了模型
# -*- coding: utf-8 -*- # 首先导入 from sqlalchemy import Column,Integer,String # sqlalchemy,自动化映射 # Flask_SQLAlchemy,这个是Flask封装后的api,更加人性化 class Book(): # 须要把这些属性的默认值写成sqlalchemy提供的固定的类型 # Column()传入参数:数据类型,主键,自增 id = Column(Integer,primary_key=True,autoincrement=True) # 数据类型,不为空 title = Column(String(50),nullable=False) author = Column(String(30),default='未名') binding = Column(String(20)) publisher = Column(String(50)) price = Column(String(20)) pages = Column(Integer) pubdate = Column(String(20)) # 惟一:unique=True isbn = Column(String(15),nullable=False,unique=True) summary = Column(String(1000)) image = Column(String(50)) # 定义一些方法 def sample(self): pass
# -*- coding: utf-8 -*- from sqlalchemy import Column,Integer,String # 将模型映射到数据库中 # 首先导入核心的对象 from flask_sqlalchemy import SQLAlchemy # 初始化 db = SQLAlchemy() # 继承db.Model class Book(db.Model): id = Column(Integer,primary_key=True,autoincrement=True) title = Column(String(50),nullable=False) author = Column(String(30),default='未名') binding = Column(String(20)) publisher = Column(String(50)) price = Column(String(20)) pages = Column(Integer) pubdate = Column(String(20)) isbn = Column(String(15),nullable=False,unique=True) summary = Column(String(1000)) image = Column(String(50))
app/__init__.py
中进行模型与flask关联# -*- coding: utf-8 -*- from flask import Flask # 导入这个db from app.models.book import db def create_app(): app = Flask(__name__) app.config.from_object('app.secure') app.config.from_object('app.setting') register_blueprint(app) # 把这个db和核心对象关联起来了 db.init_app(app) # 注意这里,这样写的话会报错 db.create_all() # 把全部的数据模型映射到数据库中 return app def register_blueprint(app): from app.web.book import web app.register_blueprint(web)
# -*- coding: utf-8 -*- # 存放比较机密的配置文件 DEBUG = True # 数据库链接url,固定格式 # 要链接的数据库类型,数据库驱动(这里还要进行安装:pipenv install cymysql) SQLALCHEMY_DATABASE_URI = 'mysql+cymysql://root:123456@localhost:3306/book'
'No application found. Either work inside a view function or push'
这个是由于在Flask中,不是实例化了app核心对象,其余代码就能够直接使用,要在上面的第二步的注意事项中` db.create_all()`方法中,把app核心对象传入便可
db.create_all(app=app)
,这样就能够了,在数据库中就能够看到表了