Flask-SQLAlchemy一对多&多对多查询---ORM(2)

文章目录

    • 1.一对多查询
      • (1).背景知识
      • (2).代码
      • (3).具体分析
    • 2.多对多查询
      • (1).背景知识
      • (2).代码
      • (3).具体分析

1.一对多查询

(1).背景知识

classes表格

classID className location leader remark
21 快乐花朵班 101 小明
22 幸福宝宝班 102 花花
23 国际宝宝班 102 jack
24 艺术兴趣班 103 tony

students表格

st_id name gender age cls_id remark
10001 小明 1 18 21 小明是位可爱的孩子
10002 小红 0 18 22 小红是位聪明的孩子
10003 大牛 1 19 21 大牛是位勇敢的孩子
10004 花花 0 17 22 花花是位懂事的孩子
10005 tony 1 20 23 tony来自美国
10006 古天乐 1 22 21 黑马王子
10007 陈道明 1 23 22 不是所有牛奶都叫特仑苏

(2).代码

# db_module文件
from APP import db


class Classes(db.Model):
    __tablename__ = "classes"
    classID = db.Column(db.INT, primary_key=True, autoincrement=True)
    className = db.Column(db.Text)
    location = db.Column(db.Text)
    leader = db.Column(db.Text)
    remark = db.Column(db.Text)
    # 在此申明Classes类与Students类具备指向关系,且具体的关联是通过外键db.ForeignKey这个绑定的
    class_relate_student = db.relationship("Students", backref='student_relate_class', lazy='dynamic')


class Students(db.Model):
    __tablename__ = "students"
    st_id = db.Column(db.INT, primary_key=True, autoincrement=True)
    name = db.Column(db.Text)
    gender = db.Column(db.Text)
    age = db.Column(db.INT)
    remark = db.Column(db.Text)
    # 指定classID这个字段来源于classes表格的外键,指向方式:表名 + 字段名(表名)
    cls_id = db.Column(db.Integer, db.ForeignKey("classes.classID"))


key = "花花"
stu = Students.query.filter_by(name=key).first()
clsName = stu.student_relate_class.className				# stu.student_relate_class 会跳转到 classes 表
print(clsName)
    

(3).具体分析

class_relate_student 描述了:Students和Classes的关系

  • 第一个参数:为对应参照的类名"Students"
  • 第二个参数:backref为类Students申明新的属性(这个属性方便在2个表格之间互相指向)
  • 第三个参数:lazy决定了什么时候SQLALchemy从数据库中加载数据
  • 有关lazy的用法:
  • select:就是访问到属性的时候,就会全部加载该属性的数据。跳转到另一个表格如果是多的一方,直接使用指向属性就行,不需要使用one(),all()等函数
  • joined:对关联的两个表使用联接
  • subquery:与joined类似,但使用子查询
  • dynamic:生成query对象列表,跳转另一个表格如果对应多的一方,需要使用one(),all(),first() 等方法获取到这个对象,然后才能取到对应的属性值。跳转到另一个表格,如果对应少的一方,循环之后直接使用属性就能获取属性值

lazy对应的详细解释,可参考:
https://blog.csdn.net/bestallen/article/details/52601457
https://blog.csdn.net/jiulixiang_88/article/details/80587071

2.多对多查询

(1).背景知识

customer表格

id name work
1 程老板 大兴有限公司
2 李老板 弘成科技
3 司马老板 小马加油有限公司

product表格

id name price
1 丝绸 35
2 铝合金 54
3 3

association表格

id customer_id product_id
1 1 1
2 1 2
3 1 3
4 2 2
5 3 2
6 3 3

(2).代码

在多对多的关系中,如何根据某个产品名,查询到哪些人购买了这个产品呢?


from flask_sqlalchemy import SQLAlchemy
from APP.app import app

db = SQLAlchemy(app)

association_table = db.Table('association',
                             db.Column('id', db.Integer, primary_key=True, autoincrement=True),
                             db.Column('customer_id', db.Integer, db.ForeignKey('customer.id')),
                             db.Column('product_id', db.Integer, db.ForeignKey('product.id'))
                             )


class Customer(db.Model):
    __tablename__ = 'customer'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(10))
    work = db.Column(db.String(20))

    def __repr__(self):
        return 'name:{name} work:{work}'.format(name=self.name, work=self.work)

    customer_to_product = db.relationship('Product',
                                          secondary=association_table,
                                          backref='product_to_customer',
                                          lazy='dynamic'
                                          )


class Product(db.Model):
    __tablename__ = 'product'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(10))
    price = db.Column(db.Float)

    def __repr__(self):
        return 'name:{name} price:{price}'.format(name=self.name, price=self.price)


key = '盐'
pro = Product.query.filter_by(name=key).first()
print(pro, type(pro))
c = pro.product_to_customer
print(c, type(c))

for i in c:
    print(i.name, i.work)

最终输出:

name:盐 price:3.0 <class '__main__.Product'>
[name:程老板 work:大兴有限公司, name:司马老板 work:小马加油有限公司] <class 'sqlalchemy.orm.collections.InstrumentedList'>
程老板 大兴有限公司
司马老板 小马加油有限公司

同理,通过查询某个人的信息,找到这个人购买了哪些产品?


key = '司马老板'
ct = Customer.query.filter_by(name=key).first()
print(ct, type(ct))

p = ct.customer_to_product
print(p, type(p))

for i in p:
    print(i.name, i.price)

  • 最终得到的结果为:

name:司马老板 work:小马加油有限公司 <class '__main__.Customer'>
SELECT product.id AS product_id, product.name AS product_name, product.price AS product_price 
FROM product, association 
WHERE %(param_1)s = association.customer_id AND product.id = association.product_id <class 'sqlalchemy.orm.dynamic.AppenderBaseQuery'>
铝合金 54.03.0

(3).具体分析

  • 先查看下association表格的建表语句:

CREATE TABLE `association` (
  `id` int(2) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `customer_id` int(2) NOT NULL COMMENT '客户号',
  `product_id` int(2) NOT NULL COMMENT '产品编号',
  PRIMARY KEY (`id`,`customer_id`,`product_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

  • 如上表格里面,三个字段都为主键。
  • 其实id字段不需要也可以,但是一定要声明另外2个字段为主键。目的是方便通过2个主键,关联两张表格。
  • 且这2个主键也分别是另外两张表格的主键。

  • 其次,分析下association表格的模型类:

association_table = db.Table('association',
                             db.Column('id', db.Integer, primary_key=True, autoincrement=True),
                             db.Column('customer_id', db.Integer, db.ForeignKey('customer.id')),
                             db.Column('product_id', db.Integer, db.ForeignKey('product.id'))
                             )

  • 如上的数据库模型类,比另外两个特殊的地方在于,他是在association表格的基础上,再重新定义2个字段的外键,且重新定义了字段的名称。
  • flask_sqlalchemy把旧的表格association,定义为新的association_table表格去使用
  • 定义了customer_id的字段,来源于外键customer.id(表名+字段)
  • 定义了product_id的字段,来源于外键product.id(表名+字段)
  • 然后,再分析下customer表与product表之间的关系
customer_to_product = db.relationship('Product',
                                      secondary=association_table,
                                      backref='product_to_customer',
                                      lazy='dynamic'
                                        )
  • 这两张表格的主键已经通过association_table表格关联。
  • db.relationship这个方法,主要是关联2个表格的对象之间的关系
  • 使用backref这个参数,可以理解为是一种虚拟的指向关系,从一个对象指向到另一个对象的中间枢纽。
  • 第一个参数:当前类需要关联的----新类名
  • 第二个参数:secondary,这个是重新定义的关联表
  • 第三个参数:新类名指向当前类名的中间枢纽名
  • lazy:这个是加载表格的方式

参考知识:
https://www.jianshu.com/p/92890a4ec0cb
https://www.cnblogs.com/chichung/p/9794850.html

你可能感兴趣的:(Python高级)