SQLAlchemy表关系

1.SQLAlchemy表关系

在MySQL中表关系有一对一、一对多和多对多,我们接下来通过SQLAlchemy实现上述表关系。

1.1.一对多

在MySQL中构建表与表之间的关系是通过设置外键来完成的,外键可以使得两张表关联,保证数据的一致性和实现一些级联操作。同样,我们需要通过SQLAlchemy设置外键来实现表关系。

现在我们在数据库中建立一张student表,表中有id字段(数值型、主键、自增),name字段(字符串类型)、age字段(字符串类型)、sex字段(枚举型,“男”和“女”);我们再建立一张score表,表中有num字段(表示序号、数值型、主键、自增)、name字段(字符型)、id(外键,对应student表中的id字段、数值型)、score字段(数值型)。student表中的一条记录可以对应score表中的多条记录。因此,父模型为student表,子模型为score表。

代码如下,

from sqlalchemy import create_engine, Column, String, Integer, Enum, ForeignKey  
# 注意要导入ForeignKey模块
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship  # 注意要导入relationship模块

HOSTNAME = '127.0.0.1'
PORT = '3306'
USERNAME = 'root'
PASSWORD = '******'
DATABASE = 'sqlalchemy'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8mb4'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)  # ?charset=utf8mb4表示设置字符集类型为utf8mb4

engine = create_engine(DB_URI)

Base = declarative_base(engine)

session = sessionmaker(engine)()


class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(10))
    age = Column(Integer)
    sex = Column(Enum('男', '女'))

    def __repr__(self):
        return "Student(name:{}, age:{}, sex:{})".format(self.name, self.age, self.sex)


class Score(Base):
    __tablename__ = 'score'
    num = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(10))
    id = Column(Integer, ForeignKey('student.id'))  
    # 设置外键,该外键对应student表id字段,一般格式为 表名.字段
    score = Column(Integer)

    student = relationship('Student', backref='score')
    # 设置relationship,第一个参数为指向的ORM模型,backref参数可以指定反向访问的属性名称

    def __repr__(self):
        return "Score(name:{}, id:{}, score:{})".format(self.name, self.id, self.score)


Base.metadata.drop_all()
Base.metadata.create_all()

注意,
1.mysql级别的外键,必须拿到一个表的外键,然后通过这个外键再去另外一张表中查找,这样太麻烦了。SQLAlchemy提供了一个relationship,这个类可以定义属性,以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问得到了。
2.外键的字段,必须和父表的主键字段类型保持一致。

下面,我们向表中添加数据,

student1 = Student(name='zhao', age=22, sex='男')
student2 = Student(name='qian', age=20, sex='男')
student3 = Student(name='sun', age=23, sex='女')
student4 = Student(name='li', age=21, sex='男')

score1 = Score(name='zhao', score=87)
score2 = Score(name='zhao', score=98)
score3 = Score(name='qian', score=78)
score4 = Score(name='qian', score=99)
score5 = Score(name='qian', score=81)
score6 = Score(name='sun', score=99)
score7 = Score(name='li', score=100)

session.add_all([student1, student2, student3, student4])

student1.score.append(score1)  # 通过backref参数,将score1与student1建立联系
student1.score.append(score2)
student2.score.append(score3)
student2.score.append(score4)
student2.score.append(score5)
student3.score.append(score6)
student4.score.append(score7)

session.commit()

这里我们也可以用以下方式添加数据,

student1 = Student(name='zhao', age=22, sex='男')
student2 = Student(name='qian', age=20, sex='男')
student3 = Student(name='sun', age=23, sex='女')
student4 = Student(name='li', age=21, sex='男')

score1 = Score(name='zhao', score=87)
score2 = Score(name='zhao', score=98)
score3 = Score(name='qian', score=78)
score4 = Score(name='qian', score=99)
score5 = Score(name='qian', score=81)
score6 = Score(name='sun', score=99)
score7 = Score(name='li', score=100)

score1.student = student1  # 通过在Score类中定义的student属性建立score1与student1的关系
score2.student = student1
score3.student = student2
score4.student = student2
score5.student = student2
score6.student = student3
score7.student = student4

session.add_all([student1, student2, student3, student4])

session.add_all([score1, score2, score3, score4, score5, score6, score7])

session.commit()

建立好关系之后,我们可以通过student表中任意一条记录查到其对应的score表记录,或者通过score表中任意一条记录查到其对应的student表记录,代码如下,

student = session.query(Student).filter(Student.name == 'zhao').first()
print(student.score)  # 通过backref参数倒查score表中的记录

score = session.query(Score).filter_by(id=3).first()
print(score.student)  # 通过在Score类中定义的student属性查找对应的student表中的记录

执行结果如下,

[Score(name:zhao, id:1, score:87), Score(name:zhao, id:1, score:98)]
Student(name:sun, age:23, sex:)

1.2.一对一

我们通过上面的例子可以看到,输出的student.score为列表形式,说明表关系为一对多模式,因此,我们只需修改backref参数即可完成一对一表关系的建立。

代码如下,

from sqlalchemy import create_engine, Column, String, Integer, Enum, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, backref  # 导入backref函数

HOSTNAME = '127.0.0.1'
PORT = '3306'
USERNAME = 'root'
PASSWORD = '******'
DATABASE = 'sqlalchemy'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8mb4'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)

engine = create_engine(DB_URI)

Base = declarative_base(engine)

session = sessionmaker(engine)()


class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(10))
    age = Column(Integer)
    sex = Column(Enum('男', '女'))

    def __repr__(self):
        return "Student(name:{}, age:{}, sex:{})".format(self.name, self.age, self.sex)


class Score(Base):
    __tablename__ = 'score'
    num = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(10))
    id = Column(Integer, ForeignKey('student.id'))
    score = Column(Integer)

    student = relationship('Student', backref=backref('score', uselist=False))
    # backref参数通过backref()函数进行设置,backref()函数中的第一个参数为指定反向访问的属性名称,第二个参数为开启一对一关系


    def __repr__(self):
        return "Score(name:{}, id:{}, score:{})".format(self.name, self.id, self.score)


Base.metadata.drop_all()
Base.metadata.create_all()

我们再次添加数据,

student1 = Student(name='zhao', age=22, sex='男')
student2 = Student(name='qian', age=20, sex='男')
student3 = Student(name='sun', age=23, sex='女')
student4 = Student(name='li', age=21, sex='男')

score1 = Score(name='zhao', score=87)
score2 = Score(name='qian', score=98)
score3 = Score(name='sun', score=78)
score4 = Score(name='li', score=99)

score1.student = student1
score2.student = student2
score3.student = student3
score4.student = student4

session.add_all([score1, score2, score3, score4])

session.commit()

查询数据,

student = session.query(Student).filter(Student.name == 'zhao').first()
print(student.score)

score = session.query(Score).filter_by(id=3).first()
print(score.student)

执行结果如下,

Score(name:zhao, id:1, score:87)
Student(name:sun, age:23, sex:)

1.3.多对多

多对多表关系的建立需要通过一张中间表来绑定他们之间的关系。先将需要做多对多的表的ORM模型定义出来,再使用Table()函数定义一个中间表,中间表只需包含两个模型的外键字段,并且让外键字段均为主键。最后,在两个需要做多对多的模型中随便选择一个模型,定义一个relationship属性,来绑定三者之间的关系,在使用relationship的时候,需要添加secondary=中间表。

下面我们来举一个例子,现在我们在数据库中建立一张student表,表中有sid字段(数值型、主键、自增),sname字段(字符串类型)、tid字段(数值型)、age字段(字符串类型)、sex字段(枚举型,“男”和“女”);我们再建立一张teacher表,表中有tid字段(数值型、主键、自增)、tname字段(字符型)、sid字段(数值型)。student表中的一条记录可以对应teacher表中的多条记录,同样,teacher表中的一条记录也可以对应student表中的多条记录。

代码如下,

from sqlalchemy import create_engine, Column, String, Integer, Enum, ForeignKey, Table  # 导入Table()函数
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, backref

HOSTNAME = '127.0.0.1'
PORT = '3306'
USERNAME = 'root'
PASSWORD = '******'
DATABASE = 'sqlalchemy'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8mb4'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)

engine = create_engine(DB_URI)

Base = declarative_base(engine)

session = sessionmaker(engine)()

student_teacher = Table('student_teacher',
                        Base.metadata,
                        Column('sid', Integer, ForeignKey('student.sid'), primary_key=True),
                        Column('tid', Integer, ForeignKey('teacher.tid'), primary_key=True))
# 使用Table()函数创建表,第一个参数为表名,第二个参数固定为Base.metadata,之后的参数为新建字段,Column()函数的第一个参数为字段名


class Student(Base):
    __tablename__ = 'student'
    sid = Column(Integer, primary_key=True, autoincrement=True)
    sname = Column(String(10))
    tid = Column(Integer)
    age = Column(Integer)
    sex = Column(Enum('男', '女'))

    def __repr__(self):
        return "Student(sname:{}, sid:{})".format(self.sname, self.sid)


class Teacher(Base):
    __tablename__ = 'teacher'
    tid = Column(Integer, primary_key=True, autoincrement=True)
    tname = Column(String(10))
    sid = Column(Integer)

    student = relationship('Student', backref='teacher', secondary=student_teacher)
    # 添加secondary参数为中间表

    def __repr__(self):
        return "Teacher(tname:{}, tid:{})".format(self.tname, self.tid)


Base.metadata.drop_all()
Base.metadata.create_all()

我们来添加一些数据,

student1 = Student(sname='zhao', age=22, sex='男')
student2 = Student(sname='qian', age=20, sex='男')
student3 = Student(sname='sun', age=23, sex='女')
student4 = Student(sname='li', age=21, sex='男')

teacher1 = Teacher(tname='wang')
teacher2 = Teacher(tname='zhang')

teacher1.student = [student1, student3, student4]  # 通过列表方式建立联系
teacher2.student = [student1, student2, student4]

session.add_all([teacher1, teacher2])

session.commit()

最后,我们来进行查询,

student = session.query(Student).filter_by(sname='zhao').first()
print(student.teacher)

teacher = session.query(Teacher).filter(Teacher.tname == 'wang').first()
print(teacher.student)

执行结果如下,

[Teacher(tname:wang, tid:1), Teacher(tname:zhang, tid:2)]
[Student(sname:zhao, sid:1), Student(sname:sun, sid:3), Student(sname:li, sid:4)]

可以看到,表关系为多对多。

你可能感兴趣的:(数据库,mysql,SQLAlchemy)