《flask web开发》狗书前三章知识点详解
web表单是每个网站都必须用到的,狗书里也是花了1章的篇幅来介绍,表单的内容相对第五章数据库要简单不少,不过由于模块版本的原因有部分bug,这边就针对一些知识点进行拓展。
开头就介绍了在app.config里插入secret_key,这边注意两点,其一是’hard to guess string’表示我们需要给这个值定一个很难猜的字符串,一般是字母数字符号都有,然后字符串长度也要相对较长;另一个就是app.config了,这是字典,在里面定义了很多的flask配置,后续可以单独定义一个Config类来封装这些配置,然后通过**app.config.from_object(Config)**把类配置到app上。
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
表单代码如下,其中Form类在Flask-WTF更新时已经被废除了,更新为FlaskForm,两者的内容不变;另外就是Required也变更为了DataRequired,其余的关于字段那块了解下各自的功能就行了。
class NameForm(FlaskForm): #Form
name = StringField('What is your name?', validators=[DataRequired()]) submit = SubmitField('Submit')
如下是我针对index路由所写的注释,具体可以通过github上进行观看,这边要说明几点。首先对于请求方法get和post,一般我们渲染页面时,会首先通过get来获取页面信息,而当我们要提交内容时(如账号密码、上传图片),则需要通过post的请求方法,那get是不是就无法提交数据到后端呢?答案肯定是显然的,get也可以提交到后端,但最大的缺点就是提交的数据会显示到url后,细心的朋友肯定发现有的时候点进去一个链接,url的链接会变成url+?key=value的形式,这就是用get带数据请求后端的形式,key=value即是传入后端的键值对,前后端的交互是通过“字典”形式的,准确来讲json是目前的主要序列化格式。
session在上一篇文章有描述,这边拓展一下,session是依赖于cookie实现的,而cookie和session都是字典格式,且都是纯文本格式。当前主流都采取session来保存客户端的状态信息,因为session会加密信息,而加密条件往往就是我们定义的secret_key,因此为了保证客户端加密的信息更加严谨,secret_key都会定的很难。session的实现很简单,当客户端第一次请求时,服务端就会定义session数据,这个数据是键值对形式,数据内容由你定,一般可定位user_id=xx,user_name=xx等等,然后再生成一个唯一标识符session_id,形成(session_id:加密后内容)这样的键值对保存到数据库,并把session_id返回给客户端。下次客户端访问时,比较两者的值一致即可。
url_for是flask里使用最广泛的函数之一,因为在后端程序和模版里都可以使用,同时它是通过反解析的形式来实现定位路由的,也就是说我们有时需要变更网址,但视图函数一般是不变的,所以通过url_for(view_func)的形式,即使后续变更了网址,但服务端能照常运行。
#4章:路由定义methods,表示该路由可接收GET或POST请求,如果不定义,则默认只接收GET请求
#4章:GET和POST的区别是,前者适用于向浏览器获取数据(如页面刷新),后者适用于向浏览器发送数据(如账号密码登录)
@app.route('/', methods=['GET', 'POST'])
def index():
#实例话NameForm类,此时每次填写即表示单独一个实例
form = NameForm()
#判断是否提交,如果提交了,则将提交的数据赋值到name变量,传给模版
if form.validate_on_submit():
#session是字典结构,get方法获取不到的话则返回None,这里第一次提交明显获取不到,所以返回None
old_name = session.get('name')
#判断old_name非空且不等于表单的输入数据时执行
if old_name is not None and old_name != form.name.data:
# flash可以把内容显示出来
flash('Looks like you have changed your name!')
#第一次提交时,session还无值,所以在这边给session赋值,赋值方法就是字典添加数据方法。
#form.name.data表示:form是NameForm实例,即NameForm下的name字段的data数据,即表单输入数据
session['name'] = form.name.data
#依据书本内容重定向到首页
return redirect(url_for('index'))
#渲染模版,同时传回数据给模版,name=name第一个name表示变量name,即前端可以通过name变量获得值,
#后一个name则表示数据,再这里就是form.name.data
return render_template('index.html', form=form, name=session.get('name'))
第四章相对简单,并没有其他重要的需要拓展,这边运行后结果如下:
数据库的知识是web的重要部分,本章介绍了关系型数据库和非关系型数据库,但是仅针对sqlite这小型关系型数据库进行了讲解。实际上web后端更重要的非关系型数据库当属redis,是以保存键值对的形式来保存数据;而关系型数据库则是mysql尤为重要,所以本章我的实现是通过mysql来实现的。
执行git checkout v4.0可以从github上拷贝对应的文件。
flask项目里大名鼎鼎的ORM就是Flask- SQLAlchemy,ORM即object-relational-Mapper,即对象关系映射,其最大的作用就是能让我们在python程序里定义数据库模型,并执行对应的数据库命令,而这个好处就是让我们能面向对象编程,而不用去理会SQL语句;另外就是可以同时应对多种关系型数据库,而我们仅需使用SQLAlchemy就行了。
数据库配置,对于SQLAlchemy的配置,一开始我碰到也很头疼,也除了bug,但实际上理清了就会觉得很简单,一般就配置三条命令,同样的通过app.config来配置到该app身上。详细解释如下,配置sqlalchemy时,'SQLALCHEMY_DATABASE_URI’和’SQLALCHEMY_TRACK_MODIFICATIONS’是必备的,不然会报错,前者表示数据库链接,后者表示是否要追踪数据库数据的修改。
"""
5章:
basedir:获取项目的动态路径,通过os.path模块,获得当前的绝对路径,__file__返回hello.py文件名,
os.path.dirname则返回hello.py的文件夹名,通过abspath获得hello.py所在的绝对路径
配置数据库config:
'SQLALCHEMY_DATABASE_URI'指定数据库链接为sqlite
'SQLALCHEMY_TRACK_MODIFICATIONS'字面意思是追踪修改,False即表示不追踪数据库的修改记录,设置的目的是不设置会报错,是flask-sqlAlchemy强制要求设置,设置为False,因为数据库变更无须追踪;
'SQLALCHEMY_COMMIT_ON_TEARDOWN'这句命令作用很大,表示数据库如果修改数据了,会自动提交数据。
"""
basedir = os.path.abspath(os.path.dirname(__file__))
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:[email protected]/flask_web'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
创建Role/User数据模型时,其中对于backref和lazy的理解是一大难题,backref的作用是可以通过一个模型来关联到另一个模型,举例说明:user=User(),此时获得user实例,我们便可以通过user.role获得对应的role数据,而user.role正式backref='role’定义的。lazy则是表示可以延迟加载。
#定义Role类,一个类对应一个数据表
class Role(db.Model):
# 表名为roles,不定义的话,SQLAlchemy会默认定义。
__tablename__ = 'roles'
# 依次定义id、name
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
#1对多:relationship表示链接的外键关系,第一个参数是链接外键所在的类名
#backref==>back reference即回引,即可以通过一个表获得关系表的数据,
# 举例:Role.users通过Role获得users的数据(下方定义),User.role(下方定义)获得role数据
# lazy=dynamic表示动态加载,意思是当Role.users时不会立马返回结果(好处是不用立马查询数据库,利于性能优化),
users = db.relationship('User', backref='role', lazy='dynamic')
# 定义__repr__便于在操作数据库时可以在控制台查询对象
def __repr__(self):
return '' % self.name
数据库的创建,如果要用python hello.py shell执行控制台命令的话,切记第二章flask-script里的manager=Manager(app),通过把app注册到manager上,通过manager来实现控制台的命令开启功能。
关于数据库的增删改查,要注意的就是每次修改后都要记得提交,即db.session.add()、db.session.commit(),后续会用try…except…来保证数据库的提交,一旦失败则执行db.session.rollback()进行回滚。这是利用了关系型数据库mysql的事务性,即只要整个执行语句没成功,则回滚到初始状态(未进行数据库操作状态)
数据表的查询是重中之重,web后端的工作内容就是获取前端参数,然后根据参数查询数据库的数据,最后再把数据返回给前端,由此可知查询的重要性。这边要谨记的就是先查询对象,然后再查询对象数据。比如User.query即表示查询对象,加上filter则可以限制满足条件的查询对象。最后加上all()就是表示查询全部数据,加上first()就表示查询满足条件的一条数据。以此类推,ORM的查询基本遵照该原则,另外就是可以根据数据表的一对多、多对多关系,通过定义backref和外键,得到User.role\Role.users来得到另一个关联数据表的数据。
数据库的整个迁移问题不大,迁移完后登录mysql就可以看到定义的模型类就生成了对应的表,同时在程序文件里migration-version-py文件里面就展示了整个orm的实现,感兴趣的朋友可以了解一下。查询后在mysql如下:
第五章内容讲完,执行git checkout v5.0即可拷贝文件。
本章按照书里的内容很难出错,要注意以下几点:
狗书前六章是后面章节的基础,暂时说明知识点如上。