1、外键的查询
第一种方式:
from sqlalchemy import create_engine, ForeignKeyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmakerfrom sqlalchemy import Column, String, Integer, EnumUSERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)class Class(Base):__tablename__ = "class"id = Column(Integer, primary_key=True, autoincrement=True)Class = Column("class", Enum("一班", "二班", "三班", "四班"))def __str__(self):return self.Classclass Student(Base):__tablename__ = "student"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10))# 添加外键Class = Column("class", Integer, ForeignKey("class.id", ondelete="CASCADE"))Base.metadata.create_all()session = sessionmaker(bind=engine)()# 首先通过过滤得到目标值dates = session.query(Student).filter(Student.name == "龚祝俊").first()# 获取外键的值Class = dates.Class# 过滤出外键对应的另一张表的值result = session.query(Class).filter(Class.id == Class)print(result)
第二种方法:这里因为数据的关系选择了查询所有结果
dates = session.query(Class).filter(Class.id == Student.Class)for date in dates:print(date)
2、一对多表关系的实现
注意点:
1、需要导入relationship方法
2、两张表之间必需存在外键关系,否则无法查询
一个小例子:实现查找一个班级中的所有学生
方法一:普通思路-通过外键进行查找
from sqlalchemy import create_engine, ForeignKeyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmakerfrom sqlalchemy import Column, String, Integer, EnumUSERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)class Class(Base):__tablename__ = "class"id = Column(Integer, primary_key=True, autoincrement=True)Class = Column("class", Enum("一班", "二班", "三班", "四班"))def __str__(self):return self.Classclass Student(Base):__tablename__ = "student"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10))# 添加外键Class = Column("class", Integer, ForeignKey("class.id", ondelete="CASCADE"))def __str__(self):return self.nameBase.metadata.create_all()session = sessionmaker(bind=engine)()date = session.query(Class).filter(Class.Class == "一班").first()results = session.query(Student).filter(Student.Class == date.id)for result in results:print(result.name)
方法二:relationship方法
from sqlalchemy import create_engine, ForeignKeyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmaker, relationshipfrom sqlalchemy import Column, String, Integer, EnumUSERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)class Class(Base):__tablename__ = "class"id = Column(Integer, primary_key=True, autoincrement=True)Class = Column("class", Enum("一班", "二班", "三班", "四班"))# 定义relationship属性,关联Student表seck_all = relationship("Student")def __str__(self):return self.Classclass Student(Base):__tablename__ = "student"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10))# 添加外键Class = Column("class", Integer, ForeignKey("class.id", ondelete="CASCADE"))# 定义relationship属性,关联Class表make_all = relationship("Class")def __str__(self):return self.nameBase.metadata.create_all()session = sessionmaker(bind=engine)()dates = session.query(Class).filter(Class.Class == "一班").first()for date in dates.seck_all:print(date)
方法三、升级版的relationship用法(backref参数,反向访问属性)
在上面的用法中分别在两张表模型中写了relationship属性,现在通过relationship属性只要写一条即可
from sqlalchemy import create_engine, ForeignKeyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmaker, relationshipfrom sqlalchemy import Column, String, Integer, EnumUSERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)class Class(Base):__tablename__ = "class"id = Column(Integer, primary_key=True, autoincrement=True)Class = Column("class", Enum("一班", "二班", "三班", "四班"))# 在该表中去除seck_all属性# seck_all = relationship("Student")# 放在这也是一样的,只不过改下内容,其中的Student是模型名# seck_all = relationship("Student", backref="make_all")def __str__(self):return self.Classclass Student(Base):__tablename__ = "student"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10))# 添加外键Class = Column("class", Integer, ForeignKey("class.id", ondelete="CASCADE"))# 定义relationship属性,关联Class表,在该表中添加backref参数make_all = relationship("Class", backref="seck_all")def __str__(self):return self.nameBase.metadata.create_all()session = sessionmaker(bind=engine)()dates = session.query(Class).filter(Class.Class == "一班").first()for date in dates.seck_all:print(date)
由于relationship参数对应的是单条数据,所以找寻all()时:
dates = session.query(Class).filter(XXXXX).all()for date in dates:print(date.seck_all)
代码分析:
方法导入:
from sqlalchemy.orm import relationship
写入模型字段(xxx = relationship(“对应关联表模型名”),backref=”定义名称进行反向查询”)
class Class(Base):__tablename__ = "class"id = Column(Integer, primary_key=True, autoincrement=True)Class = Column("class", Enum("一班", "二班", "三班", "四班"))# 定义seck_all属性,用以连接关联表表seck_all = relationship("Student")def __str__(self):return self.Class
进行查询:
找到需要查询的条件
dates = session.query(Class).filter(Class.Class == "一班").first()
调用relationship属性
for date in dates.seck_all:print(date)
当然通过学生来查询班级的操作也是没有问题的,在代码中Student模型已经写入了
make_all = relationship(“Class”)
3、关系表实现新增数据
方法一:分别提交,先在主表中增加添加新增数据,然后在子表中添加新增数据
由于之前选择的是Enum类型,无法添加新的数据,因此对数据进行重构
from sqlalchemy import create_engine, ForeignKeyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmaker, relationshipfrom sqlalchemy import Column, String, Integer, EnumUSERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)class Class(Base):__tablename__ = "class"id = Column(Integer, primary_key=True, autoincrement=True)Class = Column("class", string(10))def __str__(self):return self.Classclass Student(Base):__tablename__ = "student"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10))# 添加外键Class = Column("class", Integer, ForeignKey("class.id", ondelete="CASCADE"))def __str__(self):return self.nameBase.metadata.create_all()session = sessionmaker(bind=engine)()Class1 = Class(Class="一班")student1 = Student(name="刘晨", Class=1)# 先提交主表数据session.add(Class1)session.commit()# 在提交子表数据session.add(student1)session.commit()
方法二,通过relationship的关联进行提交(写入backref参数的同理)
from sqlalchemy import create_engine, ForeignKeyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmaker, relationshipfrom sqlalchemy import Column, String, Integer, EnumUSERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)class Class(Base):__tablename__ = "class"id = Column(Integer, primary_key=True, autoincrement=True)Class = Column("class", Enum("一班", "二班", "三班", "四班"))seck_all = relationship("Student")def __str__(self):return self.Classclass Student(Base):__tablename__ = "student"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10))# 添加外键Class = Column("class", Integer, ForeignKey("class.id", ondelete="CASCADE"))make_all = relationship("Class")def __str__(self):return self.nameBase.metadata.drop_all()Base.metadata.create_all()session = sessionmaker(bind=engine)()# 实例化Classclass1 = Class(Class="一班")# 实例化Studentstudent1 = Student(name="刘晨", Class=1)# 使Student1的make_all属性等于class1student1.make_all = class1# 添加提交student1session.add(student1)session.commit()
一般范式:(只能通过子表属性进行提交,不能用主表的属性进行提交)
子表实例化对象名.relationship属性 = 主表实例化对象名
多条数据的提交同理:
# 实例化Classclass1 = Class(Class="一班")# 实例化Studentstudent1 = Student(name="刘晨", Class=1)student2 = Student(name="孜然", Class=1)student3 = Student(name="刘欣欣", Class=1)# 使Student1的make_all属性等于class1student1.make_all = class1student12make_all = class1student3.make_all = class1# 添加提交student1session.add_all([student1, student2, student3])session.commit()
4、一对一表关系的实现
一对一其实就是一对多的特殊情况(uselist=False)模型.属性.append(实例化对象)
关系如下:
代码部分:有点问题
导入from sqlalchemy.orm import backref
from sqlalchemy import create_engine, ForeignKeyfrom sqlalchemy import Column, String, Integer, Enumfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import relationship, sessionmaker, backref# from sqlalchemy.dialects.mysql import LONGTEXT# 定义引擎USERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)class Information(Base):__tablename__ = "student"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10), nullable=False)make = relationship("InformationExtent", backref=backref("extent", uselist=False))def __str__(self):return self.nameclass InformationExtent(Base):__tablename__ = "studentextent"id = Column(Integer, primary_key=True, autoincrement=True)age = Column(Integer)gender = Column(Enum("男", "女"), default="男")uid = Column(Integer, ForeignKey("student.id", ondelete="CASCADE"))Base.metadata.drop_all()Base.metadata.create_all()session = sessionmaker(bind=engine)()information = Information(name="流浪者")informationextent1 = InformationExtent(age=18, gender="男", uid=1)informationextent2 = InformationExtent(age=18, gender="男", uid=1)information.make.append(informationextent1)information.make.append(informationextent2)session.add(information)session.commit()
5、多对多表关系的实现
需要中间表来作为桥梁
一个小例子:
from sqlalchemy import create_engine, ForeignKey, Tablefrom sqlalchemy import Column, String, Integer, Enumfrom sqlalchemy.orm import relationship, backref, sessionmakerfrom sqlalchemy.ext.declarative import declarative_base# 定义引擎USERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)# mytable = Table("mytable", metadata,# Column('mytable_id', Integer, primary_key=True),# Column('value', String(50))# )MY_TABLE = Table("many-to-many", Base.metadata,Column("Teachers_id", Integer, ForeignKey("teachers.id")),Column("Classroom", Integer, ForeignKey("classroom.id")))class Teachers(Base):__tablename__ = "teachers"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10))classroom = relationship("Classroom", backref="teacher", secondary=MY_TABLE)def __str__(self):return self.nameclass Classroom(Base):__tablename__ = "classroom"id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(10))floor = Column(Enum("1", "2", "3"))def __str__(self):return f"班级名:{self.name}, 楼层:{self.floor}"# Base.metadata.drop_all()Base.metadata.create_all()session = sessionmaker(bind=engine)()def add():# 定义老师字段teacher1 = Teachers(name="龚祝俊")teacher2 = Teachers(name="吴艳新")teacher3 = Teachers(name="阿吉非替尼")# 定义教室字段classroom1 = Classroom(name="物理教室", floor="3")classroom2 = Classroom(name="地理教室", floor="2")classroom3 = Classroom(name="化学教室", floor="1")# 建立关系,通过teacher2.classroomteacher1.classroom.append(classroom1)teacher1.classroom.append(classroom2)teacher2.classroom.append(classroom2)teacher2.classroom.append(classroom3)session.add_all([teacher1, teacher2])# 建立关系,通过classroom3.teacherclassroom3.teacher.append(teacher3)classroom3.teacher.append(teacher2)session.add(classroom3)# print(classroom3.teacher) --> []# print(teacher2.classroom) --> []session.commit()def query():dates = session.query(Teachers).get(1)for date in dates.classroom:print(date)print("-"*100)results = session.query(Classroom).filter(Classroom.name == "化学教室").first()for result in results.teacher:print(result)if __name__ == '__main__':add()query()
代码解读:
导入Table:
from sqlalchemy import Table
写入Table
一般样式
mytable = Table("mytable", Base.metadata,Column('mytable_id', Integer, primary_key=True),Column('value', String(50)))
mytable:自行命名的Table名,在secondary参数调用时使用
“mytable”表明,在数据库中该表的名字
metadata:元数据,从定义的Base中获取,写成Base.metadata
Column:定义表中的外键与其他两张表建立关系Column(“Teachers_id”, Integer, ForeignKey(“teachers.id”)需要使用ForeignKey属性(表名.字段名)
secondary参数,在relationship方法中定义
classroom = relationship("Classroom", backref="teacher", secondary=MY_TABLE)
append方法:
多对多不像是一对多,在定义relationship属性后,只能通过子表的relationship属性才能添加新的数据,多对多能够双向的增加数据(因为此时两张表没有主次之分)
# 定义老师字段teacher1 = Teachers(name="龚祝俊")teacher2 = Teachers(name="吴艳新")teacher3 = Teachers(name="阿吉非替尼")# 定义教室字段classroom1 = Classroom(name="物理教室", floor="3")classroom2 = Classroom(name="地理教室", floor="2")classroom3 = Classroom(name="化学教室", floor="1")# 建立关系,通过teacher2.classroomteacher1.classroom.append(classroom1)teacher1.classroom.append(classroom2)teacher2.classroom.append(classroom2)teacher2.classroom.append(classroom3)session.add_all([teacher1, teacher2])# 建立关系,通过classroom3.teacherclassroom3.teacher.append(teacher3)classroom3.teacher.append(teacher2)session.add(classroom3)# print(classroom3.teacher) --> []# print(teacher2.classroom) --> []session.commit()
小拓展:代码改写,可以但没有必要,哈哈(修改以及注意点写在代码中)
from sqlalchemy import create_engine, ForeignKey, Tablefrom sqlalchemy import Column, String, Integer, Enumfrom sqlalchemy.orm import relationship, backref, sessionmakerfrom sqlalchemy.ext.declarative import declarative_base# 定义引擎USERNAME = "root"PASSWORD = "147258"PORT = 3306HOSTNAME = "127.0.0.1"DATABASE = "primary"SLDG = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"engine = create_engine(SLDG)Base = declarative_base(engine)# 由于加入的是主键,所以需要对主键进行改写MY_TABLE = Table("many-to-many", Base.metadata,Column("Teachers_id", String(10), ForeignKey("teachers.name")),Column("Classroom_id", String(10), ForeignKey("classroom.name")))class Teachers(Base):__tablename__ = "teachers"id = Column(Integer, autoincrement=True)# 重新定义了主键,主键只能有一个name = Column(String(10), primary_key=True)classroom = relationship("Classroom", backref="teacher", secondary=MY_TABLE)def __str__(self):return self.nameclass Classroom(Base):__tablename__ = "classroom"id = Column(Integer)# 重新定义了主键name = Column(String(10), primary_key=True)floor = Column(Enum("1", "2", "3"))def __str__(self):return f"班级名:{self.name}, 楼层:{self.floor}"Base.metadata.drop_all()Base.metadata.create_all()session = sessionmaker(bind=engine)()def add():# 定义老师字段teacher1 = Teachers(id=1, name="龚祝俊")teacher2 = Teachers(id=2, name="吴艳新")teacher3 = Teachers(id=3, name="阿吉非替尼")# 定义教室字段classroom1 = Classroom(id=1, name="物理教室", floor="3")classroom2 = Classroom(id=2, name="地理教室", floor="2")classroom3 = Classroom(id=2, name="化学教室", floor="1")# 建立关系,通过teacher2.classroomteacher1.classroom.append(classroom1)teacher1.classroom.append(classroom2)teacher2.classroom.append(classroom2)teacher2.classroom.append(classroom3)session.add_all([teacher1, teacher2])# 建立关系,通过classroom3.teacherclassroom3.teacher.append(teacher3)classroom3.teacher.append(teacher2)session.add(classroom3)# print(classroom3.teacher) --> []# print(teacher2.classroom) --> []session.commit()# 由于改写了主键,因此get()方法失效def query():dates = session.query(Teachers).filter(Teachers.name == "吴艳新").first()for date in dates.classroom:print(date)print("-"*100)results = session.query(Classroom).filter(Classroom.name == "化学教室").first()for result in results.teacher:print(result)if __name__ == '__main__':add()query()
数据库显示:
教师表:
教室表:
中介表:
