Flask - 数据模型操作

各种查询操作

  • 先做准备工作,创建工程

    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
    from flask import Flask
    from flask_script import Manager
    from flask_sqlalchemy import SQLAlchemy
    from flask_migrate import Migrate, MigrateCommand
    import os

    app = Flask(__name__)
    manager = Manager(app)

    base_dir = os.path.abspath(os.path.dirname(__file__))
    db_uri = 'sqlite:///' + os.path.join(base_dir, 'data.sqlite')
    app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
    app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    db = SQLAlchemy(app)

    migrate = Migrate(app, db)
    manager.add_command('db', MigrateCommand)


    @app.route('/')
    def hello_world():
    return 'Hello World!'


    if __name__ == '__main__':
    manager.run()
  • get根据主键查询

  • all查询所有数据

  • first查询满足条件的第一条

  • limit限制结果集数量

  • offset设置偏移数量

  • get_or_404功能同 get,但是找不到时报 404

  • first_or_404功能同 first,但是找不到时报 404

  • order_by排序,可以指定多字段,asc 表示升序,desc 表示降序

  • paginate分页查询,项目中讲解

  • count统计数量

  • filter指定查询(过滤)条件

  • 示例:

    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
    @app.route('/query/<uid>')
    def query(uid):
    '''
    # 根据ID进行查询
    u = User.query.get(uid)
    # get_or_404(uid),找到就返回,找不到就返回404
    if u:
    return u.username
    return '查无此人'
    '''

    '''
    # 查询所有数据
    users = User.query.all()
    return ','.join(u.username for u in users)
    '''

    '''
    # 查询一条数据,返回单个对象
    # u = User.query.first()
    # first_or_404(),有返回第一条,没有报404
    # return u.username

    # 返回结果集中的第一条数据(需要遍历才能提取数据)
    # users = User.query.filter(User.age > 16).limit(1)

    # 限制结果集
    users = User.query.filter(User.age > 16).offset(1).limit(3)
    return ','.join(u.username for u in users)
    '''

    # 分页查询:paginate,项目中讲解

    '''
    # 排序,默认为升序asc,desc降序,可以指定多字段排序
    users = User.query.order_by(User.age.desc(), User.id.desc())
    return ','.join(u.username for u in users)
    '''

    # 统计结果集数量
    c = User.query.count()
    return str(c)

filter 条件

  • 关系

    1
    2
    3
    4
    5
    6
    >,__gt__(),大于,示例:User.query.filter(User.id > 1)等价于User.query.filter(User.id.__gt__(1))
    >=,__ge__()
    <,__lt__()
    <=,__le__()
    ==,__eq__()
    !=,__ne__()
  • 范围

    1
    2
    3
    between:User.query.filter(User.id.between(1, 3))
    in_: User.query.filter(User.id.in_((1, 3, 5)))
    notin_: User.query.filter(User.id.notin_((1, 3, 5)))
  • 内容

    1
    2
    3
    4
    5
    contains:	包含指定内容,如:filter(User.username.contains('xiao'))
    startwith: 指定内容开头
    endwidth: 已制定内容结尾
    like: 模糊匹配,如:filter(User.username.like('hua%'))
    notlike: 模糊取反
  • 逻辑

    1
    2
    3
    4
    from sqlalchemy import and_, or_, not_
    and_: 逻辑与,默认的,filter(User.id > 2, User.age > 17)等价于filter(and_(User.id > 2, User.age > 17))
    or_: 逻辑或
    not_: 逻辑非
  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @app.route('/filter/')
    def filter():
    # 关系
    # users = User.query.filter(User.id >= 1)
    # users = User.query.filter(User.id.__gt__(1))
    # 范围
    # users = User.query.filter(User.id.between(1, 3))
    # users = User.query.filter(User.id.in_((1, 3, 5)))
    # users = User.query.filter(User.id.notin_((1, 3, 5)))
    # 内容
    # users = User.query.filter(User.username.contains('xiao'))
    # users = User.query.filter(User.username.startswith('xiao'))
    # users = User.query.filter(User.username.endswith('hua'))
    # users = User.query.filter(User.username.like('hua%'))
    # users = User.query.filter(User.username.notlike('%hua'))
    # 逻辑处理
    # 逻辑与and
    # users = User.query.filter(User.id > 2, User.age > 17)
    # users = User.query.filter(and_(User.id > 2, User.age > 17))
    # users = User.query.filter(or_(User.id > 2, User.age > 17))
    users = User.query.filter(not_(User.id > 2))
    return ','.join(u.username for u in users)

模型关系

  • 一对多(使用最多)

    • 一:学生(Student)

      • 需要添加反向引用

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        '''
        参数介绍
        参数1:关联的模型
        backref:反向引用
        lazy:加载时机
        'select'/True:首次使时查询,默认选项
        'joined'False:关联查询时加载
        'subquery':子查询时加载
        'dynamic':不加载,但是提供数据的查询,在一的那侧数据加载时不能使用
        '''

        # 在本模型中添加用于查询的字段articles,不会体现在数据中
        # 还会在关联模型Acticle中添加反向引用stu子字段,也不会体现在数据表中

        articles = db.relationship('Article', backref=db.backref('stu', lazy=True), lazy='dynamic')
    • 多:论文(Article)

      • 需要添加外键

        1
        sid = db.Column(db.Integer, db.ForeignKey('student.id'))
  • 一对一

    • 一:学生(Student)

      • 需要添加反向引用,再指定参数 uselist=False

        1
        2
        # 一对一,只需要在一对多的基础上指定参数uselist=False
        profile = db.relationship('Profile', backref='stu', uselist=False)
    • 一:详情(Profile)

      • 需要添加外键

        1
        sid = db.Column(db.Integer, db.ForeignKey('student.id'))
  • 多对多

    • 多:学生(Student)

      • 需要添加反向引用,同时需要通过参数 secondary 指定中间关联关系
      • 多条数据加载最后使用 dynamic
        1
        2
        # 多对多,需要通过secondary指定关联的中间表
        courses = db.relationship('Course', secondary='xuankebiao', backref=db.backref('students', lazy='dynamic'), lazy='dynamic')
    • 多:课程(Course)

    • 中间关联表,选课表(xuankebiao)

      • 指定表名
      • 添加需要的外键
      • 该表不需要用户维护
        1
        2
        3
        4
        5
        # 学生课程关联模型
        xuankebiao = db.Table('xuankebiao',
        db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
        db.Column('course_id', db.Integer, db.ForeignKey('course.id'))
        )

综合示例

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import and_, or_, not_
from flask_migrate import Migrate, MigrateCommand
import os

app = Flask(__name__)
manager = Manager(app)

base_dir = os.path.abspath(os.path.dirname(__file__))
db_uri = 'sqlite:///' + os.path.join(base_dir, 'data.sqlite')
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)


class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, unique=True)
email = db.Column(db.String, unique=True)
age = db.Column(db.Integer, default=20)


class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), unique=True)
'''
参数介绍
参数1:关联的模型
backref:反向引用
lazy:加载时机
'select'/True:首次使用是查询,默认选项
'joined'False:关联查询时加载
'subquery':子查询时加载
'dynamic':不加载,但是提供数据的查询,在一的那侧数据加载时不能使用
'''
# 在本模型中添加用于查询的字段articles,不会体现在数据中
# 还会在关联模型Acticle中添加反向引用stu子字段,也不会体现在数据表中
# articles = db.relationship('Article', backref='stu', lazy='dynamic')
# 与上面的方式等价
articles = db.relationship('Article', backref=db.backref('stu', lazy=True), lazy='dynamic')

# 一对一,只需要在一对多的基础上指定参数uselist=False
profile = db.relationship('Profile', backref='stu', uselist=False)

# 多对多,需要通过secondary指定关联的中间表
courses = db.relationship('Course', secondary='xuankebiao', backref=db.backref('students', lazy='dynamic'),
lazy='dynamic')


# 课程模型
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), unique=True)


# 学生课程关联模型
xuankebiao = db.Table('xuankebiao',
db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
db.Column('course_id', db.Integer, db.ForeignKey('course.id'))
)


class Profile(db.Model):
id = db.Column(db.Integer, primary_key=True)
realname = db.Column(db.String(20), unique=True)
# 添加外键
sid = db.Column(db.Integer, db.ForeignKey('student.id'))


class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(30), unique=True)
content = db.Column(db.TEXT)
# 添加外键
sid = db.Column(db.Integer, db.ForeignKey('student.id'))


@app.route('/manytomany/')
def many_to_many():
s = Student.query.get(1)
c = Course.query.get(1)
# 学生选课
# s.courses.append(c)

# 取消选课
s.courses.remove(c)
# 保存更改
db.session.add(s)
return '操作已完成'


@app.route('/stu/<sid>')
def stu(sid):
s = Student.query.get(sid)
courses = s.courses.all()
return ','.join(c.name for c in courses)


@app.route('/course/<cid>')
def course(cid):
c = Course.query.get(cid)
students = c.students.all()
return ','.join(s.name for s in students)


@app.route('/onetoone/')
def one_to_one():
# 根据学生查详情
# s = Student.query.get(1)
# return s.profile.realname

# 根据详情查学生
p = Profile.query.get(1)
return p.stu.name


@app.route('/onetomany/<sid>')
def one_to_many(sid):
# 原始的解决方案
# articles = Article.query.filter(Article.sid == sid)
# s = Student.query.get(sid)
# articles = s.articles.all()
# return ','.join(a.title for a in articles)

a = Article.query.get(sid)
return a.stu.name


@app.route('/')
def index():
return '数据查询'


@app.route('/filter/')
def filter():
# 关系
# users = User.query.filter(User.id >= 1)
# users = User.query.filter(User.id.__gt__(1))
# 范围
# users = User.query.filter(User.id.between(1, 3))
# users = User.query.filter(User.id.in_((1, 3, 5)))
# users = User.query.filter(User.id.notin_((1, 3, 5)))
# 内容
# users = User.query.filter(User.username.contains('xiao'))
# users = User.query.filter(User.username.startswith('xiao'))
# users = User.query.filter(User.username.endswith('hua'))
# users = User.query.filter(User.username.like('hua%'))
# users = User.query.filter(User.username.notlike('%hua'))
# 逻辑处理
# 逻辑与and
# users = User.query.filter(User.id > 2, User.age > 17)
# users = User.query.filter(and_(User.id > 2, User.age > 17))
# users = User.query.filter(or_(User.id > 2, User.age > 17))
users = User.query.filter(not_(User.id > 2))
return ','.join(u.username for u in users)


@app.route('/query/<uid>')
def query(uid):
'''
# 根据ID进行查询
u = User.query.get(uid)
# get_or_404(uid),找到就返回,找不到就返回404
if u:
return u.username
return '查无此人'
'''

'''
# 查询所有数据
users = User.query.all()
return ','.join(u.username for u in users)
'''

'''
# 查询一条数据,返回单个对象
# u = User.query.first()
# first_or_404(),有返回第一条,没有报404
# return u.username

# 返回结果集中的第一条数据(需要遍历才能提取数据)
# users = User.query.filter(User.age > 16).limit(1)

# 限制结果集
users = User.query.filter(User.age > 16).offset(1).limit(3)
return ','.join(u.username for u in users)
'''

# 分页查询:paginate,项目中讲解

'''
# 排序,默认为升序asc,desc降序,可以指定多字段排序
users = User.query.order_by(User.age.desc(), User.id.desc())
return ','.join(u.username for u in users)
'''

# 统计结果集数量
c = User.query.count()
return str(c)


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

拓展

  • 快速复制一个虚拟环境
    1. 先将依赖的环境冷冻起来:pip freeze > requirement.txt
    2. 创建一个新的空虚拟化境:mkvirtualenv3 blog
    3. 选择新的虚拟环境:workon
    4. 安装相关的依赖包:pip install -r requirement.txt