• 9-flask框架-关联查询


    一、常用的SQLAlchemy关系选项

    选项名说明
    backref 在关系的另一模型中添加反向引用,用于设置外键名称,在1查多的
    primary join 明确指定两个模型之间使用的连表条件
    lazy 指定如何加载关联模型数据的方式。参数值: select(立即加载,查询所有相关数据显示,相当于lazy=True) subquery(立即加载,但使用子查询) dynamic(不加载记录,但提供加载记录的查询对象)
    uselist 如果为False,不使用列表,而使用标量值。 一对一关系中,需要设置relationship中的uselist=Flase,其他数据库操作一样。
    secondary 指定多对多关系中关系表的名字。 多对多关系中,需建立关系表,设置 secondary=关系表
    secondary join 在SQLAlchemy中无法自行决定时,指定多对多关系中的二级连表条件

    1.1 三范式:逆范式

    第一范式: 即表的列的具有原子性,不可再分解,即列的信息,不能分解, 只有数据库是关系型数据库(mysql/oracle/db2/informix/sysbase/sql server),就自动的满足1NF

    第二范式: 表中的记录是唯一的, 就满足2NF, 通常我们设计一个主键来实现

    第三范式:数据不能冗余,把关联性不强的数据可以移除到另一个表中。使用外键进行管理。

    1对1:把主表的主键放到附加表中作为外键存在。
    1对多:把主表(1) 的主键放到附加表(多)作为外键存在。
    多对多:把主表(多)的主键和附加表的(多)主键,放到第三方表(关系表)中作为外键。

    二、模型之间的关联(一对一)

    2.1 模型关联用法

    关联属性定义在主模型中

    class Student(db.Model):
        """个人信息主表"""
        ....
        # 模型属性,不是数据库的字段,不会在数据表中出现,仅仅是SQLAlchemy为了方便开发者使用关联查询所提供的对象属性
        # 因为StudentInfo和Student是一对一的关系,所以uselist=False表示关联一个数据
        info = db.relationship("StudentInfo", uselist=False,backref="student")
    
    
    class StudentInfo(db.Model):
        """个人信息附加表"""
    
        # 外键,
        # 如果是一对一,则外键放在附加表对应的模型中
        # 如果是一对多,则外键放在多的表对象的模型中    
        user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id")

    关联属性定义在模型中外键模型中

    class Student(db.Model):
        """个人信息主表"""
        ....
      
        
    from sqlalchemy.orm import backref
    class StudentInfo(db.Model):
        """个人信息附加表"""
    
        # 外键,
        # 如果是一对一,则外键放在附加表对应的模型中
        # 如果是一对多,则外键放在多的表对象的模型中    
        user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id")
        # 因为StudentInfo和Student是一对一的关系,所以uselist=False表示关联一个数据
        student = db.relationship("Student", backref=backref("info",uselist=False))

    2.2 连表后的增改查

    关联属性定义在主模型中

      1 from flask import Flask,render_template,request
      2 from flask_sqlalchemy import SQLAlchemy
      3 
      4 app = Flask(__name__)
      5 
      6 class Config(object):
      7     DEBUG = True
      8     # 数据库连接配置
      9     # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
     10     SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
     11     # 动态追踪修改设置,如未设置只会提示警告
     12     SQLALCHEMY_TRACK_MODIFICATIONS = True
     13     # 查询时会显示原始SQL语句
     14     SQLALCHEMY_ECHO = True
     15 
     16 app.config.from_object(Config)
     17 
     18 
     19 """模型类定义"""
     20 db = SQLAlchemy(app=app)
     21 # 等同于
     22 # db = SQLAlchemy()
     23 # db.init_app(app)
     24 
     25 class Student(db.Model):
     26     """学生信息模型"""
     27     # 声明与当前模型绑定的数据表名称
     28     __tablename__ = "db_students"
     29     # 字段定义
     30     """
     31     create table db_student(
     32       id int primary key auto_increment comment="主键",
     33       name varchar(15) comment="姓名",
     34     )
     35     """
     36     id = db.Column(db.Integer, primary_key=True,comment="主键")
     37     name = db.Column(db.String(15), comment="姓名")
     38     age = db.Column(db.SmallInteger, comment="年龄")
     39     sex = db.Column(db.Boolean, default=True, comment="性别")
     40     email = db.Column(db.String(128), comment="邮箱地址")
     41     money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
     42     # 模型属性,不是数据库的字段,不会在数据表中出现,仅仅是SQLAlchemy为了方便开发者使用关联查询所提供的对象属性
     43     # info 可以代表与当前数据对应的外键模型对象
     44     info = db.relationship("StudentInfo", uselist=False,backref="student")
     45 
     46     def __repr__(self):
     47         return f"{self.name}<Student>"
     48 
     49     @classmethod
     50     def add(cls):
     51         student = cls(name="小明", sex=True, age=17, email="123456@qq.com", money=100)
     52         db.session.add(student)
     53         db.session.commit()
     54         return student
     55 
     56     @property
     57     def to_dict(self):
     58         """把对象转化成字典"""
     59         return {
     60             "id": self.id,
     61             "name": self.name,
     62             "age": self.age,
     63             "sex": self.sex,
     64             "email": self.email,
     65             "money": float("%.2f" % self.money),
     66         }
     67 
     68 class StudentInfo(db.Model):
     69     """学生信息附加表"""
     70     __tablename__ = "db_student_info"
     71     id = db.Column(db.Integer, primary_key=True, comment="主键")
     72     address = db.Column(db.String(500), nullable=True, comment="地址")
     73     qq_num  = db.Column(db.String(15), nullable=True, comment="QQ号")
     74     # 外键设置[默认创建数据库物理外键]
     75     user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id")
     76 
     77 @app.route("/")
     78 def index():
     79     """添加数据"""
     80     # 添加主表信息的时候通过关联属性db.relationship同步添加附件表信息
     81     # student = Student(
     82     #     name="xiaolan02",
     83     #     age=16,
     84     #     sex=False,
     85     #     money=10000,
     86     #     email="xiaolan02@qq.com",
     87     #     info = StudentInfo(address="北京市昌平区百沙路204号", qq_num="100861000")
     88     # )
     89     # db.session.add(student)
     90     # db.session.commit()
     91 
     92     # 添加附加表数据,通过关联属性中db.relationshop的backref同步添加主表数据
     93     # info = StudentInfo(
     94     #     address="北京市昌平区百沙路204号",
     95     #     qq_num="100861220",
     96     #     student = Student(
     97     #         name="xiaolan02",
     98     #         age=16,
     99     #         sex=False,
    100     #         money=10000,
    101     #         email="xiaolan02@qq.com",
    102     #     )
    103     # )
    104     #
    105     # db.session.add(info)
    106     # db.session.commit()
    107 
    108     """查询数据"""
    109     # 正向关联----> 从主模型查询外键模型
    110     # student = Student.query.get(1)
    111     # print(student.info) # <StudentInfo 1>
    112     # print(student.info.address) # 北京市昌平区百沙路204号
    113     #
    114     # # 反向关联----> 从外键模型查询主模型
    115     # student_info = StudentInfo.query.filter(StudentInfo.qq_num=="100861220").first()
    116     # print(student_info.student) #    xiaolan02<Student>
    117     # print(student_info.student.name) #  xiaolan02
    118     # print(student_info.user_id) # 2     仅仅获取了外键真实数据
    119 
    120     """修改数据"""
    121     # 通过主表使用关联属性可以修改附加表的数据
    122     student = Student.query.get(2)
    123     student.info.address = "广州市天河区天河东路103号"
    124     db.session.commit()
    125 
    126     # 也可以通过附加表模型直接修改主表的数据
    127     student_info = StudentInfo.query.filter(StudentInfo.qq_num == "100861220").first()
    128     print(student_info.student)
    129     student_info.student.age = 22
    130     db.session.commit()
    131 
    132     return "ok"
    133 
    134 if __name__ == '__main__':
    135     with app.app_context():
    136         # 检测数据库中是否存在和模型匹配的数据表。
    137         # 如果没有,则根据模型转换的建表语句进行建表。
    138         # 如果找到,则不会进行额外处理
    139 
    140         db.create_all()
    141     app.run(debug=True)
    View Code

    关联属性定义在模型中外键模型中

      1 from flask import Flask,render_template,request
      2 from flask_sqlalchemy import SQLAlchemy
      3 
      4 app = Flask(__name__)
      5 
      6 class Config(object):
      7     DEBUG = True
      8     # 数据库连接配置
      9     # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
     10     SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
     11     # 动态追踪修改设置,如未设置只会提示警告
     12     SQLALCHEMY_TRACK_MODIFICATIONS = True
     13     # 查询时会显示原始SQL语句
     14     SQLALCHEMY_ECHO = True
     15 
     16 app.config.from_object(Config)
     17 
     18 
     19 """模型类定义"""
     20 db = SQLAlchemy(app=app)
     21 # 等同于
     22 # db = SQLAlchemy()
     23 # db.init_app(app)
     24 
     25 class Student(db.Model):
     26     """学生信息模型"""
     27     # 声明与当前模型绑定的数据表名称
     28     __tablename__ = "db_students"
     29     # 字段定义
     30     """
     31     create table db_student(
     32       id int primary key auto_increment comment="主键",
     33       name varchar(15) comment="姓名",
     34     )
     35     """
     36     id = db.Column(db.Integer, primary_key=True,comment="主键")
     37     name = db.Column(db.String(15), comment="姓名")
     38     age = db.Column(db.SmallInteger, comment="年龄")
     39     sex = db.Column(db.Boolean, default=True, comment="性别")
     40     email = db.Column(db.String(128), comment="邮箱地址")
     41     money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
     42 
     43     def __repr__(self):
     44         return f"{self.name}<Student>"
     45 
     46     @classmethod
     47     def add(cls):
     48         student = cls(name="小明", sex=True, age=17, email="123456@qq.com", money=100)
     49         db.session.add(student)
     50         db.session.commit()
     51         return student
     52 
     53     @property
     54     def to_dict(self):
     55         """把对象转化成字典"""
     56         return {
     57             "id": self.id,
     58             "name": self.name,
     59             "age": self.age,
     60             "sex": self.sex,
     61             "email": self.email,
     62             "money": float("%.2f" % self.money),
     63         }
     64 
     65 from sqlalchemy.orm import backref
     66 class StudentInfo(db.Model):
     67     """学生信息附加表"""
     68     __tablename__ = "db_student_info"
     69     id = db.Column(db.Integer, primary_key=True, comment="主键")
     70     address = db.Column(db.String(500), nullable=True, comment="地址")
     71     qq_num  = db.Column(db.String(15), nullable=True, comment="QQ号")
     72     # 外键设置[默认创建数据库物理外键]
     73     user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id")
     74     # 模型属性,不是数据库的字段,不会在数据表中出现,仅仅是SQLAlchemy为了方便开发者使用关联查询所提供的对象属性
     75     # info 可以代表与当前数据对应的外键模型对象
     76     student = db.relationship("Student", backref=backref("info",uselist=False))
     77 
     78 @app.route("/")
     79 def index():
     80     """查询数据"""
     81     # 正向关联----> 从主模型查询外键模型
     82     student_info = StudentInfo.query.filter(StudentInfo.qq_num=="100861220").first()
     83     print(student_info.student) #    xiaolan02<Student>
     84     print(student_info.student.name) #  xiaolan02
     85     print(student_info.user_id) # 2     仅仅获取了外键真实数据
     86 
     87 
     88     # 反向关联----> 从外键模型查询主模型
     89     student = Student.query.get(1)
     90     print(student.info) # <StudentInfo 1>
     91     print(student.info.address) # 北京市昌平区百沙路204号
     92 
     93     """修改数据"""
     94     # 通过主表使用关联属性可以修改附加表的数据
     95     # student = Student.query.get(2)
     96     # student.info.address = "广州市天河区天河东路103号"
     97     # db.session.commit()
     98 
     99     # 也可以通过附加表模型直接修改主表的数据
    100     # student_info = StudentInfo.query.filter(StudentInfo.qq_num == "100861220").first()
    101     # print(student_info.student)
    102     # student_info.student.age = 22
    103     # db.session.commit()
    104 
    105     return "ok"
    106 
    107 if __name__ == '__main__':
    108     with app.app_context():
    109         # 检测数据库中是否存在和模型匹配的数据表。
    110         # 如果没有,则根据模型转换的建表语句进行建表。
    111         # 如果找到,则不会进行额外处理
    112 
    113         db.create_all()
    114     app.run(debug=True)
    View Code

    三、模型之间的关联(一对多)

    3.1 模型关联用法

    关联属性定义在主模型中

    class Student(db.Model):
        ...
        # 从Student        查询  StudentAddress:  Student.address_list = []
        # 从StudentAddress 查询  Student:         StudentAddress.student = 学生模型对象
        address_list = db.relationship("StudentAddress", uselist=True, backref="student")
    
    class StudentAddress(db.Model):
        ...
        # 外键,多的一方模型中添加外间
        user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id")

    关联属性定义在模型中外键模型中

    from sqlalchemy.orm import backref
    class
    Student(db.Model): ... class StudentAddress(db.Model): ... # 外键,多的一方模型中添加外间 user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id") student = db.relationship("Student", uselist=False, backref=backref("address_list",uselist=True))

    3.2 连表后的增改查

    关联属性定义在主模型中

      1 from flask import Flask,render_template,request
      2 from flask_sqlalchemy import SQLAlchemy
      3 
      4 app = Flask(__name__)
      5 
      6 class Config(object):
      7     DEBUG = True
      8     # 数据库连接配置
      9     # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
     10     SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
     11     # 动态追踪修改设置,如未设置只会提示警告
     12     SQLALCHEMY_TRACK_MODIFICATIONS = True
     13     # 查询时会显示原始SQL语句
     14     SQLALCHEMY_ECHO = True
     15 
     16 app.config.from_object(Config)
     17 
     18 
     19 """模型类定义"""
     20 db = SQLAlchemy(app=app)
     21 # 等同于
     22 # db = SQLAlchemy()
     23 # db.init_app(app)
     24 
     25 class Student(db.Model):
     26     """学生信息模型"""
     27     # 声明与当前模型绑定的数据表名称
     28     __tablename__ = "db_students"
     29     id = db.Column(db.Integer, primary_key=True,comment="主键")
     30     name = db.Column(db.String(15), comment="姓名")
     31     age = db.Column(db.SmallInteger, comment="年龄")
     32     sex = db.Column(db.Boolean, default=True, comment="性别")
     33     email = db.Column(db.String(128), comment="邮箱地址")
     34     money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
     35     # 从Student        查询  StudentAddress:  Student.address_list = []
     36     # 从StudentAddress 查询  Student:         StudentAddress.student = 学生模型对象
     37     address_list = db.relationship("StudentAddress", uselist=True, backref="student")
     38 
     39     def __repr__(self):
     40         return f"{self.name}<Student>"
     41 
     42 from sqlalchemy.orm import backref
     43 class StudentInfo(db.Model):
     44     """学生信息附加表"""
     45     __tablename__ = "db_student_info"
     46     id = db.Column(db.Integer, primary_key=True, comment="主键")
     47     address = db.Column(db.String(500), nullable=True, comment="默认地址")
     48     qq_num  = db.Column(db.String(15), nullable=True, comment="QQ号")
     49     # 外键设置[默认创建数据库物理外键]
     50     user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id")
     51     # 模型属性,不是数据库的字段,不会在数据表中出现,仅仅是SQLAlchemy为了方便开发者使用关联查询所提供的对象属性
     52     # info 可以代表与当前数据对应的外键模型对象
     53     student = db.relationship("Student", backref=backref("info",uselist=False))
     54 
     55 class StudentAddress(db.Model):
     56     """学生收货地址"""
     57     __tablename__ = "db_student_address"
     58     id = db.Column(db.Integer, primary_key=True, comment="主键")
     59     user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id")
     60     province = db.Column(db.String(20), comment="省份")
     61     city = db.Column(db.String(20), comment="城市")
     62     area = db.Column(db.String(20), comment="地区")
     63     address = db.Column(db.String(250), comment="详细接地")
     64     # 也可以在外键模型中声明关系熟悉
     65     # student = db.relationship("Student", uselist=False, backref=backref("address_list",uselist=True))
     66 
     67     def __repr__(self):
     68         return f"{self.province}-{self.city}-{self.area}-{self.address}"
     69 
     70 @app.route("/")
     71 def index():
     72     """添加数据"""
     73     # 添加主表信息的时候通过关联属性db.relationship同步添加附件表信息
     74     student = Student(
     75         name="xiaohei",
     76         age=18,
     77         sex=False,
     78         money=12000,
     79         email="xiaohei@qq.com",
     80         address_list = [
     81             StudentAddress(province="北京市", city="北京市", area="昌平区", address="百沙路204号"),
     82             StudentAddress(province="北京市", city="北京市", area="昌平区", address="百沙路205号"),
     83         ],
     84     )
     85     db.session.add(student)
     86     db.session.commit()
     87 
     88     # 添加附加表数据,通过关联属性中db.relationshop的backref同步添加主表数据
     89     # 1. 主模型没创建的情况
     90     stu_address = StudentAddress(
     91         province="北京市",
     92         city="北京市",
     93         area="昌平区",
     94         address="百沙路206号",
     95         student=Student(name="xiaobai",age=18,sex=False,money=12000,email="xiaobai@qq.com",)
     96     )
     97     db.session.add(stu_address)
     98     db.session.commit()
     99 
    100     # 2.1 主模型已创建的情况[直接对物理外键的字段进行赋值]
    101     stu_address = StudentAddress(
    102         province="北京市",
    103         city="北京市",
    104         area="昌平区",
    105         address="百沙路207号",
    106         user_id=4,  # 如果主模型已经创建,则直接设置物理外键的值即可
    107     )
    108     db.session.add(stu_address)
    109     db.session.commit()
    110 
    111     #2.2 主模型已创建的情况[使用查询出来的模型对象,通过关联属性进行赋值]
    112     stu_address = StudentAddress(
    113         province="北京市",
    114         city="北京市",
    115         area="昌平区",
    116         address="百沙路208号",
    117         student= Student.query.get(4),  # 如果需要通过关联属性设置,则需要先把模型对象查询出来
    118     )
    119     db.session.add(stu_address)
    120     db.session.commit()
    121 
    122     """查询数据"""
    123     # 正向关联----> 从主模型查询外键模型
    124     student = Student.query.filter(Student.name=="xiaobai").first()
    125     # 获取地址列表
    126     print(student.address_list)
    127     print(student.address_list[0].address)
    128 
    129     # 反向关联----> 从外键模型查询主模型
    130     sa = StudentAddress.query.get(4)
    131     print(sa.student)
    132     print(sa.student.name)
    133 
    134     """修改数据"""
    135     # 通过主表使用关联属性可以修改附加表的数据
    136     student = Student.query.filter(Student.name == "xiaobai").first()
    137     student.address_list[0].address = "南丰路103号"
    138     db.session.commit()
    139 
    140     # 也可以通过附加表模型直接修改主表的数据
    141     sa = StudentAddress.query.filter(StudentAddress.address=="南丰路103号").first()
    142     # StudentAddress查找Student查找StudentInfo
    143     sa.student.info.address = sa.address
    144     db.session.commit()
    145     return "ok"
    146 
    147 if __name__ == '__main__':
    148     with app.app_context():
    149         # 检测数据库中是否存在和模型匹配的数据表。
    150         # 如果没有,则根据模型转换的建表语句进行建表。
    151         # 如果找到,则不会进行额外处理
    152 
    153         db.create_all()
    154     app.run(debug=True)
    View Code

    四、基于第三方关系表构建多对多

    4.1. 数据模型建立

     

    from flask import Flask,render_template,request
    from flask_sqlalchemy import SQLAlchemy
    
    app = Flask(__name__)
    
    class Config(object):
        DEBUG = True
        # 数据库连接配置
        # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
        SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
        # 动态追踪修改设置,如未设置只会提示警告
        SQLALCHEMY_TRACK_MODIFICATIONS = True
        # 查询时会显示原始SQL语句
        SQLALCHEMY_ECHO = True
    
    app.config.from_object(Config)
    
    
    """模型类定义"""
    db = SQLAlchemy(app=app)
    
    # 多对多关系表
    achievement = db.Table(
        'db_achievement',
        db.Column('id', db.Integer, primary_key=True),
        db.Column('student_id', db.Integer, db.ForeignKey('db_students.id')),
        db.Column('course_id', db.Integer, db.ForeignKey('db_course.id')),
    )
    
    class Student(db.Model):
        """学生信息模型"""
        __tablename__ = "db_students"
        id = db.Column(db.Integer, primary_key=True,comment="主键")
        name = db.Column(db.String(15), comment="姓名")
        age = db.Column(db.SmallInteger, comment="年龄")
        sex = db.Column(db.Boolean, default=True, comment="性别")
        email = db.Column(db.String(128), comment="邮箱地址")
        money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
        course_list = db.relationship("Course",secondary=achievement, backref="student_list", lazy="dynamic")
    
        def __repr__(self):
            return f"{self.name}<Student>"
    
    class Course(db.Model):
        """课程数据模型"""
        __tablename__ = "db_course"
        id = db.Column(db.Integer, primary_key=True, comment="主键")
        name = db.Column(db.String(64), unique=True, comment="课程")
        price = db.Column(db.Numeric(7, 2))
    
        def __repr__(self):
            return f'{self.name}<Course>'

    4.2、基于第三方关系表数据操作

      1 from flask import Flask,render_template,request
      2 from flask_sqlalchemy import SQLAlchemy
      3 
      4 app = Flask(__name__)
      5 
      6 class Config(object):
      7     DEBUG = True
      8     # 数据库连接配置
      9     # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
     10     SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
     11     # 动态追踪修改设置,如未设置只会提示警告
     12     SQLALCHEMY_TRACK_MODIFICATIONS = True
     13     # 查询时会显示原始SQL语句
     14     SQLALCHEMY_ECHO = True
     15 
     16 app.config.from_object(Config)
     17 
     18 
     19 """模型类定义"""
     20 db = SQLAlchemy(app=app)
     21 
     22 # 多对多关系表
     23 achievement = db.Table(
     24     'db_achievement',
     25     db.Column('id', db.Integer, primary_key=True),
     26     db.Column('student_id', db.Integer, db.ForeignKey('db_students.id')),
     27     db.Column('course_id', db.Integer, db.ForeignKey('db_course.id')),
     28 )
     29 
     30 class Student(db.Model):
     31     """学生信息模型"""
     32     __tablename__ = "db_students"
     33     id = db.Column(db.Integer, primary_key=True,comment="主键")
     34     name = db.Column(db.String(15), comment="姓名")
     35     age = db.Column(db.SmallInteger, comment="年龄")
     36     sex = db.Column(db.Boolean, default=True, comment="性别")
     37     email = db.Column(db.String(128), comment="邮箱地址")
     38     money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
     39     course_list = db.relationship("Course",secondary=achievement, backref="student_list", lazy="dynamic")
     40 
     41     def __repr__(self):
     42         return f"{self.name}<Student>"
     43 
     44 class Course(db.Model):
     45     """课程数据模型"""
     46     __tablename__ = "db_course"
     47     id = db.Column(db.Integer, primary_key=True, comment="主键")
     48     name = db.Column(db.String(64), unique=True, comment="课程")
     49     price = db.Column(db.Numeric(7, 2))
     50 
     51     def __repr__(self):
     52         return f'{self.name}<Course>'
     53 
     54 @app.route("/")
     55 def index():
     56     """添加数据"""
     57 
     58     # student = Student(
     59     #     name="xiaozhao",
     60     #     age=13,
     61     #     sex=False,
     62     #     money=30000,
     63     #     email="100000@qq.com",
     64     #     course_list=[
     65     #         Course(name="python入门", price=99.99),
     66     #         Course(name="python初级", price=199.99),
     67     #         Course(name="python进阶", price=299.99),
     68     #     ]
     69     # )
     70     # db.session.add(student)
     71     # db.session.commit()
     72 
     73     # student = Student.query.get(4)
     74     # # student.course_list = [Course.query.get(2)]  #错误写法!!! 如果数据中已经存在了课程列表了,则不要重新赋值,会变成删除操作的
     75     # student.course_list.append(Course.query.get(3))
     76     # db.session.commit()
     77 
     78     """查询操作"""
     79     # student = Student.query.get(4)
     80     # course_list = [{"name":item.name,"price":float(item.price)} for item in student.course_list]
     81 
     82     # 查询出2号课程,都有谁在读?
     83     # course = Course.query.get(2)
     84     # student_list = [{"name":item.name,"money":float(item.money)} for item in course.student_list]
     85     # print(student_list)
     86 
     87 
     88     """更新数据"""
     89     # 给报读了3号课程的同学,返现红包200块钱
     90     course = Course.query.get(3)
     91     for student in course.student_list:
     92         student.money+=200
     93 
     94     db.session.commit()
     95 
     96     return "ok"
     97 
     98 if __name__ == '__main__':
     99     with app.app_context():
    100         db.create_all()
    101     app.run(debug=True)
    View Code

    五、基于关系模型构建多对多

    在SQLAlchemy中,基于db.Table创建的关系表,如果需要新增除了外键以外其他字段,无法操作。所以将来实现多对多的时候,除了上面db.Table方案以外,还可以把关系表声明成模型的方法,如果声明成模型,则原来课程和学生之间的多对多的关系,就会变成远程的1对多了

    5.1. 数据模型建立

    from flask import Flask,render_template,request
    from flask_sqlalchemy import SQLAlchemy
    
    app = Flask(__name__)
    
    class Config(object):
        DEBUG = True
        # 数据库连接配置
        # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
        SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
        # 动态追踪修改设置,如未设置只会提示警告
        SQLALCHEMY_TRACK_MODIFICATIONS = True
        # 查询时会显示原始SQL语句
        SQLALCHEMY_ECHO = True
    
    app.config.from_object(Config)
    
    
    """模型类定义"""
    db = SQLAlchemy(app=app)
    
    class Student(db.Model):
        """学生信息模型"""
        __tablename__ = "db_students"
        id = db.Column(db.Integer, primary_key=True,comment="主键")
        name = db.Column(db.String(15), comment="姓名")
        age = db.Column(db.SmallInteger, comment="年龄")
        sex = db.Column(db.Boolean, default=True, comment="性别")
        email = db.Column(db.String(128), comment="邮箱地址")
        money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
        achievement_list = db.relationship("Achievement", uselist=True, backref="student")
        def __repr__(self):
            return f"{self.name}<Student>"
    
    class Course(db.Model):
        """课程数据模型"""
        __tablename__ = "db_course"
        id = db.Column(db.Integer, primary_key=True, comment="主键")
        name = db.Column(db.String(64), unique=True, comment="课程")
        price = db.Column(db.Numeric(7, 2))
        achievement_list = db.relationship("Achievement", uselist=True, backref="course")
        def __repr__(self):
            return f'{self.name}<Course>'
    
    from datetime import datetime
    class Achievement(db.Model):
        __tablename__ = "db_achievement"
        id = db.Column(db.Integer, primary_key=True, comment="主键")
        student_id = db.Column(db.Integer, db.ForeignKey(Student.id))
        course_id  = db.Column(db.Integer, db.ForeignKey(Course.id))
        score = db.Column(db.Numeric(4,1), default=0, comment="成绩")
        time  = db.Column(db.DateTime, default=datetime.now, comment="考试时间")

    5.2、基于关系模型数据操作

     1 from flask import Flask,render_template,request
     2 from flask_sqlalchemy import SQLAlchemy
     3 
     4 app = Flask(__name__)
     5 
     6 class Config(object):
     7     DEBUG = True
     8     # 数据库连接配置
     9     # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
    10     SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
    11     # 动态追踪修改设置,如未设置只会提示警告
    12     SQLALCHEMY_TRACK_MODIFICATIONS = True
    13     # 查询时会显示原始SQL语句
    14     SQLALCHEMY_ECHO = True
    15 
    16 app.config.from_object(Config)
    17 
    18 
    19 """模型类定义"""
    20 db = SQLAlchemy(app=app)
    21 
    22 class Student(db.Model):
    23     """学生信息模型"""
    24     __tablename__ = "db_students"
    25     id = db.Column(db.Integer, primary_key=True,comment="主键")
    26     name = db.Column(db.String(15), comment="姓名")
    27     age = db.Column(db.SmallInteger, comment="年龄")
    28     sex = db.Column(db.Boolean, default=True, comment="性别")
    29     email = db.Column(db.String(128), comment="邮箱地址")
    30     money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
    31     achievement_list = db.relationship("Achievement", uselist=True, backref="student")
    32     def __repr__(self):
    33         return f"{self.name}<Student>"
    34 
    35 class Course(db.Model):
    36     """课程数据模型"""
    37     __tablename__ = "db_course"
    38     id = db.Column(db.Integer, primary_key=True, comment="主键")
    39     name = db.Column(db.String(64), unique=True, comment="课程")
    40     price = db.Column(db.Numeric(7, 2))
    41     achievement_list = db.relationship("Achievement", uselist=True, backref="course")
    42     def __repr__(self):
    43         return f'{self.name}<Course>'
    44 
    45 from datetime import datetime
    46 class Achievement(db.Model):
    47     __tablename__ = "db_achievement"
    48     id = db.Column(db.Integer, primary_key=True, comment="主键")
    49     student_id = db.Column(db.Integer, db.ForeignKey(Student.id))
    50     course_id  = db.Column(db.Integer, db.ForeignKey(Course.id))
    51     score = db.Column(db.Numeric(4,1), default=0, comment="成绩")
    52     time  = db.Column(db.DateTime, default=datetime.now, comment="考试时间")
    53 
    54 @app.route("/")
    55 def index():
    56     """添加数据"""
    57     # 记录xiaobai本次的python入门考试成绩: 88
    58     # achievement = Achievement(
    59     #     student= Student.query.filter(Student.name=="xiaobai").first(),
    60     #     course = Course.query.filter(Course.name=="python初级").first(),
    61     #     score  = 81
    62     # )
    63     # db.session.add(achievement)
    64     # db.session.commit()
    65 
    66     """查询操作"""
    67     # 查询xiaobai的成绩
    68     student = Student.query.filter(Student.name=="xiaobai").first()
    69     for achievement in student.achievement_list:
    70         print(f"课程:{achievement.course.name},成绩:{achievement.score}")
    71 
    72     return "ok"
    73 
    74 if __name__ == '__main__':
    75     with app.app_context():
    76         db.create_all()
    77     app.run(debug=True)
    View Code

    六、逻辑外键

    6.1 建表语句

    from flask import Flask,render_template,request
    from flask_sqlalchemy import SQLAlchemy
    
    app = Flask(__name__)
    
    class Config(object):
        DEBUG = True
        # 数据库连接配置
        # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
        SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
        # 动态追踪修改设置,如未设置只会提示警告
        SQLALCHEMY_TRACK_MODIFICATIONS = True
        # 查询时会显示原始SQL语句
        SQLALCHEMY_ECHO = True
    
    app.config.from_object(Config)
    
    
    """模型类定义"""
    db = SQLAlchemy(app=app)
    
    class Student(db.Model):
        """学生信息模型"""
        __tablename__ = "db_students"
        id = db.Column(db.Integer, primary_key=True,comment="主键")
        name = db.Column(db.String(15), comment="姓名")
        age = db.Column(db.SmallInteger, comment="年龄")
        sex = db.Column(db.Boolean, default=True, comment="性别")
        email = db.Column(db.String(128), comment="邮箱地址")
        money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
        def __repr__(self):
            return f"{self.name}<Student>"
    
    class Course(db.Model):
        """课程数据模型"""
        __tablename__ = "db_course"
        id = db.Column(db.Integer, primary_key=True, comment="主键")
        name = db.Column(db.String(64), unique=True, comment="课程")
        price = db.Column(db.Numeric(7, 2))
        def __repr__(self):
            return f'{self.name}<Course>'
    
    from datetime import datetime
    class Achievement(db.Model):
        __tablename__ = "db_achievement"
        id = db.Column(db.Integer, primary_key=True, comment="主键")
        student_id = db.Column(db.Integer, comment="学生ID")
        course_id  = db.Column(db.Integer, comment="课程ID")
        score = db.Column(db.Numeric(4,1), default=0, comment="成绩")
        time  = db.Column(db.DateTime, default=datetime.now, comment="考试时间")

    6.2 逻辑外键的数据操作

    运用逻辑外键查询的基本用法:

    模型.query.join("模型类","主模型.主键==外键模型.外键").with_entities("字段1","字段2".label("字段别名")).all() 
     1 from flask import Flask,render_template,request
     2 from flask_sqlalchemy import SQLAlchemy
     3 
     4 app = Flask(__name__)
     5 
     6 class Config(object):
     7     DEBUG = True
     8     # 数据库连接配置
     9     # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
    10     SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
    11     # 动态追踪修改设置,如未设置只会提示警告
    12     SQLALCHEMY_TRACK_MODIFICATIONS = True
    13     # 查询时会显示原始SQL语句
    14     SQLALCHEMY_ECHO = True
    15 
    16 app.config.from_object(Config)
    17 
    18 
    19 """模型类定义"""
    20 db = SQLAlchemy(app=app)
    21 
    22 class Student(db.Model):
    23     """学生信息模型"""
    24     __tablename__ = "db_students"
    25     id = db.Column(db.Integer, primary_key=True,comment="主键")
    26     name = db.Column(db.String(15), comment="姓名")
    27     age = db.Column(db.SmallInteger, comment="年龄")
    28     sex = db.Column(db.Boolean, default=True, comment="性别")
    29     email = db.Column(db.String(128), comment="邮箱地址")
    30     money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
    31     def __repr__(self):
    32         return f"{self.name}<Student>"
    33 
    34 class Course(db.Model):
    35     """课程数据模型"""
    36     __tablename__ = "db_course"
    37     id = db.Column(db.Integer, primary_key=True, comment="主键")
    38     name = db.Column(db.String(64), unique=True, comment="课程")
    39     price = db.Column(db.Numeric(7, 2))
    40     def __repr__(self):
    41         return f'{self.name}<Course>'
    42 
    43 from datetime import datetime
    44 class Achievement(db.Model):
    45     __tablename__ = "db_achievement"
    46     id = db.Column(db.Integer, primary_key=True, comment="主键")
    47     student_id = db.Column(db.Integer, comment="学生ID")
    48     course_id  = db.Column(db.Integer, comment="课程ID")
    49     score = db.Column(db.Numeric(4,1), default=0, comment="成绩")
    50     time  = db.Column(db.DateTime, default=datetime.now, comment="考试时间")
    51 
    52 @app.route("/")
    53 def index():
    54     """添加数据"""
    55     # 记录xiaobai本次的python入门考试成绩: 88
    56     # achievement = Achievement(
    57     #     student_id = Student.query.filter(Student.name=="xiaobai").first().id,
    58     #     course_id  = Course.query.filter(Course.name=="python入门").first().id,
    59     #     score = 80,
    60     #     time = datetime.now(),
    61     # )
    62     # db.session.add(achievement)
    63     # db.session.commit()
    64 
    65     """查询操作"""
    66     # 查询xiaobai的成绩
    67     student = Student.query.join(
    68         Achievement,
    69         Achievement.student_id == Student.id
    70     ).join(
    71         Course,
    72         Course.id == Achievement.course_id
    73     ).filter(
    74         Student.
    75         =="xiaobai",
    76         Achievement.score==88,
    77     ).with_entities(
    78         Student.name,
    79         Course.name.label("course"),
    80         Achievement.score.label("number")
    81     ).first()
    82 
    83     # 当连表查询返回一个结果的时候,可以通过keys查看当前模型提供的字段
    84     print(student) # 字段列表名
    85     print(type(student)) # 返回值是一个SQLAlchemy内部封装的模型对象
    86     print(student.keys()) # 字段列表
    87     print(f"{student.name}的{student.course}成绩是:{student.number}")
    88 
    89     return "ok"
    90 
    91 if __name__ == '__main__':
    92     with app.app_context():
    93         db.create_all()
    94     app.run(debug=True)
    View Code

     注意:django中也有虚拟外键的设置方案:在模型中设置db_constraint = False

    七、lazy 懒加载的用法

    7.1 懒加载简介

    指定如何加载关联模型数据的方式。

    参数值:

    • select(立即加载,查询所有相关数据显示,相当于lazy=True)
    • subquery(立即加载,但使用子查询)
    • dynamic(不立即加载,但提供加载记录的查询对象)
    class Teacher(db.Model):
        ...
        # 关联属性,一的一方添加模型关联属性
        course = db.relationship("Course", uselist=True, backref="teacher",lazy='dynamic')
       
    class Course(db.Model):
        ...
        # 外键,多的一方模型中添加外间
        teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id))
    • 其中realtionship描述了Course和Teacher的关系。第一个参数为对应参照的类"Course"

    • 第二个参数backref为类Teacher申明新属性的方法

    • 第三个参数lazy决定了什么时候SQLALchemy从数据库中加载数据

      • lazy='subquery',查询当前数据模型时,采用子查询(subquery),把外键模型的属性也瞬间查询出来了。

      • lazy=True或lazy='select',查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性时,才进行连表查询数据[执行SQL]

      • lazy='dynamic',查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性并操作外键模型具体属性时,才进行连表查询数据[执行SQL]

    • 常用的lazy选项:dynamic和select

    7.2 操作实例

      1 from flask import Flask,render_template,request
      2 from flask_sqlalchemy import SQLAlchemy
      3 
      4 app = Flask(__name__)
      5 
      6 class Config(object):
      7     DEBUG = True
      8     # 数据库连接配置
      9     # SQLALCHEMY_DATABASE_URI = "数据库类型://数据库账号:密码@数据库地址:端口/数据库名称?charset=utf8mb4"
     10     SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
     11     # 动态追踪修改设置,如未设置只会提示警告
     12     SQLALCHEMY_TRACK_MODIFICATIONS = True
     13     # 查询时会显示原始SQL语句
     14     SQLALCHEMY_ECHO = True
     15 
     16 app.config.from_object(Config)
     17 
     18 
     19 """模型类定义"""
     20 db = SQLAlchemy(app=app)
     21 # 等同于
     22 # db = SQLAlchemy()
     23 # db.init_app(app)
     24 
     25 class Student(db.Model):
     26     """学生信息模型"""
     27     # 声明与当前模型绑定的数据表名称
     28     __tablename__ = "db_students"
     29     id = db.Column(db.Integer, primary_key=True,comment="主键")
     30     name = db.Column(db.String(15), comment="姓名")
     31     age = db.Column(db.SmallInteger, comment="年龄")
     32     sex = db.Column(db.Boolean, default=True, comment="性别")
     33     email = db.Column(db.String(128), comment="邮箱地址")
     34     money = db.Column(db.Numeric(10,2), default=0.0, comment="钱包")
     35     # 从Student        查询  StudentAddress:  Student.address_list = []
     36     # 从StudentAddress 查询  Student:         StudentAddress.student = 学生模型对象
     37     address_list = db.relationship("StudentAddress", uselist=True, backref="student",lazy="dynamic")
     38 
     39     def __repr__(self):
     40         return f"{self.name}<Student>"
     41 
     42 from sqlalchemy.orm import backref
     43 
     44 class StudentAddress(db.Model):
     45     """学生收货地址"""
     46     __tablename__ = "db_student_address"
     47     id = db.Column(db.Integer, primary_key=True, comment="主键")
     48     user_id = db.Column(db.ForeignKey("db_students.id"), comment="学生id")
     49     province = db.Column(db.String(20), comment="省份")
     50     city = db.Column(db.String(20), comment="城市")
     51     area = db.Column(db.String(20), comment="地区")
     52     address = db.Column(db.String(250), comment="详细接地")
     53     # 也可以在外键模型中声明关系熟悉
     54     # student = db.relationship("Student", uselist=False, backref=backref("address_list",uselist=True))
     55 
     56     def __repr__(self):
     57         return f"{self.province}-{self.city}-{self.area}-{self.address}"
     58 
     59 @app.route("/")
     60 def index():
     61     """查询数据"""
     62     # 正向关联----> 从主模型查询外键模型
     63     student = Student.query.filter(Student.name=="xiaobai").first()
     64     print(student)
     65     # # 获取地址列表[调用关联属性,直接把外建模型对应数据直接查询查来]
     66     address_list = student.address_list[0].address
     67     return "ok"
     68 
     69 """
     70 lazy="subquery"
     71 1. 只查询:student,但是同时使用子查询语句进行链表操作,把外键模型数据也查询出来了
     72 
     73     SELECT db_students.id AS db_students_id, db_students.name AS db_students_name, db_students.age AS db_students_age, db_students.sex AS db_students_sex, db_students.email AS db_students_email, db_students.money AS db_students_money 
     74     FROM db_students
     75 
     76     SELECT db_student_address.id AS db_student_address_id, db_student_address.user_id AS db_student_address_user_id, db_student_address.province AS db_student_address_province, db_student_address.city AS db_student_address_city, db_student_address.area AS db_student_address_area, db_student_address.address AS db_student_address_address, anon_1.db_students_id AS anon_1_db_students_id 
     77     FROM (SELECT db_students.id AS db_students_id  FROM db_students  WHERE db_students.name = %s  LIMIT %s) AS anon_1 
     78     INNER JOIN db_student_address ON anon_1.db_students_id = db_student_address.user_id
     79 
     80 lazy="select",
     81 1. 只查询:student
     82     SELECT db_students.id AS db_students_id, db_students.name AS db_students_name, db_students.age AS db_students_age, db_students.sex AS db_students_sex, db_students.email AS db_students_email, db_students.money AS db_students_money 
     83     FROM db_students WHERE db_students.name = %s  LIMIT %s
     84 
     85 2. 调用关联属性,直接连表查询
     86     SELECT db_students.id AS db_students_id, db_students.name AS db_students_name, db_students.age AS db_students_age, db_students.sex AS db_students_sex, db_students.email AS db_students_email, db_students.money AS db_students_money 
     87     FROM db_students WHERE db_students.name = %s  LIMIT %s
     88 
     89     SELECT db_student_address.id AS db_student_address_id, db_student_address.user_id AS db_student_address_user_id, db_student_address.province AS db_student_address_province, db_student_address.city AS db_student_address_city, db_student_address.area AS db_student_address_area, db_student_address.address AS db_student_address_address 
     90     FROM db_student_address WHERE %s = db_student_address.user_id
     91 
     92 lazy="dynamic"
     93 1. 只查询:student
     94    SELECT db_students.id AS db_students_id, db_students.name AS db_students_name, db_students.age AS db_students_age, db_students.sex AS db_students_sex, db_students.email AS db_students_email, db_students.money AS db_students_money 
     95     FROM db_students WHERE db_students.name = %s  LIMIT %s
     96 
     97 2. 调用关联属性,不连表,直到代码真的调用了外键模型的具体字段才会查询数据库
     98     SELECT db_students.id AS db_students_id, db_students.name AS db_students_name, db_students.age AS db_students_age, db_students.sex AS db_students_sex, db_students.email AS db_students_email, db_students.money AS db_students_money 
     99     FROM db_students WHERE db_students.name = %s  LIMIT %s
    100     
    101 """
    102 
    103 if __name__ == '__main__':
    104     with app.app_context():
    105         # 检测数据库中是否存在和模型匹配的数据表。
    106         # 如果没有,则根据模型转换的建表语句进行建表。
    107         # 如果找到,则不会进行额外处理
    108 
    109         db.create_all()
    110     app.run(debug=True)
    View Code
  • 相关阅读:
    react 中cookie的使用
    使用react-router-dom 来搭建react路由
    取消浏览器的自动缓存
    vue 的$nextTick方法,与$set()方法
    vue监听data以及prop中的参数变化
    iview树的修改某个节点,树刷新后自动展开你刚才展开的所有节点
    Linux vim常用命令
    Linux yum源配置
    博客园鼠标点击特效
    Linux 简介
  • 原文地址:https://www.cnblogs.com/yj0405/p/14824650.html
Copyright © 2020-2023  润新知