Flask-SQLAlchemy笔记

13 篇文章 0 订阅
1 篇文章 0 订阅

flask-SQLAlchemy的一些笔记

所谓ORM技术是指将对数据库的操作映射到对类对象的操作,用起来更方便些。
python-flask中使用Flask-SQLAlchemy管理数据库,支持多种数据库后台。
安装方式为: sudo pip install flask-sqlalchemy

以下程序使用的数据库是mysql,除了一些配置参数稍有区别,其余对数据库类型没有影响

在flask程序中想要连接数据库需要在程序中加载一个配置参数:

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://usernamer:passwd@server/database'
db = SQLAlchemy(app)

可以在代码中使用 db.create_all() 测试是否连接成功

1、数据库的使用

创建数据库使用 db.create_all() 实现, 删库使用 db.drop_all()
如果数据库表已存在现有数据库中,那么 db.create_all() 不会执行任何更新操作或重建这个表,最粗暴的方法就是,删库再创建。
当然正确的做法应该是使用 Flask-Migrate 进行数据库迁移。

使用SQLAlchemy定义的数据模型需要继承自 db.Model
需要有一个类变量 __tablename__ 定义在数据库中使用的表名(如果省略了,会直接用类的类名作为表名)
其余类变量都是该模型的属性成员,被定义为 db.Column 的实例
db.Column 类构造函数的第一个参数可省略,如果不省略则是一个字符串类型变量,表名在数据库中的列名,如果省略了则是是数据库列和模型属性的类型,其余参数指定属性的配置选项

示例:

class Article(db.Model):
    __tablename__ = 'article'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text(), nullable=False)

    def __repr__(self):
        return '<Article %r>' % self.title

常用的SQLAlchemy列类型

类型Python类型说明
Integerint普通整数,一般是32位
SmallIntegerint取值范围小的整数,一般是16位
BigIntegerint或long不限制精度的整数
Floatfloat浮点数
Numericdecimal.Decimal定点数
Stringstr变长字符串,限长
Textstr变长字符串,不限长
Unicodeunicode变长unicode字符串,限长
UnicodeTextunicode变长unicode字符串,不限长
Booleanbool布尔值
Datedatetime.date日期
Timedatetime.time时间
DateTimedatetime.datetime日期和时间
Intervaldatetime.timedelta时间间隔
Enumstr一组字符串
PickleType任何Python对象自动使用Pickle序列化
LargeBinarystr二进制文件

常用的SQLAlchemy列选项

选项名说明
primary_key若为True,这列就是表的主键
unique若为True,这列不允许出现重复的值
index若为True,为这列创建索引,提升查询效率
nullable若为True,这列允许为null
default为这列定义默认值
autoincrement若为true,则自增

Flask-SQLAlchemy要求每个模型都要定义主键,这一列常被命名为id。
虽然没有强制要求,但为一般会为模型定义一个 __repr__() 方法,返回一个具有可读性的字符串模型(和__str__()类似),可在调试和测试时使用。

2、数据库常用操作(增删改查)

1. 增

在数据库表中添加一行

在ORM技术中增加很简单,其实就是创建一个对象,然后进行事务提交即可。
具体操作如下:

art = Article(title='title1', content='text body')
db.session.add(art)
db.session.commit()

如上所示, 构造一个模型对象,设置相应属性,最后进行事务提交即可。

2. 查

查找数据库中的一行数据

在ORM技术中,查找到的数据对象都被保存在模型对象中了。后面的改和删都是基于查找技术之上的。
Flask-SQLAlchemy为每个模型类都提供了query对象,供查询使用

# 查找所有title为title1的数据库行
art = Article.query.filter(Article.title=='title1').all()
art = Article.query.filter_by(title='title1').all()

# 查找tittle为title1的第一行
art = Article.query.filter(Article.title=='title1').first()
art = Article.query.filter_by(title='title1')[0]

首先看看前两行,这两行是查询所有title属性为title1的行,Article.query.filter(Article.title=='title1') 这一句其实相当于一个数据库查询语句,并没有直接获取所有数据库对象,想要看看原生的数据库查询语句可以打印下 str(Article.query.filter(Article.title=='title1'))

加个all()即可返回所有值的一个列表。若只需要第一个元素则可以直接first()

由前两行我们可以看出,按条件查询有两种常用方式,一种是 filter 另一种是 filter_by 。这两者的区别主要在于,filter是提供一种基本SQL查询,怎么说呢?看看filter方式的查询方法,至于一个 = 意味着赋值,也就是查询,按照谁等于谁的方法,当然filter可以提供多个参数,之间用,连接即可,也就是说想要用filter查询,你必须知道确切的取值。而filter_by则提供了一种更宽泛的查询,在其中可以进行一些逻辑比较之类的操作。

由后两句我们可以看出,前面的查询其实有点相当于一个列表了(只是相当于,我们还是需要用all()获取真正的所有对象的列表),因此如果我们要取出第一个元素可以使用 first() 或者直接像 list 那样的操作。当然要取任意位置的元素还是只能用第二种了,但是这里还是比较推崇第一种,为什么呢?首先,一般需要取单个元素的一般都是第一个,因为要取一个出来的时候,一般都是唯一一个,而不是从一堆里乱取一个,如果有很多个满足条件,肯定是取列表。因此需求一般是取一个出来。那么为什么第一个比第二个好呢?因为如果没有满足条件的时候,第一种方式会返回一个 None 值,而第二个会抛出异常,判断 None 可比 try...except... 好处理些吧。

上面的 all, filter, filter_by 其实都是 query 上的的过滤器。

常用的SQLAlchemy查询过滤器
过滤器说明
filter()把过滤器添加到原查询上,返回一个新查询
filter_by()把等值过滤器添加到原查询上,返回一个新查询
limit()使用指定的值限制原查询返回的结果数量,返回一个新查询
offset()偏移原查询返回的结果,返回一个新查询
order_by()根据指定条件对原查询进行排序,返回一个新查询
group_by()根据指定条件对原查询进行分组,返回一个新查询

看了上面说明那一栏应该能理解我上面说的,为什么前面的查询是相当于列表,而是不就是列表,因为它实际上还只是一个查询,还没有获取到具体的值对象。
在查询上应用指定过滤器后,还需要触发该查询获取真正的值对象。
除了上面介绍到了的 all(), first() 之外还有一些其它的方法。

常用的SQLAlchemy查询执行函数
方法说明
all()以列表形式返回查询的所有结果
first()返回查询的第一个结果,如果没有结果,则返回None
first_or_404()返回查询的第一个结果,如果没有结果,则终止请求,返回404错误响应
get()返回指定主键对应的行,如果没有对应的行,则返回None
get_or_404()返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回404错误响应
count()返回查询结果的数量
paginate()返回一个Paginate对象,它包含指定范围的结果

改其实就是把要修改的对象查找出来,进行属性值修改,再进行一次事务提交即可。

art = Article.query.filter_by(title='title1').first()
if art is not None:
    art.title = 'new title'
db.session.commit()

删除操作和修改操作是比较类似的,先查找出要删除的对象,进行删除操作,再进行一次事务提交即可。

art = Article.query.filter_by(title='title1').first()
if art is not None:
    db.session.delete(art)
db.session.commit()

顺带提下,事务回滚操作是 db.session.rollback()

数据库关系

首先定义两个表模型:

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(String(20), nullable=True)

    def __repr__(self):
        return "<user %s>" % self.username


class Article(db.Model):
    __tablename__ = 'article'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text(), nullable=False)
    author_id = db.Column(db.Integer)

    def __repr__(self):
        return "<article %s>" % self.title

外键约束

关系型数据库有个非常重要的概念就是外键约束。
那么在 Flask-SQLAlchemy 中怎么指定外键约束呢?

class Article(db.Model):
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))

应该使用 ForeignKey 来设置,并且需要注意的是,括号里的参数 user.id 这里的 user 是表名(__tablename__),而不是模型名(类名)

反向引用

Flask-SQLAlchemy 中还提供了一种反向引用的关系。反向引用可以根据外键约束找到关联的关系模型,此时可以直接获取相关的模型对象,而不是外键的值。

class User(db.Model):
    # ...
    xxx = db.relationship('Article', backref='user')

class Article(db.Model):
    # ...
    yyy = db.relationship('User', backref='articles')

这是干了个什么事儿呢?
假设有如下代码:

user = User(username="flask")
db.session.add(user)
db.session.commit()

art1 = Article(title="title1", content="text1", author_id=user.id)
art2 = Article(title="title2", content="text2", author_id=user.id)
db.session.add(art1)
db.session.add(art2)
db.session.commit()

# <user flask>
print(art1.user)
# <user flask>
print(art2.user)
# [<article title1>, <article title2>]
print(user.articles)

可以看到,建立了外键关系的对象,可以直接通过反向关系直接访问有关键关系的模型对象。是不是非常方便?
下面看看这个是怎么用的:
可以很明显猜到是 xxx = db.relationship('Article', backref='user')yyy = db.relationship('User', backref='articles') 这两句起的作用,分析下它们的共性,我们就能看明白反向引用的用法了。
首先 这里是随便定义了一个成员 xxx, yyy 这里想说的是,这里随便定义什么都不影响,这里只是举例告诉你不影响,所以随便取了名字,你以后在写的时候,最好不要这样,最好定义一些有意义的名字。
其次就是两个参数,第一个参数,不难发现,是和本表有外键约束的那个表的模型名(类名)而不是前面说的那样的是表名(__tablename__)。第二个参数,也还好理解,其实就是我们在进行反向引用时使用的那个变量。

我们在user表中加入了 articles 这个反向引用,我们就可以通过 u.articles 得到这个用户的所有article。同样,我们在article表中加入了 user 这个反向引用,我们就可以通过 art.user 得到这个art的user。

多对多关系

数据库中很多时候都需要我们定义一个多对多关系,数据库的正确处理方法是定义第三张表,SQLAlchemy中同样也是这样处理的。
首先定义两个数据模型,标签和文章。一个文章可能有多个标签,一个标签可能对应多个文章,所以就需要定义第三章表来处理这个关系了。

class Article(db.Model):
    __tablename__ = 'article'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(100), nullable=False)

class Tag(db.Model):
    __table__ = 'tag'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(20), nullable=False)

下面我们需要定义第三张表了,格式如下:

# 表名 成员属性1 成员属性2
article_tag = db.Table('article_tag',
        # 第一个参数是数据库属性名
        db.Column('article_id', db.Integer, db.Foreign_key('article.id'), primary_key=True)
        db.Column('tag_id', db.Integer, db.Foreign_key('tag.id'), primary_key=True)
    )

格式为创建一个 db.Table 的对象,注意这里不是再定义类了。然后传入第一个参数是数据库表名,然后两个参数就是相关联的两个表了。
这里db.Column多了一个字符串参数,这个参数其实就相当于前面写的 xxx = db.Column(...) 就相当于里面的xxx了,是属性名。
为什么这里要这样写呢?因为这里是在构造一个 db.Table 的对象,而不是在定义类成员属性了,所以就要换种方式写。

这里同样可以加入反向引用:

class Article(db.Model):
    # ...
    tags = db.relationship('Tag', secondary=article_tag, backref=db.backref('articles'))

可以看到这里反向引用是借助了第二章表,并在secondary参数中指定了这个表。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值