利用pycharm学习flask(十一)

本文内容:

1.数据库操作
2.在视图函数中操作数据库
3.集成python shell

数据库操作

创建表

我们使用Flask-SQLAlchemy根据模型类创建数据库,必须安装flask-script。截至目前hello.py完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from flask import Flask, render_template, session, redirect, url_for,flash
from flask.ext.script import Manager
from flask_bootstrap import Bootstrap
from flask_moment import Moment
from datetime import datetime
from flask_wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required
from flask_sqlalchemy import SQLAlchemy
import os


basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'+os.path.join(basedir,'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SECRET_KEY'] = '123456'
db = SQLAlchemy(app)
bootstrap = Bootstrap(app)
moment = Moment(app)
manager = Manager(app)

class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(64),unique=True)
users = db.relationship('User',backref='role')

def __repr__(self):
return '<Role %r>' % self.name

class User(db.Model):
__teblename__ = 'users'
id = db.Column(db.Integer,primary_key=True)
username = db.Column(db.String(64),unique=True,index=True)
role_id = db.Column(db.Integer,db.ForeignKey('roles.id'))

def __repr__(self):
return '<User %r>' % self.username

class NameForm(Form):
name = StringField('What is your name?',validators=[Required()])
submit = SubmitField('Submit')

@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('Looks like you have changed your name!')
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html',form = form,name = session.get('name'))


@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)

@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'),404

@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'),500

manager.run()
if __name__ == '__main__':
app.run(debug=True)

在pycharm最下方点击terminal进入shell,然后通过preferences中的project interpreter找到虚拟环境目录,在shell中进入并启动虚拟环境:

1
source activate

运行:

>>> from hello import db
>>> db.create_all()

你会在程序目录发现一个data.sqlite的文件,如果数据库表已经存在,那么这个命令不会创建或更新这个表。更新现有数据库表的粗暴方式是先删除旧表再重新创建:

>>> db.drop_all()
>>> db.create_all()

插入行

下面这段代码创建了一些角色和用户:

>>> from hello import Role, User
>>> admin_role = Role(name=’Admin’)
>>> mod_role = Role(name=’Moderator’)
>>> user_role = Role(name=’User’)
>>> user_john = User(username=’john’, role=admin_role)
>>> user_susan = User(username=’susan’, role=user_role)
>>> user_david = User(username=’david’, role=user_role)

这些新建对象的id属性并没有明确设定,因为主键是由Flask-SQLAlchemy管理的。现在这些对象只存在于Python中,还未写入数据库。因此id尚未赋值。

通过数据库会话管理对数据库所做的改动,在Flask-SQLAlchemy中,会话由db.session表示。准备把对象写入数据库之前,先将其添加到会话中:

>>> db.session.add(admin_role)
>>> db.session.add(mod_role)
>>> db.session.add(user_role)
>>> db.session.add(user_john)
>>> db.session.add(user_susan)
>>> db.session.add(user_david)

或者简写成:
>>> db.session.add_all([admin_role, mod_role, user_role,user_john, user_susan, user_david])

这时候,id属性已经赋值,这里数据库会话也称为事务。

什么是事务:

“A账户向B账号汇钱”来说明事务

1、从A账号中把余额读出来。
2、对A账号做减法操作。
3、把结果写回A账号中。
4、从B账号中把余额读出来。
5、对B账号做加法操作。
6、把结果写回B账号中。

为了数据的一致性,这6件事,要么都成功做完,要么都不成功。而且这个操作的过程中。对A、B找好的其他访问必须锁死,所谓锁死就是要排除其他的读写操作,不然会有脏数据问题,这就是事务。

修改行

>>> admin_role.name = ‘Administrator’
>>> db.session.add(admin_role)
>>> db.session.commit()

删除行

>>> db.session.delete(mod_role)
>>> db.session.commit()

查询行

Flask-SQLAlchemy为每个模型类都提供了query对象。最基本的模型查询是取回对应表中的所有记录:

>>> Role.query.all()
>>> User.query.all()

使用过滤器可以配置query对象进行更精确的数据库查询,下面是查找所有角色为User的所有用户:

>>> User.query.filter_by(role=user_role).all()

若要查看SQLAlchemy为查询生成的原生SQL查询语句,只需把query对象转化为字符串:

>>> str(User.query.filter_by(role=user_role))

如果退出了shell会话,上面例子中创建的对象就不会以python对象的形式存在,而是作为各自数据库表中的行。如果你打开了一个新的shell会话,就要从数据库中读取行,再重新创建python对象,下面发起一个查询,并加载名为User的用户角色:

>>> user_role = Role.query.filter_by(name=’User’).first()

下图列出了可在query对象上调用的常用过滤器和查询执行函数

下面的例子分别从关系的两端查询角色和用户之间的一对多关系:

>>> users = user_role.users
>>> users
>>> users[0].role

这个例子中的user_role.users查询有个小问题。执行 user_role.users表达式时,隐含的查询会调用all() 返回一个用户列表。query对象是隐藏的,因此无法指定更精确的查询 过滤器。就这个特定示例而言,返回一个按照字母顺序排序的用户列表可能更好。在下面的例子中,我们修改了关系的设置,加入了 lazy = ‘dynamic’ 参数,从而禁止自动执行查询。

修改hello.py:

1
2
3
4
class Role(db.Model): 
# ...
users = db.relationship('User', backref='role', lazy='dynamic')
# ...

这样配置好关系后,user_role.users会返回一个尚未执行的查询,因此可以在其上添加过滤器:
>>> user_role.users.order_by(User.username).all()
>>> user_role.users.count()

在视图函数中操作数据库

修改hello.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first()
if user is None:
user = User(username = form.name.data)
db.session.add(user)
session['known'] = False
else:
session['known'] = True
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html',form = form, name = session.get('name'),known = session.get('known',False))

修改templates/index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Flasky{% endblock %}

{% block page_content %}
<div class="page-header">
<h1>Hello, {% if name %}{{name}}{% else %}Stranger{% endif %}!</h1>
{% if not known %}
<p>Pleased to meet you!</p>
{% else %}
<p>Happy to see you again!</p>
{% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

修改后,提交表单后,程序会使用filter_by查询过滤器在数据库中查找提交的名字。变量known被写入用户会话中,因此重定向之后,可以把数据传给模板,用来显示自定义的欢迎消息。首先如上要在python shell建立数据库表然后添加数据,然后输入susan、david等数据库里有的信息后和输入没有的信息就会出现两种不同的消息。

集成python shell

每次启动shell都要导入数据库实例和模型,为了避免一直重复导入,我们可以让flask-script的shell命令自动导入特定的对象。

若要把对象添加到导入列表中,我们要为shell命令注册一个make_context回调函数

修改hello.py:

1
2
3
4
5
from flask_script import Manager, Shell

def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role)
manager.add_command("shell", Shell(make_context=make_shell_context))

make_shell_context()函数注册了程序、数据库实例以及模型,因此这些对象能直接导入shell:

$ python hello.py shell
>>> app
\
>>> db
\
>>> User
\

坚持原创技术分享,您的支持将鼓励我继续创作!