【补充】sqlalchemy介绍和快速使用

orm 框架引入

  • django orm--》只能用在django中,不能独立使用

[1]Python界的orm框架

  • peewee
  • sqlalchemy:企业级
  • djagno rom
  • Tortoise ORM
  • GINO

[2]Go界orm框架

  • gorm 国人写的
  • Xorm

[3]Java界orm框架

  • ssh 框架springmvc structs Hibernate(java的orm框架)
  • ssh spring springmvc Hibernate
  • ssm Spring SpringMVC MyBatis (orm框架)
  • springboot :sb框架 ---》java工程师就是spring工程师
  • spring cloud

【一】ORM框架引入

  • ORM (Object-Relational Mapping) 是一种编程技术,用于在关系型数据库和面向对象编程语言之间建立映射关系,将数据库中的表、记录等转化为对象,并使开发人员可以通过操作对象的方式来操作数据库。以下是一些常见的ORM框架。

  • Django ORM: Django是一个功能强大的Python Web框架,其中包含了自己的ORM,但它只能在Django项目中使用。可以使用Django ORM来定义模型类,然后使用该类进行数据库查询和操作。

【二】Python界的ORM框架

【1】Peewee

  • Peewee是一个简单而轻量级的Python ORM框架,具有简单直观的API和丰富的功能。
  • 示例代码:
    from peewee import *
    
    db = SqliteDatabase('my_app.db')
    
    class User(Model):
        username = CharField()
        password = CharField()
    
        class Meta:
            database = db
    
    # 创建表格
    User.create_table()
    # 插入数据
    user = User(username='john', password='secret')
    user.save()

【2】SQLAlchemy

  • SQLAlchemy是一个功能强大且广泛使用的Python ORM框架,适用于企业级应用程序。
  • 示例代码:
    from sqlalchemy import create_engine, Column, Integer, String
    from sqlalchemy.orm import sessionmaker
    from sqlalchemy.ext.declarative import declarative_base
    
    engine = create_engine('sqlite:///my_app.db')
    Session = sessionmaker(bind=engine)
    session = Session()
    Base = declarative_base()
    
    class User(Base):
        __tablename__ = 'users'
        id = Column(Integer, primary_key=True)
        username = Column(String)
        password = Column(String)
    
    Base.metadata.create_all(engine)  # 创建表格
    
    # 插入数据
    user = User(username='john', password='secret')
    session.add(user)
    session.commit()

【4】Django ORM

  • Django的ORM提供了丰富的功能,可与Django Web框架紧密集成,用于查询和操作数据库。

  • 示例代码:

    from django.db import models
    
    class User(models.Model):
        username = models.CharField(max_length=100)
        password = models.CharField(max_length=100)
    
    # 插入数据
    user = User(username='john', password='secret')
    user.save()

【5】Tortoise ORM

  • Tortoise ORM是一个异步Python ORM框架,具有简单易用的API,并支持多种异步IO库。
  • 示例代码:
    from tortoise import fields, models
    
    class User(models.Model):
        username = fields.CharField(max_length=100)
        password = fields.CharField(max_length=100)
    
    # 插入数据
    user = User(username='john', password='secret')
    await user.save()

【6】GINO

  • GINO是一个异步Python ORM框架,基于SQLAlchemy构建,专注于与异步IO库的兼容性和性能优化。
  • 示例代码:
    from sqlalchemy import Column, String
    from gino import Gino
    
    db = Gino()
    
    class User(db.Model):
        __tablename__ = 'users'
        username = Column(String)
        password = Column(String)
    
    # 插入数据
    await db.gino.create_all()  # 创建表格
    user = await User.create(username='john', password='secret')

【三】Go界ORM框架

【1】GORM

  • GORM是一个功能强大且易用的Go ORM框架,由国人编写,支持多种数据库。
  • 示例代码:
    package main
    
    import (
        "gorm.io/driver/mysql"
        "gorm.io/gorm"
    )
    
    type User struct {
        ID       uint
        Username string
        Password string
    }
    
    func main() {
        dsn := "username:password@tcp(127.0.0.1:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local"
        db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
        if err != nil {
            panic("failed to connect database")
        }
        db.AutoMigrate(&User{})  // 创建表格
    
        // 插入数据
        user := User{Username: "john", Password: "secret"}
        db.Create(&user)
    }

【2】Xorm

  • Xorm是Go语言中另一个流行的ORM框架,拥有丰富的特性,并支持多种数据库。
  • 示例代码:
    package main
    
    import (
        "github.com/go-xorm/xorm"
        _ "github.com/go-sql-driver/mysql"
    )
    
    type User struct {
        ID       int64
        Username string
        Password string
    }
    
    func main() {
        engine, err := xorm.NewEngine("mysql", "username:password@tcp(127.0.0.1:3306)/db_name")
        if err != nil {
            panic("failed to connect database")
        }
        engine.Sync2(&User{})  // 创建表格
    
        // 插入数据
        user := User{Username: "john", Password: "secret"}
        engine.Insert(&user)
    }

【四】Java界ORM框架

【1】Hibernate

  • Hibernate是Java语言中最常用的ORM框架之一,通过映射配置文件或注解来建立对象和数据库表之间的映射关系。
  • 示例代码:
    import javax.persistence.*;
    
    @Entity
    @Table(name = "users")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column
        private String username;
    
        @Column
        private String password;
    
        // Getters and Setters
    }
    
    // 插入数据
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("my_app");
    EntityManager em = emf.createEntityManager();
    User user = new User();
    user.setUsername("john");
    user.setPassword("secret");
    em.getTransaction().begin();
    em.persist(user);
    em.getTransaction().commit();

【2】MyBatis

  • MyBatis是一种半自动ORM框架,将SQL语句与Java方法进行映射,提供了灵活的查询和操作数据库的方式。
  • 示例代码:
    import org.apache.ibatis.annotations.*;
    
    @Mapper
    public interface UserMapper {
    
        @Insert("INSERT INTO users(username, password) VALUES(#{username}, #{password})")
        void insertUser(User user);
    }
    
    // 插入数据
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession session = sqlSessionFactory.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = new User();
    user.setUsername("john");
    user.setPassword("secret");
    mapper.insertUser(user);
    session.commit();
    session.close();

【3】Spring Data JPA

  • Spring Data JPA是基于JPA(Java Persistence API)标准的ORM框架,通过继承或声明接口的方式,提供了简化数据库访问的方法。
  • 示例代码:
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface UserRepository extends JpaRepository<User, Long> {
    }
    
    // 插入数据
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    UserRepository repository = context.getBean(UserRepository.class);
    User user = new User();
    user.setUsername("john");
    user.setPassword("secret");
    repository.save(user);

【4】Spring Boot + Spring Data JPA

  • Spring Boot是一个快速开发的框架,可轻松集成Spring Data JPA进行ORM操作。
  • 示例代码:
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.SpringApplication;
    import org.springframework.beans.factory.annotation.Autowired;
    
    @Entity
    @Table(name = "users")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column
        private String username;
    
        @Column
        private String password;
    
        // Getters and Setters
    }
    
    public interface UserRepository extends JpaRepository<User, Long> {
    }
    
    @SpringBootApplication
    public class Application implements CommandLineRunner {
    
        @Autowired
        private UserRepository repository;
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
        public void run(String... args) throws Exception {
            User user = new User();
            user.setUsername("john");
            user.setPassword("secret");
            repository.save(user);
        }
    }

sqlalchemy框架深入学习

【一】介绍

  • SQLAlchemy是一个基于Python实现的ORM框架。
  • 该框架建立在 DB API之上,使用关系对象映射进行数据库操作
  • 简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。
pip3 install sqlalchemy

【二】组成部分

  • Engine,框架的引擎
  • Connection Pooling ,数据库连接池
  • Dialect,选择连接数据库的DB API种类(sqlite,mysql...)
  • Schema/Types,架构和类型
  • SQL Exprression Language,SQL表达式语言
  • SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

【三】操作不同数据库

MySQL-Python
    mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
    
pymysql
    mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
    
MySQL-Connector
    mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
    
cx_Oracle
    oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]

orm不能创建数据库---》只能创建表,删除表---》sqlalchemy不能增加删除字段--》借助于第三方插件实现

【四】简单使用(能创建表,删除表,不能修改表)

  • 修改表:在数据库添加字段,类对应上

  • 执行原生sql(不常用)

import time
import threading
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine

# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
def task(arg):
    # 第二步:通过engine获得链接
    conn = engine.raw_connection()
    cursor = conn.cursor()
    cursor.execute(
        "select * from app01_book"
    )
    result = cursor.fetchall()
    print(result)
    cursor.close()
    conn.close()

for i in range(20):
    t = threading.Thread(target=task, args=(i,))
    t.start()

【五】orm进阶使用

  • models.py
import datetime
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
Base = declarative_base()

class Users(Base):
    __tablename__ = 'users'  # 数据库表名称
    id = Column(Integer, primary_key=True)  # id 主键
    name = Column(String(32), index=True, nullable=False)  # name列,索引,不可为空
    # email = Column(String(32), unique=True)
    #datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
    # ctime = Column(DateTime, default=datetime.datetime.now)
    # extra = Column(Text, nullable=True)

    __table_args__ = (
        # UniqueConstraint('id', 'name', name='uix_id_name'), #联合唯一
        # Index('ix_id_name', 'name', 'email'), #索引
    )

def init_db():
    """
    根据类创建数据库表
    :return:
    """
    engine = create_engine(
        "mysql+pymysql://root:123456@127.0.0.1:3306/aaa?charset=utf8",
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )

    Base.metadata.create_all(engine)

def drop_db():
    """
    根据类删除数据库表
    :return:
    """
    engine = create_engine(
        "mysql+pymysql://root:123456@127.0.0.1:3306/aaa?charset=utf8",
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )

    Base.metadata.drop_all(engine)

if __name__ == '__main__':
    # drop_db()
    init_db()
  • app.py
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import Users
#"mysql+pymysql://root@127.0.0.1:3306/aaa"
engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)
Connection = sessionmaker(bind=engine)

# 每次执行数据库操作时,都需要创建一个Connection
con = Connection()

# ############# 执行ORM操作 #############
obj1 = Users(name="lqz")
con.add(obj1)
# 提交事务
con.commit()

# 关闭session,其实是将连接放回连接池
con.close()

【六】sqlalchemy

  • 创建模型表
    • models.py
import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, DateTime, UniqueConstraint, Index

# 1 sqlalchemy,原生操作sql
# 2 sqlalchemy创建表删除表
Base = declarative_base()


# print(type(Base))
class User(Base):
    # 以__开头的是配置
    __tablename__ = 'users'  # 数据库表名称,如果不写,以类名作为表名

    id = Column(Integer, primary_key=True)  # 主键索引,聚簇索引
    name = Column(String(64), index=True, nullable=False)  # name字段加辅助索引
    email = Column(String(32), unique=True)
    # datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
    ctime = Column(DateTime, default=datetime.datetime.now())
    extra = Column(Text, nullable=True)

    __table_args__ = (
        UniqueConstraint('id', 'name', name='uix_id_name'),  # 联合唯一
        Index('ix_id_name', 'name', 'email'),  # 索引
    )
  • 创建表
from sqlalchemy import create_engine

# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:1314521@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

# 创建表,删除表
# 1 在数据库中创建表
Base.metadata.create_all(engine)

# 2 删除表
# Base.metadata.drop_all(engine)
  • 存储数据(基于session)
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base, User
from sqlalchemy.orm import sessionmaker

# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:1314521@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

# 创建表,删除表
# (1)在数据库中创建表
# Base.metadata.create_all(engine)

# (2)删除表
# Base.metadata.drop_all(engine)

# 操作表中得数据
# 第二步:得到一个session对象---》不是flask的session---》会话---》链接
# 把引擎传入
Session = sessionmaker(bind=engine)
# 得到session对象
session = Session()

# 第三步:使用session对象操作数据
# 创建对象
user = User(name='dream', email='123@qq.com')
# 保存到数据库:将创建的对象交给 session会话
session.add(user)
# 提交事务
session.commit()
# 关闭会话
session.close()
  • 查看数据
mysql> use db001;
Database changed
mysql> show tables;
+-----------------+
| Tables_in_db001 |
+-----------------+
| users           |
+-----------------+
1 row in set (0.00 sec)

mysql> select * from users;
+----+-------+------------+---------------------+-------+
| id | name  | email      | ctime               | extra |
+----+-------+------------+---------------------+-------+
|  1 | dream | 123@qq.com | 2023-08-26 14:52:13 | NULL  |
+----+-------+------------+---------------------+-------+
1 row in set (0.00 sec)

【七】scoped_session线程安全

  • 如果集成到flask中,session会话是要做成全局,还是每个视图函数有自己的一个
    • 应该做成,每个视图函数,都新创建一个session对象
  • 这样每次都要加括号得到session对象
  • scoped_session 全局只有一个session对象,在不同视图函数就用这一个---》保证线程安全
    • 做成了每个线程自己一个单独的session对象

【1】scoped_session 使用

import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base, User
from sqlalchemy.orm import sessionmaker, scoped_session

# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:1314521@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

# 创建表,删除表
# (1)在数据库中创建表
# Base.metadata.create_all(engine)

# (2)删除表
# Base.metadata.drop_all(engine)

# 操作表中得数据
# 第二步:得到一个session对象---》不是flask的session---》会话---》链接
# 把引擎传入
Session = sessionmaker(bind=engine)

# 得到session对象
session = Session()
print(type(session)) # <class 'sqlalchemy.orm.session.Session'>

# 使用 scoped_session 将 原来的 Session包一下, 这个session全局用一个即可
# :sqlalchemy.orm.scoping.scoped_session
session = scoped_session(Session)
print(type(session)) # <class 'sqlalchemy.orm.scoping.scoped_session'>

# 第三步:使用session对象操作数据
# 创建对象
user = User(name='dream', email='456@qq.com')
# 保存到数据库:将创建的对象交给 session会话
session.add(user)
# 提交事务
session.commit()
# 关闭会话
session.close()
  • 查看数据
mysql> select * from users;
+----+-------+------------+---------------------+-------+
| id | name  | email      | ctime               | extra |
+----+-------+------------+---------------------+-------+
|  1 | dream | 123@qq.com | 2023-08-26 14:52:13 | NULL  |
|  2 | dream | 456@qq.com | 2023-08-26 14:57:55 | NULL  |
+----+-------+------------+---------------------+-------+
2 rows in set (0.00 sec)

【2】scoped_session 源码分析

  • scoped_session是SQLAlchemy中用于实现线程范围的会话对象的类。
  • 它通过为每个线程创建独立的会话来提供线程安全的数据库访问。
from sqlalchemy.orm.scoping import scoped_session
  • scoped_session类没有add方法,但是可以通过add方法添加数据。
    • 这是因为在scoped_session类上加了一个装饰器 @create_proxy_methods
    • 装饰器的作用是为scoped_session类添加一组代理方法和属性,这些方法和属性都是通过委派给底层的Session对象实现的。
    • 装饰器接收一个参数列表,指定了要为scoped_session类添加的方法和属性。
    • 这些方法包括addquerycommit等常用的数据库操作方法。
@create_proxy_methods(
    Session,
    ":class:`_orm.Session`",
    ":class:`_orm.scoping.scoped_session`",
    classmethods=["close_all", "object_session", "identity_key"],
    methods=[
        "__contains__",
        "__iter__",
        "add",
        "add_all",
        "begin",
        "begin_nested",
        "close",
        "commit",
        "connection",
        "delete",
        "execute",
        "expire",
        "expire_all",
        "expunge",
        "expunge_all",
        "flush",
        "get",
        "get_bind",
        "is_modified",
        "bulk_save_objects",
        "bulk_insert_mappings",
        "bulk_update_mappings",
        "merge",
        "query",
        "refresh",
        "rollback",
        "scalar",
        "scalars",
    ],
    attributes=[
        "bind",
        "dirty",
        "deleted",
        "new",
        "identity_map",
        "is_active",
        "autoflush",
        "no_autoflush",
        "info",
    ],
)
class scoped_session(Generic[_S]):
  • 补充:类装饰器本质
###1  加在类上的装饰器
def auth(func):
    def inner(*args,**kwargs):
        res=func(*args,**kwargs)
        res.add='999'
        return res
    return inner

@auth  # Person=auth(Person)
class Person():
    pass


# 执行:Person()----->在执行---》inner
p=Person()  # inner()  inner的返回值给了p
print(p.add)


# 2 类作为装饰器
  • 每个线程自己的一个session对象
    • scoped_session类的构造函数有两个参数:
      • session_factoryscopefunc
    • 其中,session_factory是一个sessionmaker对象,用于创建新的Session对象。
    • scopefunc是一个可选的函数,用于确定会话对象的作用域。
    • 我们只传入了与一个 session_factory
    • 而 scopefunc 是None
def __init__(
    self,
    session_factory: sessionmaker[_S],
    scopefunc: Optional[Callable[[], Any]] = None,
):
    self.session_factory = session_factory

    if scopefunc:
        self.registry = ScopedRegistry(session_factory, scopefunc)
    else:
        self.registry = ThreadLocalRegistry(session_factory)
  • 如果scopefunc参数为None,则会执行self.registry = ThreadLocalRegistry(session_factory),创建一个ThreadLocalRegistry对象来管理会话对象。
    • 它通过Python标准库中的threading.local()实现线程范围的数据存储。
    • ThreadLocalRegistry类的构造函数接收一个createfunc参数,该参数是一个可调用对象,用于创建新的会话对象。
    • ThreadLocalRegistry内部使用了threading.local()来维护每个线程独立的会话对象。
    • 每个线程通过调用createfunc获取自己的会话对象,并将其保存在threading.local()对象中供后续使用。
def __init__(self, createfunc: Callable[[], _T]):
    self.createfunc = createfunc
    self.registry = threading.local()


def __call__(self) -> _T:
    try:
        return self.registry.value  # type: ignore[no-any-return]
    except AttributeError:
        # self.createfunc() ---- > Session() --- >真正的session对象
        # value 是 session 会话对象,放在了 registry 里面
        # threading.local() --- > 每个线程单独用自己的 
        val = self.registry.value = self.createfunc()
        return val  # type: ignore[no-any-return]
  • threading.local()

    • 多线程并发操作,不需要加锁,不会出现并发安全问题,每个线程用的都是自己的那个数据

    • 核心原理是:通过线程id号做个区分

      -线程1 t.a=88  内部 ---》{线程id号:{a:88}}
      -线程1 t.a=77  内部 ---》{线程id号:{a:88},线程id号:{a:77}}
    • 不同线程用的都是 t 对象,threading.local(),但是每个线程都是用自己的

      线程1   t.a=100  --->在当前线程中  print(t.a)   ---》100
      线程2   t.a=99  --->在当前线程中  print(t.a)    ---》99
  • 小结

    • scoped_session的对象,就像使用Session的对象一样用(内部用了装饰器修饰)
    • scoped_session 是线程安全的,每个线程自己的一个session对象
    • t=threading.local(),多线程并发操作,不需要加锁,不会出现并发安全问题,保证了每个线程用的都是自己的那个数据

【八】基于 scoped_session 增删查改数据

【0】基础数据

  • models.py
import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, DateTime, UniqueConstraint, Index

# 1 sqlalchemy,原生操作sql
# 2 sqlalchemy创建表删除表
Base = declarative_base()


# print(type(Base))
class User(Base):
    # 以__开头的是配置
    __tablename__ = 'users'  # 数据库表名称,如果不写,以类名作为表名

    id = Column(Integer, primary_key=True)  # 主键索引,聚簇索引
    name = Column(String(64), index=True, nullable=False)  # name字段加辅助索引
    email = Column(String(32), unique=True)
    # datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
    ctime = Column(DateTime, default=datetime.datetime.now())
    extra = Column(Text, nullable=True)

    __table_args__ = (
        UniqueConstraint('id', 'name', name='uix_id_name'),  # 联合唯一
        Index('ix_id_name', 'name', 'email'),  # 索引
    )
  • main.py
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base, User
from sqlalchemy.orm import sessionmaker, scoped_session

# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:1314521@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

Session = sessionmaker(bind=engine)
session = scoped_session(Session)

# 创建表,删除表
#1 在数据库中创建表
# Base.metadata.create_all(engine)

# 2 删除表
# Base.metadata.drop_all(engine)



# ----------- 最后要提价事务,关闭链接 -----------
# 提交事务
session.commit()
# 关闭会话
session.close()

【1】添加数据

(1)add

# (1)增加数据
# (1.1) add
session.add(User(name="dream", email="555"))
mysql> select * from users;
+----+-------+------------+---------------------+-------+
| id | name  | email      | ctime               | extra |
+----+-------+------------+---------------------+-------+
|  1 | dream | 123@qq.com | 2023-08-26 14:52:13 | NULL  |
|  2 | dream | 456@qq.com | 2023-08-26 14:57:55 | NULL  |
|  3 | dream | 789@qq.com | 2023-08-26 14:59:15 | NULL  |
|  4 | dream | 555        | 2023-08-26 15:35:50 | NULL  |
+----+-------+------------+---------------------+-------+
4 rows in set (0.00 sec)

(2)add_all

# (1)增加数据
# (1.2) add_all
user1 = User(name="dream_1", email="111")
user2 = User(name="dream_2", email="222")
# add_all 参数为可迭代对象,内部是 for 循环
session.add_all([user1, user2])
mysql> select * from users;
+----+---------+------------+---------------------+-------+
| id | name    | email      | ctime               | extra |
+----+---------+------------+---------------------+-------+
|  1 | dream   | 123@qq.com | 2023-08-26 14:52:13 | NULL  |
|  2 | dream   | 456@qq.com | 2023-08-26 14:57:55 | NULL  |
|  3 | dream   | 789@qq.com | 2023-08-26 14:59:15 | NULL  |
|  4 | dream   | 555        | 2023-08-26 15:35:50 | NULL  |
|  5 | dream_1 | 111        | 2023-08-26 15:36:36 | NULL  |
|  6 | dream_2 | 222        | 2023-08-26 15:36:36 | NULL  |
+----+---------+------------+---------------------+-------+
6 rows in set (0.00 sec)

【2】查询数据

(1)filter

#  (2) 查找数据
# (2.1) filter :传表达式
res = session.query(User).filter(User.id > 2).all()
print(res)
[<models.User object at 0x00000131B50C6AF0>, <models.User object at 0x00000131B50C6A60>, <models.User object at 0x00000131B50C6B80>, <models.User object at 0x00000131B50C6BE0>]
  • 解决办法
    • 重写模型表加入
# 查询多条数据不生效
# 只有打印对象的时候才会触发 __str__ 方法
# 当查询单条数据时,会触发  __str__ 方法
def __str__(self):
    return self.name

# 重写 __repr__ 方法,查到的数据返回的是 name 否则是 Object
# 当查询结果是多条数据时,会触发  __repr__ 方法
def __repr__(self):
    return self.name
# (2) 查找数据
# (2.1) filter :传表达式
# .all() : 查出来的数据就是一个列表,不是queryset对象
# .first() : 想查单条直接用 first 方法

res_all = session.query(User).filter(User.id > 2).all()
# 等价于 select * from User where User.id > 2 limit 1;
res_first = session.query(User).filter(User.id > 2).first()

# 在表达式中 == 才是 = 的效果
res_name_dream_all = session.query(User).filter(User.name == 'dream').all()
res_name_dream_first = session.query(User).filter(User.name == 'dream').first()

print('res_all :>>> ', res_all)
print('res_first :>>> ', res_first)
print('res_name_dream_all :>>> ', res_name_dream_all)
print('res_name_dream_first :>>> ', res_name_dream_first)
res_all :>>>  [dream, dream, dream_1, dream_2]
res_first :>>>  dream
res_name_dream_all :>>>  [dream, dream, dream, dream]
res_name_dream_first :>>>  dream

(2)filter by

# (2.2)filter by : 传参数
res_all = session.query(User).filter_by(id=2).all()
res_first = session.query(User).filter_by(id=2).first()
res_name_dream_all = session.query(User).filter_by(name='dream').all()
res_name_dream_first = session.query(User).filter_by(name='dream').first()

print('res_all :>>> ', res_all)
print('res_first :>>> ', res_first)
print('res_name_dream_all :>>> ', res_name_dream_all)
print('res_name_dream_first :>>> ', res_name_dream_first)
res_all :>>>  [dream]
res_first :>>>  dream
res_name_dream_all :>>>  [dream, dream, dream, dream]
res_name_dream_first :>>>  dream

【3】删除数据

# (3)删除数据
# delete * from User where id >=6 ;
res_del = session.query(User).filter(User.id >= 6).delete()
print(res_del)  # 结果为影响的行数
# 没有先查询出对象再删除对象的方法,只有查完就删的方法
user=session.query(User).filter(User.id == 5).first()
user.delete()  # 它没有单独删对象的

【4】修改数据

# (4)修改数据
# 先查出 数据 再 修改数据 , res 为影响的行数
res = session.query(User).filter(User.id > 0).update({"name": "dream"})
mysql> select * from users;
+----+-------+------------+---------------------+-------+
| id | name  | email      | ctime               | extra |
+----+-------+------------+---------------------+-------+
|  1 | dream | 123@qq.com | 2023-08-26 14:52:13 | NULL  |
|  2 | dream | 456@qq.com | 2023-08-26 14:57:55 | NULL  |
|  3 | dream | 789@qq.com | 2023-08-26 14:59:15 | NULL  |
|  4 | dream | 555        | 2023-08-26 15:35:50 | NULL  |
|  5 | dream | 111        | 2023-08-26 15:36:36 | NULL  |
+----+-------+------------+---------------------+-------+
5 rows in set (0.00 sec)
# 类似于django的F查询
# (1)字符串相加
session.query(User).filter(User.id > 0).update({User.name: User.name + "099"}, synchronize_session=False)
# (2)数字相加
session.query(User).filter(User.id > 0).update({"age": User.age + 1}, synchronize_session="evaluate")
# 查到单个对象,修改属性
# 再用add增加进去
# 修改数据成功   
# add 只要有id,就是修改
res = session.query(User).filter(User.id == 1).first()
# print(res)
res.name = 'yyyy'
session.add(res)
mysql> select * from users;
+----+-------+------------+---------------------+-------+
| id | name  | email      | ctime               | extra |
+----+-------+------------+---------------------+-------+
|  1 | yyyy  | 123@qq.com | 2023-08-26 14:52:13 | NULL  |
|  2 | dream | 456@qq.com | 2023-08-26 14:57:55 | NULL  |
|  3 | dream | 789@qq.com | 2023-08-26 14:59:15 | NULL  |
|  4 | dream | 555        | 2023-08-26 15:35:50 | NULL  |
|  5 | dream | 111        | 2023-08-26 15:36:36 | NULL  |
+----+-------+------------+---------------------+-------+
5 rows in set (0.00 sec)

【九】模型表的关系

  • 一对一
  • 一对多
  • 多对多
  • 都是外键关系
  • 一对一其实是 一对多 的一种特例

【1】一对多(一对一)

(1)表关系创建

import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, DateTime, UniqueConstraint, Index, ForeignKey
from sqlalchemy.orm import relationship


class Hobby(Base):
    __tablename__ = 'hobby'
    id = Column(Integer, primary_key=True)
    caption = Column(String(50), default='篮球')

    def __str__(self):
        return self.caption

    def __repr__(self):
        return self.caption


class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)  # 不会自动生成id
    name = Column(String(32), index=True, nullable=True)
    # hobby指的是tablename而不是类名
    # 一对多关系一旦确立,关联关系写在多的一方---》物理外键
    hobby_id = Column(Integer, ForeignKey("hobby.id"))

    # 跟数据库无关,不会新增字段,只用于快速链表操作
    # 类名,backref用于反向查询
    hobby = relationship('Hobby', backref='pers')  # 以后 person.hobby 就是hobby对象

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name

(2)创建表

import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base, User
from sqlalchemy.orm import sessionmaker, scoped_session

# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:1314521@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 创建表,删除表
# 1 在数据库中创建表
Base.metadata.create_all(engine)

# 2 删除表
# Base.metadata.drop_all(engine)


# ----------- 最后要提价事务,关闭链接 -----------
# 提交事务
session.commit()
# 关闭会话
session.close()
mysql> show tables;
+-----------------+
| Tables_in_db001 |
+-----------------+
| hobby           |
| person          |
| users           |
+-----------------+
3 rows in set (0.00 sec)

(3)操作数据

[1]添加数据
# (1) 添加数据
# 因为有外键约束,所以要先添加 hobby 再添加 person

# 方式一 :
session.add(Hobby(caption="音乐"))
# 已经创建好 数据 并且 明确知道外键 ID
session.add(Person(name="dream", hobby_id=1))
mysql> select * from person;
+----+-------+----------+
| id | name  | hobby_id |
+----+-------+----------+
|  1 | dream |        1 |
+----+-------+----------+
1 row in set (0.00 sec)

mysql> select * from hobby;  
+----+---------+
| id | caption |
+----+---------+
|  1 | 音乐    |
+----+---------+
1 row in set (0.00 sec)
# 方式二
# 外键可以是一个对象
# 可以先查存来再 hobby = 查出来的数据  (只创建 person)
# 也可以 直接在 hobby = 一个新的对象 (可以同时创建 person 和 hobby)
session.add(Person(name="hope", hobby=Hobby(caption="篮球")))
mysql> select * from person;
+----+-------+----------+
| id | name  | hobby_id |
+----+-------+----------+
|  1 | dream |        1 |
|  2 | hope  |        2 |
+----+-------+----------+
2 rows in set (0.00 sec)

mysql> select * from hobby;  
+----+---------+
| id | caption |
+----+---------+
|  1 | 音乐    |
|  2 | 篮球    |
+----+---------+
2 rows in set (0.00 sec)
[2]查询数据
# (2) 查询数据
# (2.1)通过 person 查询 hobby 正向查询
people = session.query(Person).filter_by(id=2).first()
print(f'people :>>> {people}  ')
print(f'hobby_id :>>> {people.hobby_id}')
print(f'hobby :>>> {people.hobby}')
print(f'hobby.caption :>>> {people.hobby.caption}')
people :>>> hope  
hobby_id :>>> 2
hobby :>>> 篮球
hobby.caption :>>> 篮球
# (2.2)通过 hobby 查询 person 反向查询
hobby = session.query(Hobby).filter_by(id=2).first()
# 所有喜欢 id=2的人
print(f'hobby :>>> {hobby}')
# 模型表中定义的 relationship 中的 backref='pers'
print(f'hobby.pers :>>> {hobby.pers}')
hobby :>>> 篮球
hobby.pers :>>> [hope]
[3]更新删除同上面

【2】多对多

(1)表关系创建

import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, DateTime, UniqueConstraint, Index, ForeignKey
from sqlalchemy.orm import relationship

# 多对多关系
# 中间表  手动创建
class Boy2Girl(Base):
    __tablename__ = 'boy2girl'
    id = Column(Integer, primary_key=True, autoincrement=True)
    girl_id = Column(Integer, ForeignKey('girl.id'))
    boy_id = Column(Integer, ForeignKey('boy.id'))


class Girl(Base):
    __tablename__ = 'girl'
    id = Column(Integer, primary_key=True)
    name = Column(String(64), unique=True, nullable=False)

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name


class Boy(Base):
    __tablename__ = 'boy'
    id = Column(Integer, primary_key=True, autoincrement=True) #autoincrement 默认就是true
    name = Column(String(64), unique=True, nullable=False)

    # 就是咱们之前的ManyToMany,不会在表中生成字段---》因为它是个表----》这个字段可以放在Girl表
    girls = relationship('Girl', secondary='boy2girl', backref='boys')

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name

(2)创建表

import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base, User, Hobby, Person
from sqlalchemy.orm import sessionmaker, scoped_session


# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:1314521@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 创建表,删除表
# 1 在数据库中创建表
Base.metadata.create_all(engine)

# 2 删除表
# Base.metadata.drop_all(engine)


# ----------- 最后要提价事务,关闭链接 -----------
# 提交事务
session.commit()
# 关闭会话
session.close()

(3)操作数据

[1]添加数据
# (1) 添加数据
# (1.1) 笨办法 添加数据
girl = Girl(name="John")
boy = Boy(name="Revin")
session.add_all([girl, boy])
# 操作中间表(纯手动操作中间表)
session.add(Boy2Girl(girl_id=1, boy_id=1))
mysql> select * from boy;
+----+-------+
| id | name  |
+----+-------+
|  1 | Revin |
+----+-------+
1 row in set (0.00 sec)

mysql> select * from girl; 
+----+------+
| id | name |
+----+------+
|  1 | John |
+----+------+
1 row in set (0.00 sec)

mysql> select * from boy2girl;
+----+---------+--------+
| id | girl_id | boy_id |
+----+---------+--------+
|  1 |       1 |      1 |
+----+---------+--------+
1 row in set (0.00 sec)
# (1.2) 使用 relationship
# 增加了一个 boy
boy = Boy(name="Dream")
# 增加了两个 girl
boy.girls = [Girl(name="Hope"), Girl(name="Fun")]
# 并且 在中间表中 给 boy 增加了两条记录
session.add(boy)
mysql> select * from boy;      
+----+-------+
| id | name  |
+----+-------+
|  2 | Dream |
|  1 | Revin |
+----+-------+
2 rows in set (0.00 sec)

mysql> select * from girl;   
+----+------+
| id | name |
+----+------+
|  3 | Fun  |
|  2 | Hope |
|  1 | John |
+----+------+
3 rows in set (0.00 sec)

mysql> select * from boy2girl;
+----+---------+--------+
| id | girl_id | boy_id |
+----+---------+--------+
|  1 |       1 |      1 |
|  2 |       2 |      2 |
|  3 |       3 |      2 |
+----+---------+--------+
3 rows in set (0.00 sec)
[2]查询数据
# (2)查询
# 基于对象的跨表查询
# 正向
boy = session.query(Boy).filter(Boy.id == 2).first()
print('boy.girls :>>> ',boy.girls)
boy.girls :>>>  [Hope, Fun]
# 反向
girl = session.query(Girl).filter(Girl.id == 2).first()
print('girl.boys :>>> ',girl.boys)
girl.boys :>>>  [Dream]

【十】更多查询方式

【1】查询所有记录并返回一个普通列表

res = session.query(User).all()
  • 这将查询User表的所有记录,并以普通列表形式返回结果。

【2】只查询某几个字段

res = session.query(User.name.label('xx'), User.email)
  • 这将查询User表的nameemail字段,并将name字段重命名为xx
  • 返回结果也是一个查询对象。

【3】使用filterfilter_by方法进行条件查询

res = session.query(User).filter(User.name == "dream").all()
res = session.query(User).filter(User.name != "dream").all()
res = session.query(User).filter(User.name != "dream", User.email == '3@qq.com').all()
  • 这些示例演示了如何使用filter方法根据条件筛选记录,例如等于、不等于等条件。

【4】在Django中使用Q对象的类似功能

res = session.query(User).filter_by(name='dream999').all()
res = session.query(User).filter_by(name='dream999',email='47@qq.com').all()
  • 这些示例演示了如何使用filter_by方法根据等于条件筛选记录。

【5】获取第一个记录

res = session.query(User).first()
  • 这将返回查询结果的第一个记录。

【6】使用占位符进行查询

res = session.query(User).filter(text("id<:value or name=:name")).params(value=20, name='dream').all()
  • 这个示例演示了如何使用占位符进行查询,其中:value:name是占位符,可以通过params方法传入具体的值。

【7】自定义查询

res=session.query(User).from_statement(text("SELECT * FROM users where email=:email")).params(email='3@qq.com').all()
  • 这个示例演示了如何执行自定义的SQL查询语句。

【8】高级查询

(1)查询满足条件 User.id > 1 且 User.name 等于 'dream999' 的所有结果

res = session.query(User).filter(User.id > 1, User.name == 'dream999').all()
  • 这个查询语句将返回所有 User 表中满足条件的用户记录,其中 id 大于 1 并且 name 等于 'dream999'。
  • 这个例子的用途可能是获取具有指定 id 和名称的特定用户的信息。

(2)查询满足条件 User.id 在 1 到 9 之间且 User.name 等于 'dream999' 的所有结果

res = session.query(User).filter(User.id.between(1, 9), User.name == 'dream999').all()
  • 这个查询语句将返回 User 表中 id 在 1 到 9 之间且 name 等于 'dream999' 的所有用户记录。
  • 这个例子可以用来获取在特定范围内有特定名称的用户记录。

(3)查询满足条件 User.id 在 1 到 9 之间的所有结果

res = session.query(User).filter(User.id.between(1, 9)).all()
  • 这个查询语句将返回 User 表中 id 在 1 到 9 之间的所有用户记录。
  • 这个例子可以用来获取在指定范围内的用户记录。

(4)查询满足条件 User.id 在 [1, 3, 4] 中的所有结果

res = session.query(User).filter(User.id.in_([1,3,4])).all()
  • 这个查询语句将返回 User 表中 id 在给定列表 [1, 3, 4] 中的所有用户记录。
  • 这个例子可以用来获取具有特定 id 的用户记录。

(5)查询满足条件 User.email 在 ['3@qq.com', 'r@qq.com'] 中的所有结果

res = session.query(User).filter(User.email.in_(['3@qq.com','r@qq.com'])).all()
  • 这个查询语句将返回 User 表中 email 在给定列表 ['3@qq.com', 'r@qq.com'] 中的所有用户记录。
  • 这个例子可以用来获取具有特定电子邮件地址的用户记录。

(6)查询不满足条件 User.id 不在 [1, 3, 4] 中的所有结果

res = session.query(User).filter(~User.id.in_([1,3,4])).all()
  • 这个查询语句将返回 User 表中 id 不在给定列表 [1, 3, 4] 中的所有用户记录。
  • 这个例子可以用来排除具有特定 id 的用户记录。

(7)查询不满足条件 User.id 不在满足条件 User.name 为 'dream' 的用户记录中的所有结果

res = session.query(User).filter(~User.id.in_(session.query(User.id).filter_by(name='dream'))).all()
  • 这个查询语句将返回 User 表中 id 不在满足条件 User.name 为 'dream' 的用户记录中的所有用户记录。
  • 这个例子可以用来排除特定条件下的用户记录。

(8)查询满足条件 User.id 大于等于 3 且 User.name 为 'dream999' 的所有结果

res = session.query(User).filter(and_(User.id >= 3, User.name == 'dream999')).all()
  • 这个查询语句将返回 User 表中 id 大于等于 3 且 name 为 'dream999' 的所有用户记录。
  • 这个例子可以用来获取同时满足多个条件的用户记录。

(9)查询满足条件 User.id 小于 3 且 User.name 为 'dream999' 的所有结果

res = session.query(User).filter(User.id < 3, User.name == 'dream999').all()
  • 这个查询语句将返回 User 表中 id 小于 3 且 name 为 'dream999' 的所有用户记录。
  • 这个例子可以用来获取同时满足多个条件的用户记录。

(10)查询满足条件 User.id 小于 2 或 User.name 为 'eric' 的所有结果

res = session.query(User).filter(or_(User.id < 2, User.name == 'eric')).all()
  • 这个查询语句将返回 User 表中 id 小于 2 或 name 为 'eric' 的所有用户记录。
  • 这个例子可以用来获取满足任一条件的用户记录。

(11)查询满足条件 User.id 小于 2,同时满足 User.name 为 'dream999' 且 User.id 大于 3,或 User.extra 不为空的所有结果

res = session.query(User).filter(or_(User.id < 2, and_(User.name == 'dream999', User.id > 3), User.extra != "")).all()
  • 这个查询语句将返回 User 表中满足以下任一条件的用户记录:id 小于 2,或者同时满足 name 为 'dream999' 且 id 大于 3,或者 User.extra 字段不为空。这个例子可以用来获取多条件联合查询时的用户记录。

(12)查询满足条件 User.email 包含 '@' 符号的所有结果

res = session.query(User).filter(User.email.like('%@%')).all()
  • 这个查询语句将返回 User 表中 email 字段包含 '@' 符号的所有用户记录。
  • 这个例子可以用来获取具有有效电子邮件地址的用户记录。

(13)查询不满足条件 User.name 以字母 'e' 开头的所有结果

res = session.query(User.id).filter(~User.name.like('e%'))
  • 这个查询语句将返回 User 表中 name 不以字母 'e' 开头的用户记录的 id 列表。
  • 这个例子可以用来查找不符合特定名字格式要求的用户。

(14)查询不满足条件 User.name 以字母 'e' 开头的所有结果

res = session.query(User).filter(~User.name.like('e%')).all()
  • 这个查询语句将返回 User 表中 name 不以字母 'e' 开头的所有用户记录。
  • 这个例子可以用来查找不符合特定名字格式要求的用户。

【9】分页

  • 分页是一种常见的数据处理方式,用于将大量数据分割为更小的部分进行展示或处理。

  • 对于SQLAlchemy而言,可以通过切片和限制查询结果来实现分页查询。

  • 对于给定的查询语句 session.query(User)[2*5:2*5+2],意味着一页显示2条数据,需要查询第5页的结果。

  • 其中,User 是要查询的实体类。解释如下:

  • [2*5:2*5+2] 表示从索引10(第5页的起始索引)开始,取2条数据,即第5页的数据。

【10】排序的查询

  • 下面是关于排序的查询操作,以及不同的排序方式和多条件排序的示例代码:

(1)根据 name 降序排列(从大到小)的示例代码

res = session.query(User).order_by(User.name.desc()).all()

(2)根据 email 降序排列(从大到小)的示例代码

res = session.query(User).order_by(User.email.desc()).all()

(3)根据 price 升序排列(从小到大)的示例代码

res = session.query(Book).order_by(Book.price.asc()).all()

(4)先根据 name 降序排列,再按照 id 升序排列的示例代码

res = session.query(User).order_by(User.name.desc(), User.id.asc())

【12】分组查询

  • 接下来是关于分组查询的操作,包括了5个常见聚合函数:

(1)分组后只查询分组字段和聚合函数结果(严格模式下,不能查询除此之外的字段)的示例代码

res = session.query(User).group_by(User.extra)

(2)分组后查询分组字段以及最大 idid 之和、最小 id 和平均 id 的示例代码

from sqlalchemy.sql import func

res = session.query(
    User.name,
    func.max(User.id),
    func.sum(User.id),
    func.min(User.id),
    func.avg(User.id)).group_by(User.name).all()

for item in res:
    print(item)
  • 在以上示例中,name 表示分组字段,max(id) 表示计算每组中的最大 idsum(id) 表示计算每组中 id 的总和,min(id) 表示计算每组中的最小 idavg(id) 表示计算每组中 id 的平均值。

(3)分组后使用 having 进行条件筛选(筛选条件为最大 id 大于2)的示例代码

from sqlalchemy.sql import func

res = session.query(
    User.name,
    func.max(User.id),
    func.sum(User.id),
    func.min(User.id)).group_by(User.name).having(func.max(User.id) > 2).all()

print(res)
  • 以上示例中,使用 having 条件筛选出最大 id 大于2的分组结果。

【十一】sqlalchemy执行原生sql

# 原生sql查询,查出的结果是对象
# 原生sql查询,查询结果列表套元组

from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import create_engine

engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/db001", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
#### 执行原生sql方式一:
# 查询方式一:
# cursor = session.execute('select * from users')
# result = cursor.fetchall()
# print(result) #列表套元组

# 添加
# cursor = session.execute('insert into users(name,email) values(:name,:email)',
#                          params={"name": 'dream', 'email': '3333@qq.com'})
# session.commit()
# print(cursor.lastrowid)


###执行原生sql方式二(以后都用session操作---》socpe_session线程安全)一般不用
# conn = engine.raw_connection()
# cursor = conn.cursor()
# cursor.execute(
#     "select * from app01_book"
# )
# result = cursor.fetchall()


# 执行原生sql方式三:
# res = session.query(User).from_statement(text("SELECT * FROM boy where name=:name")).params(name='dream').all()


session.close()

【补充】django中如何反向生成models

python manage.py inspectdb > app/models.py

【补充】原生sql(django-orm如何执行原生sql)

# 执行完的结果映射到对象中---》上面讲的  方式三:
from model import Book
books_obj_list = Book.objects.raw('select distinct id, book_name from test_book')
for book_obj in books_obj_list:
	print(book_obj.id, book_obj.book_name)


    
# 纯原生sql 
from django.db import connection
cur=connection.cursor() 
cur.execute('select distinct id, book_name from test_book')
print(cur.fetch_all())
cur.close()


with connection.cursor() as cur:
    cur.execute('select distinct id, book_name from test_book')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值