使用 Flask-SQLAlchemy 从数据库中查询数据,可以指定查询的条件。数据库中的数据很多,用户需要的只是某一条数据或满足某个条件的数据。
在 Flask-SQLAlchemy 中,指定查询条件是通过数据对象的 query 对象来实现的,query 对象中实现了很多常用的过滤方法,可以方便地实现过滤查询。
一、准备数据库和数据表
1. 创建一个 flask_alchemy_search.py 文件,编写连接数据库和模型类的代码并运行,创建两个数据表。
代码语言:javascript复制from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://admin:Mysql!123@127.0.0.1:3306/MyDB_one'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
class Phone(db.Model):
__tablename__ = 'Phone_tb'
pid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
person = db.relationship('Person', backref='phone', lazy='dynamic')
def __repr__(self):
return 'Phone_name: {}'.format(self.name)
class Person(db.Model):
__tablename__ = 'Person_tb'
mid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
age = db.Column(db.Integer)
phone_id = db.Column(db.Integer, db.ForeignKey('Phone_tb.pid'))
def __repr__(self):
return 'Person_name: {}'.format(self.name)
db.drop_all()
db.create_all()
if __name__ == '__main__':
app.run(debug=True)
使用创建好的 MySQL 用户 admin 连接数据库,在 MyDB_one 数据库中先删除再创建两张数据表 Phone_tb 和 Person_tb 。
2. 创建完成数据表后,将代码中的 db.drop_all() ,db.create_all() 和 app.run(debug=True) 注释掉。
二、在数据表中批量插入数据
因为相同的代码在之前已经使用过,所以在准备数据表时,先将数据表删除了,重新建新的表。数据表是空,要查询数据,数据表中首先要有数据,先批量添加数据到数据表中。
代码语言:javascript复制if __name__ == '__main__':
phone_one = Phone(name='IPhone')
phone_two = Phone(name='Mi')
phone_three = Phone(name='NOKIA')
phone_four = Phone(name='HUAWEI')
phone_five = Phone(name='OPPO')
phone_six = Phone(name='VIVO')
db.session.add_all([phone_one, phone_two, phone_three, phone_four, phone_five, phone_six])
db.session.commit()
per_one = Person(name='You', age=18, phone_id=1)
per_two = Person(name='Me', age=81, phone_id=3)
per_three = Person(name='JackMa', age=60, phone_id=2)
per_four = Person(name='Panshiyi', age=50, phone_id=4)
per_five = Person(name='DingLei', age=40, phone_id=1)
db.session.add_all([per_one, per_two, per_three, per_four, per_five])
db.session.commit()
在 flask_alchemy_search.py 中继续添加上面的代码,运行,会在数据表 Phone_tb 中添加 6 条数据,在数据表 Person_tb 中添加 5 条数据。
这些数据用于后面使用 Flask-SQLAlchemy 进行过滤查询的素材。
三、使用 Flask-SQLAlchemy 进行过滤查询
数据添加完成,注释掉添加数据的代码,(表中有唯一字段,重复添加会报错),然后开始查询数据。
1. 查询表中的所有对象
代码语言:javascript复制 all_person = Person.query.all()
print(all_person)
使用 query 对象的 all() 方法来查询表中的所有数据,返回的结果是所有数据组成的一个列表,显示的字段是 __repr__ 中定义的字段。
如查询 Person_tb 中的所有数据,运行结果:
代码语言:javascript复制[Person_name: You, Person_name: Me, Person_name: JackMa, Person_name: Panshiyi, Person_name: DingLei]
2. 查询表中的第一个对象
代码语言:javascript复制 first = Person.query.first()
print(first)
使用 query 对象的 first() 方法来查询表中的第一条数据。
代码语言:javascript复制Person_name: You
3. 主键查询,如果主键不存在则无返回内容
代码语言:javascript复制 three = Person.query.get(3)
print(three)
使用 query 对象的 get() 方法来根据主键查询数据,在 get() 中传入一个表中存在的主键值。
代码语言:javascript复制Person_name: JackMa
4. 精确查询,获取满足条件的数据
代码语言:javascript复制 person_p = Person.query.filter_by(name='Panshiyi').all()
print(person_p)
使用 query 对象的 filter_by() 方法来指定条件查询精确的数据,精确查询需要指定某个字段完整的值。
在 filter_by() 中通过键值对指定查询条件,在 filter_by() 方法后需要链式跟上 all() 方法,才能返回查询对象。
代码语言:javascript复制[Person_name: Panshiyi]
5. 模糊查询,返回满足条件的数据
代码语言:javascript复制 person_i = Person.query.filter(Person.name.endswith('i')).all()
print(person_i)
使用 query 对象的 filter() 方法来查询满足条件的数据,在 filter() 中通过数据对象的字段特征来指定查询条件。
代码语言:javascript复制[Person_name: Panshiyi, Person_name: DingLei]
6. 通过 != 进行取反查询
代码语言:javascript复制 boss = Person.query.filter(Person.name != 'Me').all()
print(boss)
在 filter() 方法中,指定对象属性的取反条件,可以完成逻辑非的查询。
代码语言:javascript复制[Person_name: You, Person_name: JackMa, Person_name: Panshiyi, Person_name: DingLei]
7. and_ 进行 逻辑与 查询
代码语言:javascript复制 from sqlalchemy import and_
oppo = Phone.query.filter(and_(Phone.name.startswith('o'), Phone.name.endswith('o'))).all()
print(oppo)
先从 sqlalchemy 中导入 and_ ,用于 逻辑与 查询。将并列的条件写在 and_() 中,返回结果是同时满足 and_() 中的所有条件的所有数据。
上面创建了两张数据表,现在换一张表查询,如查询开头结尾都是 o 字母的手机品牌,运行结果如下:
代码语言:javascript复制[Phone_name: OPPO]
8. not_ 进行 逻辑非 查询
代码语言:javascript复制 from sqlalchemy import and_, not_
vivo = Phone.query.filter(and_(not_(Phone.name.startswith('o')), Phone.name.endswith('o'))).all()
print(vivo)
上面使用 != 指定对象属性可以实现逻辑非,也可以使用 sqlalchemy 中的 not_ 实现逻辑非查询。
先从 sqlalchemy 中导入 not_ ,将取反的条件写在 not_() 中,返回的查询结果就是取反的结果。
代码语言:javascript复制[Phone_name: VIVO]
9. or_ 进行 逻辑或 查询
代码语言:javascript复制 from sqlalchemy import or_
phone_i = Phone.query.filter(or_(Phone.name.startswith('i'), Phone.name.endswith('i'))).all()
print(phone_i)
先从 sqlalchemy 中导入 or_ ,将查询的条件写在 or_() 中,返回的查询结果是满足其中任意一条条件的所有数据。
代码语言:javascript复制[Phone_name: IPhone, Phone_name: Mi, Phone_name: HUAWEI]
四、Flask-SQLAlchemy 关系字段和关联查询
在上面创建的两张表中,已经设置了关系字段。
Person 与 Phone 的关系是一对多的关系。
在 Person 模型类中,定义了关系字段 phone_id 。
代码语言:javascript复制phone_id = db.Column(db.Integer, db.ForeignKey('Phone_tb.pid'))
这个关系字段关联到了 Phone 模型类中的主键 pid 。
在 Phone 模型类中,定义了关系字段 person 。
代码语言:javascript复制person = db.relationship('Person', backref='phone', lazy='dynamic')
通过 realtionship 描述了 Phone 和 Person 的关系,这种关系有一对多,多对多等,上面的两张表是一对多的关系,Person 是 '一' ,Phone 是 '多' ,realtionship 字段定义在 '多' 的模型类中。
在 realtionship 中,第一个参数为对应 '一' 的模型类 "Person" 。
第二个参数 backref 是在模型类 Person 中申明一条新属性的方法,这个属性名是通过关系字段查询数据时使用的属性。在数据表中,不会创建这个字段(也可以说是隐藏字段),但是这个属性名不能与 Person 中已有的属性同名,否则属性冲突,在数据表中添加数据时会报错。
第三个参数 lazy 是可选的,决定了什么时候 SQLALchemy 从数据库中加载数据,是一种优化查询速度的方式,对于数据量大或查询条件比较复杂时会有用,具体可以自己扩展一下。
接下来,进行关联查询。
代码语言:javascript复制 me = Person.query.get(2)
me_phone = me.phone
print(me_phone)
先从 Person 中查出一条数据,然后通过关系字段查询 Phone 中的数据,返回的是一个 Phone 对象,而不是一个字段值。
这就完成了从 Person 中的关系字段查询到 Phone 中的对象。
代码语言:javascript复制Phone_name: NOKIA