找回密码
 会员注册
查看: 33|回复: 0

Flask项目搭建及部署——Python

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64454
发表于 2024-9-3 14:50:34 | 显示全部楼层 |阅读模式
flask搭建及部署pip19.2.3python3.7.5Flask1.1.1Flask-SQLAlchemy2.4.1Pika1.1.0Redis3.3.11flask-wtf0.14.21、创建flask项目: 创建完成后整个项目结构树: app.py:项⽬管理⽂件,通过它管理项⽬。static:存放静态文件templates文件夹:用于放置html模板文件由于flask属于轻量级web框架,更加自由、灵活,可扩展性强,第三方库的选择面广,开发时可以结合自己最喜欢用的轮子,也能结合最流行最强大的Python库。所以这个框架的代码架构需要自己设计。2、创建项目主要逻辑代码保存目录手动创建application目录、filter目录及其子目录application:项目主要逻辑代码保存目录_init_.py:创建flask应用并加载配置,如mysql,redis,rabbitmq,apps:专门用于保存每一个项目的蓝图app1:app1蓝图目录,在app1下的init_.py中文件中创建蓝图对象,view.py中新增对应的视图文件,在model.py中写模型代码settings:项目配置存储目录dev.py:项目开发阶段配置文件prop.py:项目生成阶段配置文件static:项目静态文件夹(用于存放css一类的文件)templates:用于放置html模板文件filter:整个项目拦截器目录requestFilter.py:针对整个app项目全局路由拦截规则定义app.py:项⽬管理⽂件,通过它启动整个项目2.1配置mysql数据库,加载配置文件并针对整个app项目定义全局db2.1.1settings.py#全局通用配置类classConfig(object):  """项目配置核心类"""  #调试模式  DEBUG=False​  #配置日志  #LOG_LEVEL="DEBUG"  LOG_LEVEL="INFO"​​  #配置redis  #项目上线以后,这个地址就会被替换成真实IP地址,mysql也是  REDIS_HOST='yourhost'  REDIS_PORT=yourport  REDIS_PASSWORD='yourpassword'  REDIS_POLL=10​  #数据库连接格式  SQLALCHEMY_DATABASE_URI="mysql+pymysql://user:password@localhost:3306/test?charset=utf8"  #动态追踪修改设置,如未设置只会提示警告  SQLALCHEMY_TRACK_MODIFICATIONS=False  #查询时会显示原始SQL语句  SQLALCHEMY_ECHO=False  #数据库连接池的大小  SQLALCHEMY_POOL_SIZE=10  #指定数据库连接池的超时时间  SQLALCHEMY_POOL_TIMEOUT=10  #控制在连接池达到最大值后可以创建的连接数。当这些额外的连接回收到连接池后将会被断开和抛弃。  SQLALCHEMY_MAX_OVERFLOW=2​​  #rabbitmq参数配置  RABBITUSER="user"  RABBITPASSWORD="password"  RABBITHOST="yourip"  RABBITPORT=yourport2.1.2dev.pyfrom.importConfig​classDevelopmentConfig(Config):  '开发模式下的配置'  #查询时会显示原始SQL语句  SQLALCHEMY_ECHO=True2.1.3prop.pyfrom.importConfig​classProductionConfig(Config):  """生产模式下的配置"""  DEBUG=False2.1.4加载配置文件,定义全局的db(SQLALchemy类的实例)供项目使用#主应用的根目录app=Flask(__name__)​config={  'dev'evelopmentConfig,  'prop'roductionConfig,}​#设置配置类Config=config['dev']​#加载配置app.config.from_object(Config)​#创建数据库连接对象db=SQLAlchemy(app)dev:测试环境配置prop:生产环境配置Flask应用app配置加载通常三种方式从配置对象中加载:app.config.from_object()从配置文件中加载:app.config.from_pyfile()-ini文件从环境变量中加载:app.config.from_envvar()配置对象从配置对象中加载,创建配置的类:#配置对象,里面定义需要给APP添加的一系列配置classConfig(object):  DEBUG=True​​app=Flask(__name__)​#从配置对象中加载配置app.config.from_object(Config)app.run()配置文件从配置文件中加载,在目录中定义一个配置文件config.iniapp=Flask(__name__)​#从配置对象中加载配置app.config.from_pyfile("config.ini")app.run()环境变量app=Flask(__name__)#从环境变量中加载app.config.from_envvar("FLASKCONFIG")app.run()2.2定义model模型,负责和数据库交互app1.modelfromapplicationimportdb​classWdtest(db.Model):  __tablename__="wdtest"#设置表名  id=db.Column(db.String(100),primary_key=True,comment="主键ID")  name=db.Column(db.String(20),index=True,comment="姓名")  age=db.Column(db.Integer,default=True,comment="年龄")模型表示程序使用的持久化实体.在Flask-SQLALchemy中,模型一般是一个Python类,类中的属性对应数据库中的表.db.Model:创建模型,db.Column:创建模型属性.tablename:指定表名模型属性类型:类型名Python类型说明Integerint普通整数,一般是32位SmallIntegerint取值范围小的整数,一般是16位BigIntegerint或long不限制精度的整数Floatfloat浮点数Numericdecimal.Decimal定点数Stringstr变长字符串Textstr变长字符串,对较长或不限长度的字符串做了优化Unicodeunicode变长Unicode字符串UnicodeTextunicode变长Unicode字符串,对较长或不限长度的字符串做了优化Booleanbool布尔值Datedatetime.date日期Timedatetime.time时间DateTimedatetime.datetime日期和时间Intervaldatetime.timedelta时间间隔Enumstr一组字符串PickleType任何Python对象自动使用Pickle序列化LargeBinarystr二进制文件常用SQLAlchemy列选项选项名说明primary_key如果设为True,这列就是表的主键unique如果设为True,这列不允许出现重复的值index如果设为True,为这列创建索引,提升查询效率nullable如果设为True,这列允许使用空值;如果设为False,这列不允许使用空值default为这列定义默认值2.3声明蓝图app1._init.py#给app取别名为'index'index_blu=Blueprint('index',__name__,template_folder='templates',static_folder='static')​from.viewsimport*template_folder:指定模板文件路径,查找顺序,先全局templates里面找,没找到,再往子蓝图里面找.这里是把view中所有的视图都声明在index这个蓝图里面,接下来我们需要做的是将这个声明好的蓝图,注册进我们的项目中。2.4将声明好的蓝图注册进app中application.init_:fromapplication.settings.devimportDevelopmentConfigfromapplication.settings.propimportProductionConfig​#主应用的根目录app=Flask(__name__)​config={  'dev'evelopmentConfig,  'prop'roductionConfig,}​#设置配置类Config=config['dev']​#加载配置app.config.from_object(Config)​#创建数据库连接对象db=SQLAlchemy(app)​#todo注册蓝图from.apps.app1importindex_bluapp.register_blueprint(index_blu,url_prefix='/index')针对:app=Flask(name)解释Flask类初始化参数Flask类init方法部分代码def__init__(    self,    import_name,    static_url_path=None,    static_folder="static",    static_host=None,    host_matching=False,    subdomain_matching=False,    template_folder="templates",    instance_path=None,    instance_relative_config=False,    root_path=None,  ):passimport_name:Flask程序所在的包(模块),传__name__static_url_path:静态文件访问路径,可以不传,默认为:/+static_folderstatic_folder:静态文件存储的文件夹,可以不传,默认为statictemplate_folder:模板文件存储的文件夹,可以不传,默认为templates3通过以上的步骤后,我们可以基本操作数据库了:以下所有示例代码,皆在view.py中去实现3.1增:先写怎么增,然后增加,最后提交student=Wdtest(id=ids,name=name,age=age)try:  application.db.session.add(student)  application.db.session.commit()except:  #事務回滾  application.db.session.rollback()3.2删:先获取数据库中的这个数据,再删除它user=Wdtest.query.first()  application.db.session.delete(user)  application.db.session.commit()3.3改:user=Wdtest.query.first()user.name=nametry:  application.db.session.commit()except:  #事務回滾  application.db.session.rollback()3.4查:#查询所有⽤户数据user_list=Wdtest.query.all()​#查询有多少个⽤户user_list_num=Wdtest.query.count()#查询第1个⽤户user=Wdtest.query.first()#查询id为3的⽤户[3种⽅式]user=Wdtest.query.get(3) #根据主键查询user_list=Wdtest.query.filter_by(id=3).all() #以关键字实参形式进行匹配字段user_list=Wdtest.query.filter(Wdtest.id==3).all() #以恒等式形式匹配字段​#查询名字结尾字符为g的所有⽤户Wdtest.query.filter(Wdtest.name.endswith('g')).all()​#查询名字包含‘wa'的所有项目user_list=Wdtest.query.filter(Wdtest.name.contains('wa')).all()#模糊查询user_list=Wdtest.query.filter(Wdtest.name.like('%a%')).all()#查询名字wa开头和age为20的所有⽤户[2种⽅式]user_list=Wdtest.query.filter(Wdtest.name.startswith('wa'),Wdtest.age==20).all()user_list=Wdtest.query.filter(and_(Wdtest.name.startswith('wa'),Wdtest.age==20)).all()​#非条件查询查询名字不等于wade的所有⽤户[2种⽅式]user_list=Wdtest.query.filter(not_(Wdtest.name=='wade')).all()user_list=Wdtest.query.filter(Wdtest.name!='wade').all()​#in条件查询user_list=Wdtest.query.filter(Wdtest.id.in_(['97124f50-0208-11ea-a66c-04ea56212bdf','3'])).all()​#所有⽤户先按年龄从⼩到⼤,再按id从⼤到⼩排序,取前5个user_list=Wdtest.query.order_by(Wdtest.age,Wdtest.id.desc()).limit(5).all()​#分⻚查询,每⻚3个,查询第2⻚的数据pn=Wdtest.query.paginate(2,3)print(pn.pages)print(pn.page)print(pn.items)4路由传参有时我们需要将同一类URL映射到同一个视图函数处理,比如:使用同一个视图函数来显示不同用户的个人信息。#路由传递参数@app.route('/user/')defuser_info(id):  return'%s'%id路由传递的参数默认当做string处理####指定请求方式在Flask中,定义一个路由,默认的请求方式为:GETOPTIONSHEAD在装饰器添加请求指定方式:@app.route('/test',methods=['GET','POST'])deftest():  return"ok"5动态正则匹配路由flask实现正则匹配步骤:导入转换器基类:在Flask中,所有的路由的匹配规则都是使用转换器对象进行记录自定义转换器:自定义类继承于转换器基类添加转换器到默认的转换器字典中使用自定义转换器实现自定义匹配规则###实现:导入转换器基类fromwerkzeug.routingimportBaseConverter自定义转换器#自定义正则转换器classRegexConverter(BaseConverter):  def__init__(self,url_map,*args):    super(RegexConverter,self).__init__(url_map)    #将接受的第1个参数当作匹配规则进行保存    self.regex=args[0]添加转换器到默认的转换器字典中,并指定转换器使用时名字为:reapp=Flask(__name__)​#将自定义转换器添加到转换器字典中,并指定转换器使用时名字为:regexapp.url_map.converters['regex']=RegexConverter使用转换器去实现自定义匹配规则当前此处定义的规则是:3位数字@app.route('/index/')defuser_info(id):  return"id为%s"%id自定义转换器其他函数实现继承于自定义转换器之后,还可以实现to_python和to_url这两个函数去对匹配参数做进一步处理:to_python:该函数参数中的value值代表匹配到的值,可输出进行查看匹配完成之后,对匹配到的参数作最后一步处理再返回,比如:转成int类型的值再返回:classRegexConverter(BaseConverter):  def__init__(self,url_map,*args):    super(RegexConverter,self).__init__(url_map)    #将接受的第1个参数当作匹配规则进行保存    self.regex=args[0]​  defto_python(self,value):    returnint(value)系统自带转换器DEFAULT_CONVERTERS={  'default':     UnicodeConverter,  'string':     UnicodeConverter,  'any':       AnyConverter,  'path':      athConverter,  'int':       IntegerConverter,  'float':      FloatConverter,  'uuid':      UUIDConverter,}6增加日志记录、redis配置加载、mq配置加载6.1日志记录Settings._init:#配置日志#LOG_LEVEL="DEBUG"LOG_LEVEL="INFO"日志记录级别FATAL/CRITICAL=致命的,危险的ERROR=错误WARNING=警告INFO=信息DEBUG=调试NOTSET=没有设置application._init:1、日志模块基础配置,如:日志存放地址、日志记录格式、日志等级#增加日志模块defsetup_log(Config):  #设置日志等级  logging.basicConfig(level=Config.LOG_LEVEL)  #创建日志记录器,指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限  file_log_handler=RotatingFileHandler('log/log',maxBytes=1024*1024*300,backupCount=10)  #创建日志记录的格式日志等级输入日志信息的文件名行数日志信息  formatter=logging.Formatter('%(asctime)s:%(levelname)s%(filename)s:%(lineno)d%(message)s')  #为刚创建的日志记录器设置日志记录格式  file_log_handler.setFormatter(formatter)  #为全局的日志工具对象(flaskapp使用的)添加日志记录器  logging.getLogger().addHandler(file_log_handler)2、日志启动#日志启动setup_log(Config)6.2redis配置及加载之前我们在config中已经把redis的配置已经写进去了,所以这里可以直接创redis连接池供app全局使用application._init:#新增redis连接模块defconnectRedis(Config):  pool=redis.ConnectionPool(host=Config.REDIS_HOST,port=Config.REDIS_PORT,password=Config.REDIS_PASSWORD,                max_connections=Config.REDIS_POLL)  redis_store=redis.Redis(connection_pool=pool)  returnredis_store使用示例:@index_blu.route("/redis",methods=["POST","GET"])defadd_toRedis():  logging.info("cometohere")  key=request.args.get("key")  application.redis_store.set(key,"1233")  value=application.redis_store.get(key)  print(value)  return"12333"6.3rabbitmq基础配置及加载#rabbitmq配置访问#添加用户名和密码credentials=pika.PlainCredentials(Config.RABBITUSER,Config.RABBITPASSWORD)#配置连接参数parameters=pika.ConnectionParameters(host=Config.RABBITHOST,port=Config.RABBITPORT,credentials=credentials)connection=pika.BlockingConnection(parameters)channel=connection.channel()使用示例:@index_blu.route("/rabitmq",methods=["POST","GET"])defadd_rabitmq():  logging.info("cometorabiitmq")  application.channel.queue_declare(queue='queuetest2')​  return"33333"7全局拦截器配置filerter.requestFilter这里只是简单针对请求路径非index的进行拦截,如果还有其他拦截条件或者机制,可以继续在filter这个包下添加fromflaskimportrequestimportapplication​#拦截器,每次的请求进来都会做的操作@application.app.before_requestdefbefore_action():  #获取当前请求的路由(路径)  a=request.path  print(a)  u=a.split('/')  iflen(a)>2:    ifu[1]=='index':      print('success')  else:    return"无权限请求"拦截器加载进app:#拦截器加载requestFilter.before_action8请求对象request和返回对象Response请求对象request,使用前先导入request模块fromflaskimportrequest获取url请求参数:request.args获取form表单中的数据:request.form获取请求体原始数据:request.data获取文件数据:request.files获取cookie:request.cookies获取header信息:request.headers获取请求方法:request.method获取请求路径:request.pathResponse视图函数中可以返回的值可以直接返回字符串,底层将这个字符串封装成了Response对象元组,响应格式(响应体,状态码,头信息),不一定都要写,底层也是封装了一个Response对象返回Response或其子类(jsonify子类返回标准json)实现一个自定义Response对象步骤继承Response对象实现方法force_typeforce_type(cls,rv,environ=None)指定app.response为你定义的类如果返回的值不是可以返回的对象,就会调用force_type方法实现classJSONResponse(Response):​  @classmethod  defforce_type(cls,response,environ=None):    '''    这个方法只有视图函数返回非字符、非元祖、非Response对象才会调用    :paramresponse:是视图函数的返回值    :paramenviron:    :return:    '''    print(response)    print(type(response))    ifisinstance(response,(list,dict)):​      #jsonify除了将字典转换成json对象,还将对象包装成了一个Response对象      response=jsonify(response)​    returnsuper(JSONResponse,cls).force_type(response,environ)      app.response_class=JSONResponse9异常捕获及自定义异常捕获错误errorhandler装饰器注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法参数:code_or_exception–HTTP的错误状态码或指定异常例如统一处理状态码为500,404的错误给用户友好的提示:@app.errorhandler(500)definternal_server_error(e):  return'服务器搬家了哈哈哈'​@app.errorhandler(404)definternal_server_error(e):  return'瞎请求什么路径呢'例如自定义错误413@app.errorhandler(413)defzero_division_error(e):  return'除数不能为0'异常捕获abort方法抛出一个给定状态代码的HTTPException或者指定响应,例如想要用一个页面未找到异常来终止请求,你可以调用abort(404)。参数:code–HTTP的错误状态码@index_blu.route("/exception",methods=["POST","GET"])defexception():  logging.info("cometoexception")  try:    print(2)    a=3/0  except:    abort(413)  return"ooooo"10上下文 上下文:即语境,语意,在程序中可以理解为在代码执行到某个时刻,根据之前代码锁做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以做的事情。Flask中有两种上下文:请求上下文(requestcontext)和应用上下文(applicationcontext)。Flask中上下文对象:相当于一个容器,保存了Flask程序运行过程中的一些信息。1.application指的是当你调用app=flask(name)创建的这个对象app。2.request指的是每次http请求发生时,WSGIserver(比如gunicorn)调用Flask.call()之后,在Flask对象内部创建的Request对象;3.application表示用于相应WSGI请求的应用本身,request表示没出http请求;4.appliacation的生命周期大于request,一个application存活期间,可能发生多次http请求,所以,也就会有多个request;请求上下文(requestcontext):在Flask中,可以直接在视图函数中使用request这个独享进行获取先关数据,而request就是请求上下文的对象,保存了当前本次请求的相关数据,请求上线文对象有:request、sessionrequest:封装了HTTP请求的内容,针对的是http请求。例如:user=request.args.get('user'),获取的是get请求的参数。session:用来记录请求会话中的信息,针对的是用户信息。例如:session['name']=user.id科可以记录用户信息。还可以通过session.get('name')获取用户信息。应用上下文(applicationcontext):它不是一直存在的,它只是requestcontext中的一个对app的代理,所谓的localproxy。它的作用主要是帮助request获取当前的应用,它是伴request而生,随request而灭的。应用上下文对象有:current_app,gcurrent_app:应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:应用的启动脚本是哪个文件,启动时指定了哪些参数加载了哪些配置文件,导入了哪些配置连接了哪个数据库有哪些可以调用的工具类、常量当前flask应用在哪个机器上,哪个IP上运行,内存多大current_app.namecurrent_app.test_value='value'g变量:g作为flask程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的threadid区别g.name='abc'注意:不同的请求,会有不同的全局变量两者的区别:请求上下文:保存了客户端和服务器交互的数据应用上下文:flask应用程序运行过程中,保存的一些配置信息,比如程序名、数据库连接、应用信息等11部署gunicorn作为服务器,安装gunicornpip3installgunicorn启动gunicorn-w3-b127.0.0.1:8000app:app-w处理进程数-b运⾏主机ip端⼝dpj.wsgi项⽬的wsgigunicorn常⽤配置-cCONFIG:CONFIG,配置⽂件的路径,通过配置⽂件启动;⽣产环境使⽤; ​-bADDRESS:ADDRESS,ip加端⼝,绑定运⾏的主机; ​-wINT,--workersINT:⽤于处理⼯作进程的数量,为正整数,默认为1; ​-kSTRTING,--worker-classSTRTING:要使⽤的⼯作模式,默认为sync异步,可以下载 ​eventlet和gevent并指定 ​--threadsINT:处理请求的⼯作线程数,使⽤指定数量的线程运⾏每个worker。为正整数,默认为1。 ​--worker-connectionsINT:最⼤客户端并发数量,默认情况下这个值为1000。 ​--backlogint:未决连接的最⼤数量,即等待服务的客户的数量。默认2048个,⼀般不修改; ​-pFILE,--pidFILE:设置pid⽂件的⽂件名,如果不设置将不会创建pid⽂件 ​--access-logfileFILE:要写⼊的访问⽇志⽬录--access-logformatSTRING:要写⼊的访问⽇志格式 ​--error-logfileFILE,--log-fileFILE:要写⼊错误⽇志的⽂件⽬录。 ​--log-levelLEVEL:错误⽇志输出等级。 ​--limit-request-lineINT:HTTP请求头的⾏数的最⼤⼤⼩,此参数⽤于限制HTTP请求⾏的允 ​许⼤⼩,默认情况下,这个值为4094。值是0~8190的数字。 ​--limit-request-fieldsINT:限制HTTP请求中请求头字段的数量。此字段⽤于限制请求头字 ​段的数量以防⽌DDOS攻击,默认情况下,这个值为100,这个值不能超过32768 ​--limit-request-field-sizeINT:限制HTTP请求中请求头的⼤⼩,默认情况下这个值为8190 ​字节。值是⼀个整数或者0,当该值为0时,表示将对请求头⼤⼩不做限制 ​-tINT,--timeoutINT:超过这么多秒后⼯作将被杀掉,并重新启动。⼀般设定为30秒; ​--daemon:是否以守护进程启动,默认false; ​--chdir:在加载应⽤程序之前切换⽬录; ​--graceful-timeoutINT:默认情况下,这个值为30,在超时(从接收到重启信号开始)之后仍然活着 ​的⼯作将被强⾏杀死;⼀般使⽤默认; ​--keep-aliveINT:在keep-alive连接上等待请求的秒数,默认情况下值为2。⼀般设定在1~5秒之 ​间。 ​--reload:默认为False。此设置⽤于开发,每当应⽤程序发⽣更改时,都会导致⼯作重新启动。 ​--spew:打印服务器执⾏过的每⼀条语句,默认False。此选择为原⼦性的,即要么全部打印,要么全部 ​不打印; ​--check-config:显示现在的配置,默认值为False,即显示。 ​-eENV,--envENV:设置环境变量;本次分享到此结束,觉得有所帮助的朋友点点关注点点赞!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-27 13:58 , Processed in 0.429110 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表