1、外键的查询
第一种方式:
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, String, Integer, Enum
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.Class
class 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, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, String, Integer, Enum
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.Class
class 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.name
Base.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, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import Column, String, Integer, Enum
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.Class
class 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.name
Base.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, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import Column, String, Integer, Enum
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.Class
class 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.name
Base.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, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import Column, String, Integer, Enum
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.Class
class 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.name
Base.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, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import Column, String, Integer, Enum
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.Class
class 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.name
Base.metadata.drop_all()
Base.metadata.create_all()
session = sessionmaker(bind=engine)()
# 实例化Class
class1 = Class(Class="一班")
# 实例化Student
student1 = Student(name="刘晨", Class=1)
# 使Student1的make_all属性等于class1
student1.make_all = class1
# 添加提交student1
session.add(student1)
session.commit()
一般范式:(只能通过子表属性进行提交,不能用主表的属性进行提交)
子表实例化对象名.relationship属性 = 主表实例化对象名
多条数据的提交同理:
# 实例化Class
class1 = Class(Class="一班")
# 实例化Student
student1 = Student(name="刘晨", Class=1)
student2 = Student(name="孜然", Class=1)
student3 = Student(name="刘欣欣", Class=1)
# 使Student1的make_all属性等于class1
student1.make_all = class1
student12make_all = class1
student3.make_all = class1
# 添加提交student1
session.add_all([student1, student2, student3])
session.commit()
4、一对一表关系的实现
一对一其实就是一对多的特殊情况(uselist=False)模型.属性.append(实例化对象)
关系如下:
代码部分:有点问题
导入from sqlalchemy.orm import backref
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy import Column, String, Integer, Enum
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker, backref
# from sqlalchemy.dialects.mysql import LONGTEXT
# 定义引擎
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.name
class 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, Table
from sqlalchemy import Column, String, Integer, Enum
from sqlalchemy.orm import relationship, backref, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# 定义引擎
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.name
class 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.classroom
teacher1.classroom.append(classroom1)
teacher1.classroom.append(classroom2)
teacher2.classroom.append(classroom2)
teacher2.classroom.append(classroom3)
session.add_all([teacher1, teacher2])
# 建立关系,通过classroom3.teacher
classroom3.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.classroom
teacher1.classroom.append(classroom1)
teacher1.classroom.append(classroom2)
teacher2.classroom.append(classroom2)
teacher2.classroom.append(classroom3)
session.add_all([teacher1, teacher2])
# 建立关系,通过classroom3.teacher
classroom3.teacher.append(teacher3)
classroom3.teacher.append(teacher2)
session.add(classroom3)
# print(classroom3.teacher) --> []
# print(teacher2.classroom) --> []
session.commit()
小拓展:代码改写,可以但没有必要,哈哈(修改以及注意点写在代码中)
from sqlalchemy import create_engine, ForeignKey, Table
from sqlalchemy import Column, String, Integer, Enum
from sqlalchemy.orm import relationship, backref, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# 定义引擎
USERNAME = "root"
PASSWORD = "147258"
PORT = 3306
HOSTNAME = "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.name
class 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.classroom
teacher1.classroom.append(classroom1)
teacher1.classroom.append(classroom2)
teacher2.classroom.append(classroom2)
teacher2.classroom.append(classroom3)
session.add_all([teacher1, teacher2])
# 建立关系,通过classroom3.teacher
classroom3.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()
数据库显示:
教师表:
教室表:
中介表: