SQLAlchemy操作

新增
user = User(mobile="15270496981", name="xlz")
# 生成sql语句
db.session.add(user)
# 执行sql语句
db.session.commit()

对于批量添加也可使用如下语法:

user1 = User(mobile="15270496981", name="xlz1")
user2 = User(mobile="15270496982", name="xlz2")
# 生成sql语句
db.session.add_all([user1, user2])
# 执行sql语句
db.session.commit()
查询
  1. all():查询所有,返回列表
User.query.all()
  1. first():查询第一个,返回对象
User.query.first()
  1. get():根据主键ID获取对象,若主键不存在返回None
User.query.get(2)
  1. 其它
db.session.query(User).all()
db.session.query(User).first()
db.session.query(User).get(2)
过虑
filter_by
user1 = User.query.filter_by(mobile="18516952650").first()
# and关系
user2 = User.query.filter_by(mobile="18516952650", id=1).first()
print(user1)
print(user2)
filter
User.query.filter(User.mobile=='18516952650').first()
逻辑或:or_
  1. 导入
from sqlalchemy import or_
  1. 使用
# 逻辑或
users = User.query.filter(or_(User.mobile=="18516952650", User.mobile.startswith("185"))).all()
    for user in users:
        print(user)
逻辑与:and_
  1. 导入
from sqlalchemy import and_
  1. 使用
# 逻辑与
users = User.query.filter(and_(User.mobile.startswith("182"), User.name.startswith("我是"))).all()
    for user in users:
        print(user.name, user.mobile)
逻辑非:not_
  1. 导入
from sqlalchemy import not_
  1. 使用
# 逻辑非
users = User.query.filter(not_(User.mobile.startswith("182"))).all()
for user in users:
    print(user.name, user.mobile)
offset:起始位置
# offset 偏移,起始位置
users = User.query.offset(3).all()
for user in users:
    print(user.id, user.mobile)
limit:获取限制数据
# limit
users = User.query.limit(3).all()
for user in users:
    print(user.name, user.mobile)
order_by
# order_by 排序
 User.query.order_by(User.id).all() # 正序
 User.query.order_by(User.id.desc()).all() # 倒序
复合查询
User.query.filter(User.name.startswith('13')).order_by(User.id.desc()).offset(2).limit(5).all()
query = User.query.filter(User.name.startswith('13'))
query = query.order_by(User.id.desc())
query = query.offset(2).limit(5)
ret = query.all()
优化查询
user = User.query.filter_by(id=1).first()  # 查询所有字段
select user_id, mobile......

select * from   # 程序不要使用
select user_id, mobile,.... # 查询指定字段

from sqlalchemy.orm import load_only
User.query.options(load_only(User.name, User.mobile)).filter_by(id=1).first() # 查询特定字段
聚合查询
from sqlalchemy import func

db.session.query(Relation.user_id, func.count(Relation.target_user_id)).filter(Relation.relation == Relation.RELATION.FOLLOW).group_by(Relation.user_id).all()
综合练习

SQL语句:

# 1.查询手机号码为18516952650的用户
sql: select * from user_basic where mobile='18516952650').first();
# 2.查询手机号以13开始的, 并且发布文章的数量大于10篇的用户
sql: select * from user_basic where mobile like '13%' and article_count > 10;
# 3.查询手机号以13结尾的, 或者发布文章的数量大于10篇的用户
sql: select * from user_basic where mobile like '%13' or article_count > 10;
# 4.查询手机号不是以13结尾的用户
sql: select * from user_basic where mobile not like '%13';
# 5.查询手机号不是以13结尾的用户,获取前面的第3条开始的5条数据
sql: select * from user_basic where mobile not like '%13' limit 3, 5;
# 6.查询手机号不是以13结尾的用户,以发布文章数量排序(正/降),获取前面的第3条开始的5条数据
sql: select user_id, user_name from user_basic where mobile not like '%13' order by article_count limit 3, 5;
# 7.查询关系表,以user_id分组, 查出每个用户当前的关注数量.
sql: select user_id, count(target_user_id) from user_relation group by user_id;
from datetime import datetime
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import and_, or_, not_, func
from sqlalchemy.orm import load_only

app = Flask(__name__)

# 1.配置mysql
class Config(object):
    # 配置数据库
    SQLALCHEMY_DATABASE_URI = 'mysql://root:mysql@127.0.0.1:3306/toutiao'
    # 是否追踪数据的改变,设置为False即可,后面会废弃。
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    # 是否显示sqlalchemy执行的sql语句
    # SQLALCHEMY_ECHO = True

app.config.from_object(Config)

# 2.创建db对象
db = SQLAlchemy(app)

# 3.定义模型类
class User(db.Model):
    # 指定映射数据库中的哪张表
    __tablename__ = "user_basic"

    # 此类是业务自己用的,跟SQLAlchemy没关系
    class STATUS:
        ENABLE = 1
        DISABLE = 0

    #  db.Column的第一个参数映射对应的字段
    id = db.Column('user_id', db.Integer, primary_key=True, doc='用户ID')
    mobile = db.Column(db.String, doc='手机号')
    password = db.Column(db.String, doc='密码')
    name = db.Column('user_name', db.String, doc='昵称')
    profile_photo = db.Column(db.String, doc='头像')
    last_login = db.Column(db.DateTime, doc='最后登录时间')
    is_media = db.Column(db.Boolean, default=False, doc='是否是自媒体')
    is_verified = db.Column(db.Boolean, default=False, doc='是否实名认证')
    introduction = db.Column(db.String, doc='简介')
    certificate = db.Column(db.String, doc='认证')
    article_count = db.Column(db.Integer, default=0, doc='发帖数')
    following_count = db.Column(db.Integer, default=0, doc='关注的人数')
    fans_count = db.Column(db.Integer, default=0, doc='被关注的人数(粉丝数)')
    like_count = db.Column(db.Integer, default=0, doc='累计点赞人数')
    read_count = db.Column(db.Integer, default=0, doc='累计阅读人数')
    account = db.Column(db.String, doc='账号')
    email = db.Column(db.String, doc='邮箱')
    status = db.Column(db.Integer, default=1, doc='状态,是否可用')

class Relation(db.Model):
    """
    用户关系表
    """
    __tablename__ = 'user_relation'

    class RELATION:
        DELETE = 0
        # 代表关注
        FOLLOW = 1
        # 拉黑
        BLACKLIST = 2

    id = db.Column('relation_id', db.Integer, primary_key=True, doc='主键ID')
    user_id = db.Column(db.Integer, doc='用户ID')
    target_user_id = db.Column(db.Integer, doc='目标用户ID')
    relation = db.Column(db.Integer, doc='关系')
    ctime = db.Column('create_time', db.DateTime, default=datetime.now, doc='创建时间')
    utime = db.Column('update_time', db.DateTime, default=datetime.now, onupdate=datetime.now, doc='更新时间')

if __name__ == '__main__':
    # 1.查询手机号码为18516952650的用户
    # 方式一
    # user = User.query.options(load_only(User.id, User.name, User.mobile)).filter(User.mobile == "18516952650").first()
    # print(user)
    # 方式二
    # user = User.query.filter_by(mobile="18516952650").first()
    # print(user)
    # 2.查询手机号以13开始的, 并且发布文章的数量大于10篇的用户
    # users = User.query.filter(and_(User.mobile.startswith("13"), User.article_count > 10)).all()
    # for user in users:
    #     print(user)
    # 3.查询手机号以13结尾的, 或者发布文章的数量大于10篇的用户
    # users = User.query.filter(or_(User.mobile.endswith("13"), User.article_count > 10)).all()
    # for user in users:
    #     print(user)
    # 4.查询手机号不是以13结尾的用户
    # users = User.query.filter(not_(User.mobile.endswith("13"))).all()
    # for user in users:
    #     print(user)
    # 5.查询手机号不是以13结尾的用户, 获取前面的第3条开始的5条数据
    # users = User.query.filter(not_(User.mobile.endswith("13"))).offset(2).limit(5)
    # for user in users:
    #     print(user.id, user.name, user.mobile)
    # 6.查询手机号不是以13结尾的用户, 以发布文章数量排序(正 / 降), 获取前面的第3条开始的5条数据
    # users = User.query.filter(not_(User.mobile.endswith("13"))).order_by(User.article_count).offset(2).limit(5)
    # for user in users:
    #     print(user.id, user.name, user.mobile, user.article_count)
    # 7.查询关系表, 以user_id分组, 查出每个用户当前的关注数量.
    relations = db.session.query(Relation.user_id, func.count(Relation.target_user_id)).\
        filter(Relation.relation==1).group_by(Relation.user_id).all()
    for relation in relations:
        print("id:{}, conut:{}".format(relation[0], relation[1]))
关联查询
  1. 原生写法:
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model):
    # 指定映射数据库中的哪张表
    __tablename__ = "user_basic"

    # 此类是业务自己用的,跟SQLAlchemy没关系
    class STATUS:
        ENABLE = 1
        DISABLE = 0

    #  db.Column的第一个参数映射对应的字段
    id = db.Column('user_id', db.Integer, primary_key=True, doc='用户ID')
    mobile = db.Column(db.String, doc='手机号')
     # 使用property属性,可以将方法当属性使用
    @property
    def followings(self):
        relations = Relation.query.filter(Relation.user_id == self.id).all()
        return relations

class Relation(db.Model):
    """
    用户关系表
    """
    __tablename__ = 'user_relation'

    class RELATION:
        DELETE = 0
        # 代表关注
        FOLLOW = 1
        # 拉黑
        BLACKLIST = 2

    id = db.Column('relation_id', db.Integer, primary_key=True, doc='主键ID')
    user_id = db.Column(db.Integer, doc='用户ID')
    target_user_id = db.Column(db.Integer, doc='目标用户ID')
    relation = db.Column(db.Integer, doc='关系')
    ctime = db.Column('create_time', db.DateTime, default=datetime.now, doc='创建时间')
    utime = db.Column('update_time', db.DateTime, default=datetime.now, onupdate=datetime.now, doc='更新时间')

使用查询:

from flask import Flask
from flask_foreign_key.config import Config
from flask_foreign_key.models import db, User, Relation

app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)

if __name__ == '__main__':
    with app.app_context():
        # 获取用户id为1的用户对象
        user = User.query.get(1)
        # 获取关注列表
        relations = user.followings
        print(relations)
        for relation in relations:
            print(relation.id, relation.user_id,relation.target_user_id, relation.relation)
使用ForeignKey
在模型中使用db.relationship创建关系属性
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model):
    # 指定映射数据库中的哪张表
    __tablename__ = "user_basic"

    # 此类是业务自己用的,跟SQLAlchemy没关系
    class STATUS:
        ENABLE = 1
        DISABLE = 0

    #  db.Column的第一个参数映射对应的字段
    id = db.Column('user_id', db.Integer, primary_key=True, doc='用户ID')
    mobile = db.Column(db.String, doc='手机号')
    # 1.使用db.relationship创建关系属性
    # 此属性只是为了方便查询,在数据库表中并没有对应的字段。
    # 参数: 关联的类名称(字符串的形式)
    followings = db.relationship('Relation', foreign_keys='Relation.user_id')

class Relation(db.Model):
    """
    用户关系表
    """
    __tablename__ = 'user_relation'

    class RELATION:
        DELETE = 0
        # 代表关注
        FOLLOW = 1
        # 拉黑
        BLACKLIST = 2

    id = db.Column('relation_id', db.Integer, primary_key=True, doc='主键ID')
    # 2.1.使用类.属性
    # user_id = db.Column(db.Integer, db.ForeignKey(User.id), doc='用户ID')
    # 2.2.使用表.字段名
    user_id = db.Column(db.Integer, db.ForeignKey('user_basic.user_id'), doc='用户ID')
    target_user_id = db.Column(db.Integer, doc='目标用户ID')
    relation = db.Column(db.Integer, doc='关系')
    ctime = db.Column('create_time', db.DateTime, default=datetime.now, doc='创建时间')
    utime = db.Column('update_time', db.DateTime, default=datetime.now, onupdate=datetime.now, doc='更新时间')

使用外键查询

from flask import Flask

from flask_foreign_key.config import Config
from flask_foreign_key.models import db, User

app = Flask(__name__)
app.config.from_object(Config)

db.init_app(app)

if __name__ == '__main__':
    with app.app_context():
        # 获取用户id为1的用户对象
        user = User.query.get(1)
        # 获取关注列表
        print(user.followings)
        for relation in relations:
            print(relation.id, relation.user_id,relation.target_user_id, relation.relation)
使用primaryjoin
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model):
    # 指定映射数据库中的哪张表
    __tablename__ = "user_basic"

    # 此类是业务自己用的,跟SQLAlchemy没关系
    class STATUS:
        ENABLE = 1
        DISABLE = 0

    #  db.Column的第一个参数映射对应的字段
    id = db.Column('user_id', db.Integer, primary_key=True, doc='用户ID')
    mobile = db.Column(db.String, doc='手机号')

    # 使用primaryjoin
    fans = db.relationship("Relation", primaryjoin="Relation.target_user_id==foreign(User.id)")

class Relation(db.Model):
    """
    用户关系表
    """
    __tablename__ = 'user_relation'

    class RELATION:
        DELETE = 0
        # 代表关注
        FOLLOW = 1
        # 拉黑
        BLACKLIST = 2

    id = db.Column('relation_id', db.Integer, primary_key=True, doc='主键ID')
    user_id = db.Column(db.Integer, doc='用户ID')
    target_user_id = db.Column(db.Integer, doc='目标用户ID')
    relation = db.Column(db.Integer, doc='关系')
    ctime = db.Column('create_time', db.DateTime, default=datetime.now, doc='创建时间')
    utime = db.Column('update_time', db.DateTime, default=datetime.now, onupdate=datetime.now, doc='更新时间')
from flask import Flask

from flask_foreign_key.config import Config
from flask_foreign_key.models import db, User

app = Flask(__name__)
app.config.from_object(Config)

db.init_app(app)

if __name__ == '__main__':
    with app.app_context():
        # 获取用户id为1的用户对象
        user = User.query.get(1)
        # 获取粉丝列表
        fans = user.fans
        print(fans)
使用primaryjoin,代码看起来更加直观,所以建议使用primaryjoin
join查询的使用
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model):
    # 指定映射数据库中的哪张表
    __tablename__ = "user_basic"

    # 此类是业务自己用的,跟SQLAlchemy没关系
    class STATUS:
        ENABLE = 1
        DISABLE = 0

    #  db.Column的第一个参数映射对应的字段
    id = db.Column('user_id', db.Integer, primary_key=True, doc='用户ID')
    mobile = db.Column(db.String, doc='手机号')

class Relation(db.Model):
    """
    用户关系表
    """
    __tablename__ = 'user_relation'

    class RELATION:
        DELETE = 0
        # 代表关注
        FOLLOW = 1
        # 拉黑
        BLACKLIST = 2

    id = db.Column('relation_id', db.Integer, primary_key=True, doc='主键ID')
    user_id = db.Column(db.Integer, doc='用户ID')
    target_user_id = db.Column(db.Integer, doc='目标用户ID')
     # 方法四:指定字段
    target_user = db.relationship('User', primaryjoin='Relation.target_user_id==foreign(User.id)', uselist=False)
    relation = db.Column(db.Integer, doc='关系')
    ctime = db.Column('create_time', db.DateTime, default=datetime.now, doc='创建时间')
    utime = db.Column('update_time', db.DateTime, default=datetime.now, onupdate=datetime.now, doc='更新时间')

join查询的使用:

from flask import Flask

from flask_foreign_key.config import Config
from flask_foreign_key.models import db, User, Relation
from sqlalchemy.orm import load_only, contains_eager

app = Flask(__name__)
app.config.from_object(Config)

db.init_app(app)

if __name__ == '__main__':
    with app.app_context():
        fans = Relation.query.join(Relation.target_user).
        options(load_only(Relation.target_user_id),
                contains_eager(Relation.target_user).load_only(User.name)).all()
        print(fans)

小结:
因为使用子查询,会生成多条sql语句进行查询,效率低下。所以我们要使用inner join提高查询效率。

面试问题:
有没有使用过sqlalchemy的一些高级用法,解决性能问题?
上面的使用就是答案。

relathionship其他参数说明
uselist: 是否返回对象列表, True 返回对象列表 False返回第一个对象
lazy='dynamic' 惰性查询,返回一个query对象,可以进一步过滤
lazy="dynamic"只可以用在一对多和多对多关系中,不可以用在一对一和多对一中
小结:

在sqlalchemy设置的外键属性跟数据库表中的外键没有任何关系。

更新
  • 方式一
user = User.query.get(1)
  user.name = 'Python'
  db.session.add(user)
  db.session.commit()
  • 方式二
User.query.filter_by(id=1).update({'name':'python'})
  db.session.commit()
删除
  • 方式一
user = User.query.order_by(User.id.desc()).first()
  db.session.delete(user)
  db.session.commit()
  • 方式二
User.query.filter(User.mobile='18512345678').delete()
  db.session.commit()
事务
  • flask的sqlalchmey自动开启事务
  • 查询的时候,自动开启和回滚。
  • db.session.commit() 提交事务
  • db.session.rollback() 回滚
  • 事务是在请求上下文中开启。
environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'}
with app.request_context(environ):
    try:
        user = User(mobile='13021074747', name='张三')
        db.session.add(user)
        db.session.flush() # 将db.session记录的sql传到数据库中执行
        profile = UserProfile(id=user.id)
        db.session.add(profile)
        db.session.commit()
    except:
        db.session.rollback()