ORM 全拼Object-Relation Mapping. 称为对象-关系映射主要实现模型对象到关系数据库数据的映射.比如:把数据库表中每条记录映射为一个模型对象
优点 :
缺点 :
相比较直接使用SQL语句操作数据库,有性能损失.
安装
pip install flask-sqlalchemy
如果连接的是 mysql 数据库,需要安装 flask-mysqldb或者pymysql
pymysql 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库,Python2中则使mysqldb。
pip install flask-mysqldb
如果flask-mysqldb安装不上,安装, pip install pymysql
数据库连接设置
设置数据库的链接地址,追踪信息
格式:mysql://<用户名>:<密码>@:<端口>/数据库名
如果安装pymysql ,需要这样连接数据库
mysql+pymysql://<用户名>:<密码>@:<端口>/数据库名
# 数据库链接地址
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:[email protected]:3306/test'
# 动态追踪修改设置,如未设置只会提示警告
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
若要查看映射的sql语句,设置:app.config['SQLALCHEMY_ECHO'] = True
配置完成需要去 MySQL 中创建项目所使用的数据库
mysql -uroot -pmysql
create database test charset utf8;
"""
操作流程:
- 1.安装扩展
- pip install flask_sqlalchemy
- pip install flask_mysqldb / pymysql
- 2.设置数据库的配置信息
- 3.创建sqlalchemy对象db,关联app
- 4.编写模型类,字段,继承自db.Model,
- 5.操作数据库
- 增删改
- 查询
"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 2.设置数据库的配置信息
# 设置数据库的链接信息,
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://tom:[email protected]:3306/test"
# 该字段增加了大量的开销,会被禁用,建议设置为False
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
# 3.创建sqlalchemy对象db,关联app
db = SQLAlchemy(app)
# 4.编写模型类,字段,继承自db.Model,才能被映射
class Student(db.Model):
# 如果不指定表名称,默认就是类名的小写,需要自己指定,需要如下
__tablename__ = "students"
# 主键, 参数1: 表示id的类型, 参数2: 表示id的约束类型
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
@app.route('/')
def hello_world():
return "helloworld"
if __name__ == '__main__':
# 删除继承自db.Model的表
db.drop_all()
# 5.创建数据库的表,创建的是继承自db.Model的表
db.create_all()
app.run(debug=True)
mysql> show tables
-> ;
+----------------+
| Tables_in_test |
+----------------+
| students |
+----------------+
1 row in set (0.00 sec)
mysql> desc students;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(32) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
在Flask-SQLAlchemy中,插入、修改、删除操作,均由数据库会话管理。
会话用 db.session 表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用 db.session.commit() 方法提交会话。
在 Flask-SQLAlchemy 中,查询操作是通过 query 对象操作数据。
最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。
"""
增删改
- 全部都是使用db.session操作
- 常见方法:
- db.session.add(obj) 添加单个对象
- db.session.add_all([obj1,obj2]) 添加多个对象
- db.session.delete(obj) 删除单个对象
- db.session.commit() 提交会话
- db.drop_all() 删除继承自db.Model所有表
- db.create_all() 创建继承自db.Model的所有表
- 其他:
- db.session.rollback() 回滚
- db.session.remove() 移除会话
- 案例: 编写两个模型类, 一个角色模型类, 还有一个用户模型类
- 关系: 一对多
"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 1.设置数据库的配置信息
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://tom:[email protected]:3306/test"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
# 2.创建SQLalchemy对象,关联app
db = SQLAlchemy(app)
# 3.编写模型类
# 角色(一方)
class Role(db.Model):
__tablename__ = "roles"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
# 如果一个类继承自object那么重写__str__方法即可, 如果是继承自db.Model那么需要重写__repr__方法
def __repr__(self):
return "" % self.name
# 用户(多方)
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
# 建立外键
role_id = db.Column(db.Integer, db.ForeignKey(Role.id))
# 如果一个类继承自object那么重写__str__方法即可修改对象的默认输出, 如果是继承自db.Model那么需要重写__repr__方法
def __repr__(self):
return "" % self.name
@app.route('/')
def hello_world():
return "helloworld"
if __name__ == '__main__':
# 先删除表,后创建
db.drop_all()
db.create_all()
app.run(debug=True)
mysql> desc roles;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(32) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
mysql> desc users;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(32) | YES | | NULL | |
| role_id | int(11) | YES | MUL | NULL | |
+---------+-------------+------+-----+---------+----------------+
具体操作:
In [1]: from demo02role_user import *
In [2]: role=Role(name="admin")
In [3]: role2=Role(name="ops")
In [5]: db.session.add_all([role,role2])
In [6]: db.session.commit()
mysql> select * from roles;
+----+-------+
| id | name |
+----+-------+
| 1 | admin |
| 2 | ops |
+----+-------+
In [7]: user1=User(name="laowang",role_id=role.id)
In [8]: user2=User(name="laozhang",role_id=role2.id)
In [9]: db.session.add_all([user1,user2])
In [10]: db.session.commit()
mysql> select * from users;
+----+----------+---------+
| id | name | role_id |
+----+----------+---------+
| 1 | laowang | 1 |
| 2 | laozhang | 2 |
+----+----------+---------+
2 rows in set (0.00 sec)
In [11]: user2.name
Out[11]: 'laozhang'
In [12]: user2.name="laowu" # 改
In [13]: db.session.commit()
mysql> select * from users;
+----+---------+---------+
| id | name | role_id |
+----+---------+---------+
| 1 | laowang | 1 |
| 2 | laowu | 2 |
+----+---------+---------+
2 rows in set (0.00 sec)
In [14]: db.session.delete(user2) #删
In [15]: db.session.commit()
mysql> select * from users;
+----+---------+---------+
| id | name | role_id |
+----+---------+---------+
| 1 | laowang | 1 |
+----+---------+---------+
realtionship描述了Role和User的关系。
第一个参数为对应参照的类"User"
第二个参数backref为类User,反向引用属性
第三个参数lazy决定了什么时候SQLALchemy从数据库中加载数据
如果设置为子查询方式(subquery),则会在加载完Role对象后,就立即加载与其关联的对象,这样会让总查询数量减少,但如果返回的条目数量很多,就会比较慢
设置为 subquery 的话,role.users 返回所有数据列表
另外,也可以设置为动态方式(dynamic),这样关联对象会在被使用的时候再进行加载,并且在返回前进行过滤,如果返回的对象数很多,或者未来会变得很多,那最好采用这种方式
"""
查询
relationship 属性方便关联查询,通常写在一方,并且不会在数据库产生响应的字段
"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 1.设置数据库的配置信息
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://tom:[email protected]:3306/test"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_ECHO"] = True
# 2.创建SQLalchemy对象,关联app
db = SQLAlchemy(app)
# 3.编写模型类
# 角色(一方)
class Role(db.Model):
__tablename__ = "roles"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
# 给role添加了一个users属性, 那么查询的方式是, role.users
# 给user添加了一个role属性, 那么查询的方式是, user.role
users = db.relationship("User", backref="role", lazy="dynamic")
# 如果一个类继承自object那么重写__str__方法即可, 如果是继承自db.Model那么需要重写__repr__方法
def __repr__(self):
return "" % self.name
# 用户(多方)
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
email = db.Column(db.String(32))
password = db.Column(db.String(32))
# 建立外键
role_id = db.Column(db.Integer, db.ForeignKey(Role.id))
# 如果一个类继承自object那么重写__str__方法即可, 如果是继承自db.Model那么需要重写__repr__方法
def __repr__(self):
return "" % (self.id, self.name, self.email, self.password)
@app.route('/')
def hello_world():
return "helloworld"
if __name__ == '__main__':
# 为了演示方便,先删除表,后创建
db.drop_all()
db.create_all()
# 创建测试数据
ro1 = Role(name='admin')
db.session.add(ro1)
db.session.commit()
# 再次插入一条数据
ro2 = Role(name='user')
db.session.add(ro2)
db.session.commit()
# 多条用户数据
us1 = User(name='wang', email='[email protected]', password='123456', role_id=ro1.id)
us2 = User(name='zhang', email='[email protected]', password='201512', role_id=ro2.id)
us3 = User(name='chen', email='[email protected]', password='987654', role_id=ro2.id)
us4 = User(name='zhou', email='[email protected]', password='456789', role_id=ro1.id)
us5 = User(name='tang', email='[email protected]', password='158104', role_id=ro2.id)
us6 = User(name='wu', email='[email protected]', password='5623514', role_id=ro2.id)
us7 = User(name='qian', email='[email protected]', password='1543567', role_id=ro1.id)
us8 = User(name='liu', email='[email protected]', password='867322', role_id=ro1.id)
us9 = User(name='li', email='[email protected]', password='4526342', role_id=ro2.id)
us10 = User(name='sun', email='[email protected]', password='235523', role_id=ro2.id)
db.session.add_all([us1, us2, us3, us4, us5, us6, us7, us8, us9, us10])
db.session.commit()
app.run(debug=True)
查:
数据库增加,删除,修改操作:
增加:
user = User(name='laowang')
db.session.add(user)
db.session.commit()
修改:
user.name = 'xiaohua'
db.session.commit()
删除:
db.session.delete(user)
db.session.commit()
以下12条查询语句:
特点:
模型.query: 得到了所有模型的数据的结果集对象
模型.query.过滤器: 过滤出了想要的数据,还是一个查询结果集对象
模型.query.过滤器.执行器: 取出了结果集中的内容
查询所有用户数据
User.query.all() ==> [user1,user2]
查询有多少个用户
User.query.count()
查询第1个用户
User.query.first()
查询id为4的用户[3种方式]
User.query.get(4) (这里面只能是数字 或 ‘4’) 查询不到不会报错
User.query.filter_by(id = 4).first() (一个等号)查询不到不会报错
User.query.filter(User.id == 4).first() 查询不到不会报错
查询名字结尾字符为g的所有数据[开始/包含]
User.query.filter(User.name.endswith('g')).all()
User.query.filter(User.name.startswith('g')).all()
User.query.filter(User.name.contains('g')).all()
查询名字不等于wang的所有数据[2种方式]
User.query.filter(User.name != 'wang').all()
或者
from sqlalchemy import not_
User.query.filter(not_(User.name == 'wang')).all()
查询名字和邮箱都以 li 开头的所有数据[2种方式]
from sqlalchemy import and_
User.query.filter(User.name.startswith('li'),User.email.startswith('li')).all()
User.query.filter(and_(User.name.startswith('li'),User.email.startswith('li'))).all()
查询password是 `123456` 或者 `email` 以 `com` 结尾的所有数据
from sqlalchemy import or_
User.query.filter(or_(User.password == '123456',User.email.endswith('com'))).all()
查询id为 [1, 3, 5, 7, 9] 的用户列表
User.query.filter(User.id.in_([1,3,5,7,9])).all()
查询name为liu的角色数据
user = User.query.filter(User.name == 'liu').first()
role = Role.query.filter(Role.id == user.role_id).first()
查询所有用户数据,并以邮箱排序
User.query.order_by(User.email).all()
User.query.order_by(User.email.desc()).all() # 升序
每页3个,查询第2页的数据
paginate = User.query.paginate(page, per_page,Error_out)
paginate = User.query.paginate(2,3,False)
page: 哪一个页
per_page: 每页多少条数据
Error_out: False 查不到不报错
paginate .pages: 共有多少页
paginate .items: 当前页数的所有对象
paginate .page: 当前第几页
限制查询2条数据
User.query.limit(2).all()
sqlalchemy查询常见的过滤方式
filter过滤条件:
过滤是数据提取的一个很重要的功能,以下对一些常用的过滤条件进行解释,并且这些过滤条件都是只能通过filter方法实现的:
1. equals : ==
news= session.query(News).filter(News.title == "title1").first()
query里面是列查询,字段,传入模型名表示全字段 。
filter是行查询,没有filter表示全表查询
2. not equals : !=
query(User).filter(User.name != 'ed')
3. like[不区分大小写] & ilike[区分大小写]:
query(User).filter(User.name.like('%ed%··'))
query(User).filter(User.name.like('__ed%__')) _表示站位符
4. in:
query(User).filter(User.name.in_(['ed','wendy','jack']))
5. not in:
query(User).filter(~User.name.in_(['ed','wendy','jack']))
6. is null:
query(User).filter(User.name==None)
# 或者是
query(User).filter(User.name.is_(None))
7. is not null:
query(User).filter(User.name != None)
# 或者是
query(User).filter(User.name.isnot(None))
8. and:
query(User).filter(and_(User.name=='ed',User.fullname=='Ed Jones'))
# 或者是传递多个参数
query(User).filter(User.name=='ed',User.fullname=='Ed Jones')
# 或者是通过多次filter操作
query(User).filter(User.name=='ed').filter(User.fullname=='Ed Jones')
9. or:
query(User).filter(or_(User.name=='ed',User.name=='wendy'))
如果想要查看orm底层转换的sql语句,可以在filter方法后面不要再执行任何方法直接打印就可以看到了。比如:
news = session.query(News).filter(or_(News.title=='abc',News.content=='abc'))
print(news)
* 解释: 为了方便数据库的关联查询
* 特点:
* 1.不会在数据库产生实体字段
* 2.关系属性,需要在一方添加
* 3.外键是添加在多方的, 并且关系属性的使用,需要依赖于外键
* 需求:
* 1.如果知道了角色的情况下,能否快速查询出,哪些用户扮演了该角色
* 原始查询方法:
* role = Role.query.get(1)
* users = User.query.filter(User.role_id == role.id).all()
* 快速查询:
* 使用relationship添加关系属性,就可以快速查询了.
* 格式:在一方添加
* users = db.relationship("多方的模型类")
* 使用格式:
* role = Role.query.get(1)
* users = role.users
* 解释:如果知道了用户的情况下,能否快速查询出,哪些该用户扮演了哪个角色
- 原始查询方法:
- user = User.query.get(1)
- role = Role.querye.filter(Role.id == user.role_id).first()
- 快速查询:
- 使用backref添加反向属性,就可以快速查询了
- 格式:
- users = db.relationship("多方的模型类",backref="role")
- 快速查询:
- user = User.query.get(1)
- role = user.role
* 解释: 一旦使用了relationship,backref,那么系统会自动做子查询
* 默认子查询(subquery): 查询出了一方, 就会自动的将关联的一方查询出来,会影响系统性能
* 要设置为动态查询(dynamic): 只有用到了才去查询
* lazy使用
* db.relationship("多方的模型类",backref="role",lazy="dynamic")
user.role.all()