flask学习笔记(十三) 数据库补充
欢迎加入知了课堂,学习flask
Python Flask系列(1)——基础:http://study.163.com/course/courseMain.htm?courseId=1004091002
Python Flask框架——全栈开发: http://study.163.com/course/courseMain.htm?courseId=1004507006
一、数据库
数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户可以对数据库中的数据进行增加,修改,删除及查询操作。通过数据库管理系统对数据库进行管理控制,这里使用MySQL数据库。MySQL中可以有多个数据库,数据库是真正存储数据的地方。
数据库中以表为组织单位存储数据,根据表字段所规定的数据类型,我们可以向其中填入一条条的数据,叫做记录。
下面通过Navicat for MySQL 看到的数据库,
MySQL中可以管理多个数据库(左图),一个数据库用多个数据表(右图)。点进去其中一个数据库表,
每一列最上面的书字段名,代表表中的一个属性。下面每一行代表一条记录。
我们SQLAlchemy ORM为数据库引擎提供抽象层,可以像使用python类一样管理数据库
一个类 -> 数据库中的一张表
类中的属性 -> 数据库中一张表字段
类的一个对象 -> 数据库中表的一条数据
具体方法,上一个笔记已有演示。
二、SQLAlchemy常用数据类型 & Column常用参数
通过字段给数据表中的数据添加指定数据类型
Column()中使用参数设置字段
例如: id = Column(Integer,primary_key=True,autoincrement=True)
第一个参数 Integer ,指定id为整型,映射到数据库中是int类型
primary_key:设置id字段为主键
autoincrement:设置这个字段为自动增长的
三、query查询与filter过滤补充
在实现数据库的增删改查中,其中用到查询和数据的过滤提取。
1. query可用参数:
模型对象。指定查找这个模型中所有的对象。
articles = session.query(Article).all()
for article in articles:
print( article )
模型中的属性。可以指定只查找某个模型的其中几个属性。
articles = session.query(Article.title,Article.price).all()
print(articles)
聚合函数。
* func.count:统计行的数量。
* func.avg:求平均值。
* func.max:求最大值。
* func.min:求最小值。
* func.sum:求和。
例如,
result = session.query(func.count(Article.id)).first()
print(result)
PS:query方法查找返回的结果,完全取决于它要查询的内容,也就是query后面括号里面的东西!!!
2. filter过滤条件:
过滤是数据提取的一个很重要的功能,以下对一些常用的过滤条件进行解释,并且这些过滤条件都是只能通过filter方法实现的:
例如,下面实现查找模型对象中,title字段名与title0匹配的第一个查询对象。
article = session.query(Article).filter(Article.title == "title0").first()
print(article)
PS:如果想要查看orm底层转换的sql语句,可以在filter方法后面不要再执行任何方法直接打印就可以看到了。比如,
articles = session.query(Article).filter(or_(Article.title=='abc',Article.content=='abc'))
print(articles)
四、外键以及ORM关系
在关系型数据库中,表与表之间显然是可以有关联的,它们通过 外键 进行关联;多表之间的关系又分为多对一、一对一、多对多。这些都是通过ORM来实现的。
1.外键:
使用SQLAlchemy创建外键非常简单。在从表中增加一个字段,指定这个字段外键的是哪个表的哪个字段就可以了。从表中外键的字段,必须和父表的主键字段类型保持一致。例如,在用户表中使用
uid = Column(Integer,ForeignKey("user.id"))
通过外键让 uid 这个字段和另外一个user的 id 字段关联了。
外键约束有以下几项:
1. RESTRICT:父表数据被删除,会阻止删除。默认就是这一项。
2. NO ACTION:在MySQL中,同RESTRICT。
3. CASCADE:级联删除。
4. SET NULL:父表数据被删除,子表数据会设置为NULL。
一对多
mysql级别的外键,还不够ORM,必须拿到一个表的外键,然后通过这个外键再去另外一张表中查找,这样太麻烦了。SQLAlchemy提供了一个`relationship`,这个类可以定义属性,以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问得到了。
class User(Base):
__tablename__ = 'user'
id = Column(Integer,primary_key=True,autoincrement=True)
username = Column(String(50),nullable=False)
def __repr__(self):
return "<User(username:%s)>" % self.username
class Article(Base):
__tablename__ = 'article'
id = Column(Integer,primary_key=True,autoincrement=True)
title = Column(String(50),nullable=False)
content = Column(Text,nullable=False)
uid = Column(Integer,ForeignKey("user.id"))
author = relationship("User",backref="articles")
另外,可以通过`backref`来指定反向访问的属性名称。articles是有多个。他们之间的关系是一个一对多的关系。
__repr__在python中是一个魔术方法,在这里的作用是,一旦使用print打印User对象时,会通过这个方法返回一个可以用来表示对象的可打印字符串
一对一
在sqlalchemy中,如果想要将两个模型映射成一对一的关系,那么应该在父模型中,指定引用的时候,要传递一个`uselist=False`这个参数进去。就是告诉父模型,以后引用这个从模型的时候,不再是一个列表了,而是一个对象了。
extend = relationship( "UserExtend", uselist=False )
多对多
具体操作流程:
1. 多对多的关系需要通过一张中间表来绑定他们之间的关系。
2. 先把两个需要做多对多的模型定义出来
3. 使用Table定义一个中间表,中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”。
4. 在两个需要做多对多的模型中随便选择一个模型,定义一个relationship属性,来绑定三者之间的关系,在使用relationship的时候,需要传入一个secondary=中间表。
五、ORM层面的删除数据
ORM层面删除数据,会无视mysql级别的外键约束。直接会将对应的数据删除,然后将从表中的那个外键设置为NULL。如果想要避免这种行为,应该将从表中的外键的`nullable=False`。
六、排序
可以通过sqlalchemy对提取出来的数据,在一定条件下进行排序,
1.在模型定义的时候指定默认排序:有些时候,不想每次在查询的时候都指定排序的方式,可以在定义模型的时候就指定排序的方式。有以下两种方式:
* relationship的order_by参数:在指定relationship的时候,传递order_by参数来指定排序的字段。
author = relationship("User",backref=backref("articles",order_by=create_time.desc()))
* 在模型定义中,添加以下代码:
__mapper_args__ = {
"order_by": create_time
}
使用示例
user = session.query(User).first()
print(user.articles)
直接打印出已经排序好的数据列。
2.正序排序与倒序排序:默认是使用正序排序。如果需要使用倒序排序,那么可以使用这个字段的`desc()`方法,或者是在排序的时候使用这个字段的字符串名字,然后在前面加一个负号。
articles = session.query(Article).order_by(Article.id.desc())
七、limit、offset和切片操作
就像python中对列表的切片一样,
1. limit:可以限制每次查询的时候只查询几条数据。
2. offset:可以限制查找数据的时候过滤掉前面多少条。
3. 切片:可以对Query对象使用切片操作,来获取想要的数据。可以使用`slice(start,stop)`方法来做切片操作。也可以使用`[start:stop]`的方式来进行切片操作。一般在实际开发中,中括号的形式是用得比较多的。
articles = session.query(Article).order_by(Article.id.desc())[0:10]
print(articles)
八、懒加载
在一对多,或者多对多的时候,如果想要获取多的这一部分的数据的时候,往往能通过一个属性就可以全部获取了。比如有一个作者,想要或者这个作者的所有文章,那么可以通过user.articles就可以获取所有的。但有时候我们不想获取所有的数据,比如只想获取这个作者今天发表的文章,那么这时候我们可以给relationship传递一个lazy='dynamic',以后通过user.articles获取到的就不是一个列表,而是一个AppenderQuery对象了。这样就可以对这个对象再进行一层过滤和排序等操作。
通过`lazy='dynamic'`,获取出来的多的那一部分的数据,就是一个`AppenderQuery`对象了。这种对象既可以添加新数据,也可以跟`Query`一样,可以再进行一层过滤。
总而言之一句话:如果你在获取数据的时候,想要对多的那一边的数据再进行一层过滤,那么这时候就可以考虑使用`lazy='dynamic'`。
九、group_by分组与having再次过滤
1.根据某个字段进行分组。比如想要根据性别进行分组,来统计每个分组分别有多少人,那么可以使用以下代码来完成:
session.query(User.gender,func.count(User.id)).group_by(User.gender).all()
2.having是对查找结果进一步过滤。比如只想要看未成年人的数量,那么可以首先对年龄进行分组统计人数,然后再对分组进行having过滤。示例代码如下:
result = session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age >= 18).all()
十、join进行数据库连接
join分为left join(左外连接)和right join(右外连接)以及内连接(等值连接)。
参考的网页:http://www.jb51.net/article/15386.html
在sqlalchemy中,使用join来完成内连接。在写join的时候,如果不写join的条件,那么默认将使用外键来作为条件连接。
比如现在要实现一个功能,要查找所有用户,按照发表文章的数量来进行排序。示例代码如下:
result = session.query(User,func.count(Article.id)).join(Article).
group_by(User.id).order_by(func.count(Article.id).desc()).all()
十一、subquery:子查询
子查询可以让多个查询变成一个查询,只要查找一次数据库,性能相对来讲更加高效一点。不用写多个sql语句就可以实现一些复杂的查询。那么在sqlalchemy中,要实现一个子查询,应该使用以下几个步骤:
1. 将子查询按照传统的方式写好查询代码,然后在`query`对象后面执行`subquery`方法,将这个查询变成一个子查询。
2. 在子查询中,将以后需要用到的字段通过`label`方法,取个别名。
3. 在父查询中,如果想要使用子查询的字段,那么可以通过子查询的返回值上的`c`属性拿到。
stmt = session.query(User.city.label("city"),User.age.label("age")).filter(User.username=='李A').subquery()
result = session.query(User).filter(User.city==stmt.c.city,User.age==stmt.c.age).all()
print(result)
在数据库中寻找和李A在同一城市同一个年龄的人,打印结果!
重要参考:知了课堂Flask课堂笔记
欢迎加入知了课堂,学习flask
Python Flask系列(1)——基础:http://study.163.com/course/courseMain.htm?courseId=1004091002
Python Flask框架——全栈开发: http://study.163.com/course/courseMain.htm?courseId=1004507006