flask项目1实战:1、项目准备(二)工程开发环境准备(待进一步细化完善)

在这里插入图片描述

(根据居然老师直播课内容整理)

一、准备工作

  • 经过分析,项目需要用f以下技术
    • lask框架前后端分离
    • mysql数据库
    • redis数据库
    • 工程日志
    • 前端静态文件

1.创建虚拟环境

pipenv shell

2、安装Flask框架及相关包

  • 需要用到以下安装包
    • Flask框架
    • flask-sqlalchemy 数据库连接
    • 2.3 redis 数据库连接
    • 2.4 flask-wtf :表单处理
    • 2.5 flask-session :session 和 cookies应用
pip install flask 
pip install flask-sqlalchemy
pip install redis
pip install flask-wtf
pip install flask_session

3、创建Flask工程

二、配置环境

  • 根据项目分析,项目需要准备以下内容

    1. flask框架
  • 2、mysql

  • 3、redis

  • 4、session:参数SESSION_TYPE和SESSION_REDIS保存到redis数据库中,并对session签名

  • form验证表单需要加密

  • flask框架入口文件一般用manage.py,现在把所有可能用的到东西都写到一个入口文件,再进行拆分

1、入口文件准备

  • 入口文件需要准备以下内容:
    1、配置参数:定义成一个配置类,保存配置参数
  • mysql数据库配置参数
    • 安装并启动mysql数据库,并定义相关配置参数
  • redis数据库配置参数:注册时可能会用到图片和短信验证,保存到redis数据库中
    • 安装redis数据库
  • session保存redis数据库中时,需要配置
    2、加载配置文件
    3、创建mysql数据库
    4、创建redis数据库
    5、session绑定app
    6、form验证表单 wtf csrf
# manage.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_session import Session
import redis
from flask_wtf import CSRFProtect


app=Flask(__name__)

class Config():
    """配置信息"""
	SECRET_KEY="ASDFLKASDKJF%AKLSDXO$IPPIUF"  # 字符串随便设,用于session签名使用
    # mysql数据库配置
    # 连接数据库
    MYSQL_HOSTNAME = '127.0.0.1'
    # 数据库
    MYSQL_DATABASE = 'testdb'
    # 端口
    MYSQL_PORT = 3306
    # 用户名和密码
    MYSQL_USERNAME = 'root'
    MYSQL_PASSWORD = 'root'
    # mysql+pymysql://用记名:密码@址址:端口/数据库
    DB_URL = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_HOSTNAME, MYSQL_PORT, MYSQL_DATABASE)

    # redis 配置
    REDIS_HOSTNAME="127.0.0.1"
    REDIS_PORT=6379

    # flask-session
    SESSION_TYPE = 'redis'
    SESSION_REDIS = redis.Redis(host=REDIS_HOSTNAME, port=REDIS_PORT)
    SESSION_USE_SIGNER = True	# session 签名
    PERMANENT_SESSION_LIFETIME = 86400  # session生命周期,单位是秒,1天=60*60*24

# 加载配置参数
app.config.from_object(Config)

# 建立mysql数据库对象
db=SQLAlchemy(app)


# 创建Redis
redis_store = redis.Redis(host=Config.REDIS_HOSTNAME, port=Config.REDIS_PORT)

# session
Session(app)

# post请求  wtf  csrf
CSRFProtect(app)

app.route("/")
def index():
    return "hello world!"

if __name__=="__main__":
    app.run()

2、入口文件拆分

2.1 拆分配置文件

  • 项目配置文件一般都是单独的文件,故把 配置类拆分到配置文件中
  • 项目一般都会分为开发环境和生产环境,所以再创建两个类,用于区分开发和生产环境,有些参数都是共用,所以两个类共用公共部分基类
  • 加载配置文件时,怎么区分哪个是生产环境哪个是开发环境,有两种方式,一般对app定义采用工厂模式,传递参数,在函数内进行区分
    • 方式一:用if判断进行区分
    • 方式二:用字典引用

2.1.1 拆分后的配置文件

import redis

class Config():
    """配置信息"""
    SECRET_KEY = 'ASDAXCWE5ERTFG%%DAS34'   # 这个字符串自己随便写,用于session加密

    # mysql配置
    MYSQL_HOSTNAME = '127.0.0.1'
    MYSQL_DATABASE = 'testdb'
    MYSQL_PORT = 3306
    MYSQL_USERNAME = 'root'
    MYSQL_PASSWORD = 'root'
    # mysql+pymysql://用记名:密码@址址:端口/数据库
    DB_URL = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_HOSTNAME, MYSQL_PORT,MYSQL_DATABASE)


    # redis 配置
    REDIS_HOSTNAME = "127.0.0.1"
    REDIS_PORT = 6379

    # flask-session  session保存到redis中
    SESSION_TYPE = 'redis'

    SESSION_REDIS = redis.Redis(host=REDIS_HOSTNAME, port=REDIS_PORT)
    SESSION_USE_SIGNER = True               # 对session签名,即加密
    PERMANENT_SESSION_LIFETIME = 86400  # session生命周期, 单位是秒, 60*60*24=86400

class DevConfig(Config):
    """开发环境"""
    DEBUG = True

class ProConfig(Config):
    """生产环境"""

config_map = {
    'dev': DevConfig,
    'pro': ProConfig
}

2.1.2 对manage的改造

# manage.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_session import Session
from flask_wtf import CSRFProtect
from config import config_map

# 工厂模式
def create_app(config_name):

    app = Flask(__name__)

    # if config_name == "dev":
    #     from config import DevConfig
    #     app.config.from_object(DevConfig)
    # else:
    #     from config import ProConfig
    #     app.config.from_object(ProConfig)

    config_class = config_map.get(config_name) 	# 获取配置文件,用字典get方法可防止config_name不存时报错
    app.config.from_object(config_class)				# 加载配置文件

    return app

app=create_app("dev")

# 建立mysql数据库对象
db=SQLAlchemy(app)

# 创建Redis
redis_store = redis.Redis(host=Config.REDIS_HOSTNAME, port=Config.REDIS_PORT)

# session
Session(app)

# post请求  wtf  csrf
CSRFProtect(app)

app.route("/")
def index():
    return "hello world!"

if __name__=="__main__":
    app.run()

2.2 对应用进行分模块

  • 将业务逻辑蓝图放到相应的包中:建api_1_0包,即V1.0蓝图包
  • 建立静态文件目录: static
  • 建立自已定义的工具包:utils
  • 建立第三工具目录:libs
  • 建立日志目录:logs
  • 为进一步区分入口文件、公共文件及目录和业务逻辑目录,需要建立一个业务逻辑包lghome,将业务逻辑相关内容放到此包中,即将 api_1_0、static、utils、libs都移到此目录下
    在这里插入图片描述

2.3 对入口文件进一步拆分

  • 建立了业务逻辑包以后,入口文件中,业务逻辑相关的功能,都将移到相应的包中
  • 1、工厂模式定义的create_app、数据库定义、session、csrf 都移到lghome包的__init__中
  • 2、app路由移到api_1_0包中的一个文件里,以后再完善
  • 3、简化manage入口文件

2.3.1 将业务逻辑相关的代码移到lghome包的__init__中

  • 将相关代码移到__init__.py后,暂不处理报错,以后再优化
# ./lghome/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_session import Session
from flask_wtf import CSRFProtect
from config import config_map

# 工厂模式
def create_app(config_name):

    app = Flask(__name__)

    # if config_name == "dev":
    #     from config import DevConfig
    #     app.config.from_object(DevConfig)
    # else:
    #     from config import ProConfig
    #     app.config.from_object(ProConfig)

    config_class = config_map.get(config_name) 	# 获取配置文件,用字典get方法可防止config_name不存时报错
    app.config.from_object(config_class)				# 加载配置文件

    return app

# 建立mysql数据库对象
db=SQLAlchemy(app)

# 创建Redis
redis_store = redis.Redis(host=Config.REDIS_HOSTNAME, port=Config.REDIS_PORT)

# session
Session(app)

# post请求  wtf  csrf
CSRFProtect(app)

2.3.2 将路由代码暂时移到蓝图api_1_0包的demo.py中

# ./lghome/api_1_0/demo.py

# app.route("/")
# def index():
#     return "hello world!"

2.3.3 优化入口文件manage.py

from lghome import create_app

app = create_app('dev')

if __name__ == '__main__':
    app.run()

3、初步优化lghome包__init__.py

上面将代码移到__init__.py时,数据库定义、session、csrf 未处理

3.1 完善数据库定义

  • 将 db和redis_store 定义放到 create_app函数前面,但不绑定,方便其它项目引用,当app初始化后,再进行绑定
# mysql数据库定义
db = SQLAlchemy()
# 创建Redis
redis_store = None
  • app在create_app函数中定义,故db 和 redis_store放在create_app中绑定
    • redis_store 定义为None ,所以绑定时重新赋值即可,即在函数内用global 重新赋值
    # 使用app初始化db
    db.init_app(app)

    # redis配置
    global redis_store
    redis_store = redis.Redis(host=config_class.REDIS_HOST, port=config_class.REDIS_PORT)

3.2 完善session、csrf 定义

  • session 和 csrf 无需单独定义,直接在create_app中定义即可
   # session
    Session(app)
    
   # post请求  wtf  csrf
    CSRFProtect(app)

3.3 初步优化后的__ini__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_session import Session
from flask_wtf import CSRFProtect
from config import config_map
import redis

db = SQLAlchemy()

# 创建Redis
redis_store = None

# 工厂模式
def create_app(config_name):

    app = Flask(__name__)

    config_class = config_map.get(config_name)
    app.config.from_object(config_class)

    # 使用app初始化db
    db.init_app(app)

    # redis配置
    global redis_store
    redis_store = redis.Redis(host=config_class.REDIS_HOST, port=config_class.REDIS_PORT)

    # session
    Session(app)

    # post请求  wtf  csrf
    CSRFProtect(app)

4、优化数据库参数设置

  • 因数据库参数化时未定义 SQLALCHEMY_DATABASE_URI
  • 未设置 SQLALCHEMY_TRACK_MODIFICATIONS
  • 故修改配置文件即可
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_HOSTNAME, MYSQL_PORT,MYSQL_DATABASE)
    SQLALCHEMY_TRACK_MODIFICATIONS = False

完整代码如下:

import redis

class Config():
    """配置信息"""

    SECRET_KEY = 'ASDAXCWE5ERTFG%%DAS34'   # 这个字符串自己随便写,用于session加密

    # mysql配置
    MYSQL_HOSTNAME = '127.0.0.1'
    MYSQL_DATABASE = 'testdb'
    MYSQL_PORT = 3306
    MYSQL_USERNAME = 'root'
    MYSQL_PASSWORD = 'root'
    # mysql+pymysql://用记名:密码@址址:端口/数据库
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_HOSTNAME, MYSQL_PORT,MYSQL_DATABASE)
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    # redis 配置
    REDIS_HOSTNAME = "127.0.0.1"
    REDIS_PORT = 6379

    # flask-session  session保存到redis中
    SESSION_TYPE = 'redis'

    SESSION_REDIS = redis.Redis(host=REDIS_HOSTNAME, port=REDIS_PORT)
    SESSION_USE_SIGNER = True               # 对session签名,即加密
    PERMANENT_SESSION_LIFETIME = 86400  # 单位是秒,session生命周期,  60*60*24=86400

class DevConfig(Config):
    """开发环境"""
    DEBUG = True


class ProConfig(Config):
    """生产环境"""

config_map = {
    
    
    'dev': DevConfig,
    'pro': ProConfig
}

5、入口文件中进行数据库迁移

  • flask框架使用数据库,需要对数据进行迁移,并将app启动改为manage启动
    • 数据库迁移时,注意导入db
from lghome import db
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

# 数据库迁移
manage = Manager(app)
Migrate(app, db)
manage.add_command('db', MigrateCommand)
  • manage.py完善后代码
from lghome import create_app, db
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

app = create_app('dev')

# 数据库迁移
manage = Manager(app)
Migrate(app, db)
manage.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manage.run()

6、初步完善视图文件

6.1 定义视图文件

  • 定义蓝图,并初步定义首页路由
    • @app.route("/")改为 @api.route("/")
./lghome/api_1_0/demo.py
from flask import Blueprint

api = Blueprint('api_1_0', __name__, url_prefix='/api/v1.0')

@api.route("/")
def index():
    return "index page"

6.2 将 蓝图定义移到api_1_0包的__init__.py中

  • 因为蓝图定义,在整个包中都会使用,故需要移到__init__.py 中,方便引用
  • 在视图文件中引用蓝图api
  • 在__init__.py中将蓝图定义与路由关联起来(注意:如果不执行此步骤,系统不会报错,但无法正确显示页面)

在这里插入图片描述

# ./lghome/api_1_0/__init__.py
from flask import Blueprint

api = Blueprint('api_1_0', __name__, url_prefix='/api/v1.0')

from . import dmeo

6.3 将蓝图与app绑定

  • 蓝图定义后,与app绑定后,才能影响前端的请求,即在create_app函数中将蓝图与app绑定
    • app.register_blueprint(蓝图的实例名)
# ./lghome/__init__.py
...
def create_app(config_name)
	... ...
	
    # 注册蓝图
    from lghome import api_1_0
    app.register_blueprint(api_1_0.api)

7.到此为止,项目入口文件已拆分完成

  • 运行系统即可访问: http://127.0.0.1/api/v1.0/index
    在这里插入图片描述
    运行结果如下:

8.可能遇到的坑

  • 可能会出现循环导入,例:对db引用:
    • manage.py中需要导入db

    • lghome/init.py 中定义了db ,且引用了 lghome/api_1_0/init.py 中的api ,此时db 未创建

    • lghome/api_1_0/init.py中,引用了lghome/api_1_0/demo.py

    • lghome/api_1_0/demo.py 定义了路由,需要访问数据库,引了 db ,但db未创建,至此形成了循环引用。
      在这里插入图片描述

    • 解决办法:

  • 在 lghome/init.py 中 工厂模式create_app函数内,注册蓝图时再引用api,如下图:
    在这里插入图片描述
  • 即:
    • manage.py中需要导入db
    • lghome/init.py 中定义了db ,必入时未执行create_app函数,就未引用了 lghome/api_1_0/init.py 中的api ,可顺利完成引用, 随后db 已创建,完成引用
    • 当manage.py中调用create_app函数时,引用了 lghome/api_1_0/init.py 中的api
    • lghome/api_1_0/init.py中,引用了lghome/api_1_0/demo.py
    • lghome/api_1_0/demo.py 定义了路由,需要访问数据库,引了 db ,但db已创建,完美的避开了循环引用。

三、项目日志配置

  • 项目框架搭建时,需要搭建日志记录功能模块,支持系统各模块记录日志使用

1、创建日志模块方法

# 日志
def setup_log():
    # 设置日志的的登记  DEBUG调试级别
    logging.basicConfig(level=logging.DEBUG)
    # 创建日志记录器,设置日志的保存路径和每个日志的大小和日志的总大小
    # log 100M  log1  log
    file_log_handler = RotatingFileHandler("logs/log.log", maxBytes=1024*1024*100,backupCount=100)
    # 创建日志记录格式,日志等级,输出日志的文件名 行数 日志信息
    formatter = logging.Formatter("%(levelname)s %(filename)s: %(lineno)d %(message)s")
    # 为日志记录器设置记录格式
    file_log_handler.setFormatter(formatter)
    # 为全局的日志工具对象(flaks app使用的)加载日志记录器
    logging.getLogger().addHandler(file_log_handler)

2、启动日志

  • 在create_app()时,启动日志
  • 在这里插入图片描述

3、记录日志

  • 在需要记录日志的地方,调用logger即可
# 创建日志记录器
logger = logging.getLogger('django')
# 输出日志
logger.debug('测试logging模块debug')
logger.info('测试logging模块info')
logger.error('测试logging模块error')

4、日志更多操作详见《Flask中日志logging的使用》

四、通过蓝图方式调用静态html提供前端界面

  • 本项目是前后端分离的开发模式,为开发调试方便,通过蓝图方式提供调用静页面实现前端界面

  • 这里创建的蓝图与api_1_0的蓝图没有关系。

  • 例本项目将:静态文件保存到lghome/static/目录下
    在这里插入图片描述

  • 可以这样访问:

    • http://127.0.0.1:5000/static/html/index.html

    • 但直接将服务器的路径都暴露了

  • 建议访问方式:

    • 127.0.0.1:5000/ 访问首页
    • 127.0.0.1:5000/index.html 访问首页
    • 127.0.0.1:5000/register.html 访问注册
  • 为发达到这个效果,需要进行路由转换,自定义一个路由转换器

1、自定义一个路由转换器

  • 在lghome/utils下创建 路由转换器
from werkzeug.routing import BaseConverter

class ReConverter(BaseConverter):
    def __init__(self,map,regex):
        super().__init__(map)
        self.regex=regex

2、将自定义的路由转换器添加到

  • 路由转换器定义后,需要添加到flask框加中
  • 在注册app 时,将自定义路由转换器添加到 app.url_map.converters字典中
app.url_map.converters["re"]=ReConverter

3、定义静态html蓝图及路由

  • 在lghome下创建蓝图文件commons.py
    • 定义蓝图
    • 定义通用路由:根据自定路由转换器
    • 静态html文件在static/home下面,少了一级目录,所以把home加上即可
    • 如果没有输入页面(没有传递参数),应该访问主页,判断一下改成index.html
# /lghome/commons.py

from flask import Blueprint,current_app

html=Blueprint("web_html",__name__)

@html.route("/<re('.*'):html_file_name>")
def get_html(html_file_name):

    # 访问首页
    if not html_file_name:
        html_file_name="index.html"
        
	html_file_name = 'html/' + html_file_name

    return current_app.send_static_file(html_file_name)

4、注册静态蓝图

  • 在create_app函数中注册静态蓝图
    # 注册静态蓝图
    from lghome import web_html
    app.register_blueprint(web_html.html)

五、数据库模型准备

  • 数据库设计需要根据实际需求来设计

1、用户表user

  • 通地注册界面分析,user表应该有:id、mobile、password
  • 通过个人信息分析,user表应该还有:name、real_name、id_card、avater_url

2、房屋表house

  • house_id 、user_id、title、price、area_id
  • 房屋所在区域独立建表
  • 房屋轮播图信息也独立建表
  • 房屋标签信息也独立建表

3、区域表 area

  • area_id、name

4、房屋轮播图 house_image

  • url 、house_id

5、房屋标签信息 faciliy

  • faciliy_id、name

6 定单表 order

  • id 、user_id、house_id、create_time、start_time、end_time、price、status、days、amount、comment

六、数据库迁移

1、 db init

在这里插入图片描述

2、 db migrate

在这里插入图片描述

3、 db upgrade

在这里插入图片描述

4、检查

在这里插入图片描述

5、可能遇到的问题

5.1 执行db migrate报错,提示: ModuleNotFoundError:No module named ‘pymysql’

  • 解决办法:pip install pymysql

5.2 执行db upgrade报错,提示:

在这里插入图片描述

  • 解决办法:在蓝图进行引用(代码编写后,未在系统中进行引用)
    在这里插入图片描述

七、注意事项:

1、 系统运行前,需要启动以下数据库,并保证参数正确:

  • mysql
  • redis

2、安装必要的包

3、如果出现下面错误

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/laoluobo76/article/details/109778121