Flask是Python的轻量级Web后端框架,所谓轻量级——without orm,without templates language,without login manage,without db session manage,without admin...所以你可能要在一些方案中去选择你要的东西.
我把Web后端开发简单分为以下几步:
0. 数据库表设计;
路由设计;
HTML/JS/CSS开发;
后端表单数据校验及数据函数设计;
视图实现;
0.数据库表设计:)
根据需求设计数据表,定义清楚表字段名、字段类型、字段默认值、字段限制,表与表的关联(字段注释很重要),这里ORM选用SqlAlchemy,会话管理flak-sqlalchemy-session:
fromsqlalchemyimportString
fromsqlalchemyimportColumn
fromsqlalchemyimportDateTime
fromsqlalchemyimportInteger
fromcommons.funtionsimportget_now
fromdb.sqlite_engineimportBase
fromdb.sqlite_engineimportengine
classExample(Base):
__tablename__ ='example'
STATUS_ACTIVE =1
STATUS_BLOCK =
STATUS =
# id
id = Column(Integer,primary_key=True)
# 版本
version = Column(Integer,default=1)
# 用户名
name = Column(String(16),nullable=False)
# 邮箱
email = Column(String(64),nullable=False,unique=True)
# 创建时间
create_time = Column(DateTime,default=get_now())
# 状态
status = Column(Integer,default=STATUS_BLOCK)
基本数据类型Interger\String\DateTime\Text\Boolean\Float,超长文本LONGTEXT可能会用到。
---sqlalchemy操作数据---
查询
# 简单查询
# 查询冻结状态
query = current_session.query(Example).\
filter_by(status=Example.STATUS_BLOCK)
# 第1条记录
row = query.first()
# 所有记录
rows = query.all()
# 复杂查询
# 邮箱含'shenweiwei'并且状态激活
query = current_session.query(Example).\
filter(Example.email.contains('shenweiwei')).\
filter(Example.status == Example.STATUS_ACTIVE)
# 第1条记录
row = query.first()
# 所有记录
rows = query.all()
添加
更新
删除
1.路由设计:)
blueprint(蓝图)是flask用于组织路由/项目结构的模块,类似于Django的app,你可以在app里定义该app的相关路由,blueprint与之相似。当使用blueprint时,路由的标识将会添加上路由所在blueprint名作为前缀:bp_meet.index。
对于C(create)U(update)R(retrieve)D(delete),我们可以定义项目路由规范,例如统一的路由格式module/operate[/id],operate是(create,update,retrieve,delete)的元素,往往我们需要扩展operates,加入restart,stop,start等一系列操作。下面是定义一条新增应用的路由:
@bp_application.route('/application/add',methods=['POST','GET'],
endpoint='add')
@login_required
defapplication_add():
"""应用创建"""
pass
2.HTML/JS/CSS开发:)
HTML表单内容是由数据库表(数据)和路由(URL)来确定的,表单字段名务必与数据库表字段名保持一直!!!以便表单数据格式化直接添加/更新到数据库,不一致也没关系啦,不炸毛就好。
当前主机名
value="{{host_name}}">
主机名*
value="{%ifrow%}{{row.name}}{%endif%}"placeholder="必填"required>
平台名*
value="{%ifrow%}{{row.name_}}{%endif%}"placeholder="必填"required>
服务器地址*
value="{%ifrow%}{{row.server}}{%endif%}"placeholder="http(s)://ip:port"required>
提交
刷新页面的话,js里form.submit()提交数据:
只刷新数据的话,js里ajax放form.serializeArray()提交数据:
$.ajax({
cache:false,
type:'post',
url:'',
data: form.serializeArray(),
async:true,
success:function(resp) {
if(resp.status) {
window.location.href ='/sql/index';
}else{
alert(resp.message);
}
}
});
3.后端表单数据校验及数据函数设计:)
WTForms作为后端数据校验模块,除了字段内定义的校验器外,还可以定义validate_field(form, field)函数自定义field字段的校验,常用于复杂校验,如数据库查询校验,多字段关联校验。
form.validate() 检查所有字段的校验器,通过返回True,不通过则在form.errors里存放错误信息。
fromflask_sqlalchemy_sessionimportcurrent_session
fromwtformsimportStringField
fromwtformsimportIntegerField
fromwtformsimportvalidators
fromwtformsimportValidationError
fromcommons.base_formimportBaseForm
frombusiness.example.tablesimportExample
classIdVersion(BaseForm):
"""Id Version基类"""
id = IntegerField('id',
[validators.required(message='id is required.')])
version = IntegerField('version',
[validators.required(message='version is required.')])
defvalidate_id(self,_):
"""校验id-version"""
class_ =self.__class__.target
ifcurrent_session.query(class_).\
filter_by(id=self.id.data,
version=self.version.data).first():
raiseValidationError('record is not exist.')
classExampleBaseForm(BaseForm):
"""Example基类"""
name = StringField('name',
[validators.input_required(message='name is required.'),
validators.length(max=16,message='name_max_length=16')])
email = StringField('email',
[validators.input_required(message='email is required.'),
validators.length(max=64,message='email_max_length=64')])
status = IntegerField('status',[validators.optional()])
defdata_parser(self):
data =self.data
data.pop('csrf_token', None)
returndata
classExampleAddForm(ExampleBaseForm):
"""添加Example"""
@staticmethod
defvalidate_email(_,field):
"""添加时 校验邮箱"""
row = current_session.query(Example).filter_by(email=field.data).first()
ifrow:
raiseValidationError('user email existed: %s'% field.data)
classExampleEditForm(ExampleBaseForm,IdVersion):
"""修改Example"""
target = Example
@staticmethod
defvalidate_email(form,field):
"""修改时 校验邮箱"""
row = current_session.query(Example).filter_by(email=field.data).first()
ifrowandrow != form.id.data:
raiseValidationError('user email existed: %s'% field.data)
上面创建了四个Form类,IdVersion用于所有表单修改/删除数据时,记录版本的校验;ExampleBaseForm用于被新增/修改Form继承使用,ExampleAddForm是新增数据Form,ExampleEditForm是修改Form,ExampleEditForm与ExampleAddForm会在部分字段的校验上有差异,例如上面的validate_email实现是不一样的,删除Form可参考IdVersion。
最近发现Django和Flask在Form处理上有着一些差异,有一点,Django实例化的form具有所有提交的数据字段,不管这些字段在Form类中是否有定义,而Flask实例化的form只具有Form中类的字段。Django的这种“随和”性质让一些人的代码越加恣意妄为:新加字段不写到Form类里,不校验,反正不会报错...
往往由表单提交到后端的数据不能直接保存到数据库,需要对数据再处理,比如添加操作人,这时,我会给form类添加一个data_parser()的函数,专门处理数据,返回供新增/更新数据库记录的dict数据:
classExampleBaseForm(BaseForm):
"""校验"""
pass
4.视图实现:)
视图函数是最后一步,可能有些新人上来就是先写这个,其实不然,0到3是基石,当然第4步是最重要的业务实现部分,这里只是简单的介绍了Flask下的web开发过程,以供参考。
@bp_application.route('/application/add',methods=['POST','GET'],
endpoint='add')
@login_required
defapplication_add():
"""应用创建"""
ifrequest.method =='GET':
returnrender_template('/application/application_add.html',**locals())
form = ApplicationAddForm()
if notform.validate():
returnjsonify({'status':False,'errors': form.errors.values()})
app = form.data_parser()
row = Application(**app)
current_session.add(row)
current_session.commit()
returnjsonify({'status':True,'message':'add success.'})
一次流畅的开发是0到4一气呵成,而不是在4的时候不断修改0到3.
references:
flaskhttp://docs.jinkan.org/docs/flask/tutorial/index.html#tutorial
sqlalchemyhttp://docs.sqlalchemy.org/en/latest/core/tutorial.html
flask_sqlalchemy_sessionhttp://flask-sqlalchemy-session.readthedocs.io/en/v1.1/
wtformshttps://wtforms.readthedocs.io/en/stable/#
flask-wtfhttp://flask-wtf.readthedocs.io/en/stable/quickstart.html
领取专属 10元无门槛券
私享最新 技术干货