1、表的外键关联
使用SQLAlchemy
创建外键非常简单。在从表中增加一个字段,指定这个字段外键的是哪个表的哪个字段就可以了。从表中外键的字段,必须和主表的主键字段类型保持一致。
这种关联只关注数据表之间的外键关联,不考虑Python
对象之间的关联关系.
# 部门和员工之间,是一种典型的一(主)对多(从)
class Department(Base):
__tablename__ = 't_dept'
dept_no = Column(Integer, primary_key=True)
d_name = Column(String(255)) # 部门名字
city = Column(String(50))
# 员工表
class Employee(Base):
__tablename__ = 't_emp'
emp_no = Column(Integer, primary_key=True)
emp_name = Column(String(50))
job = Column(String(50))
hire_time = Column(DATE) # 入职时间
sal = Column(DECIMAL(10, 2)) # 薪资,连两位小数共十位
dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='CASCADE'))
外键的删除选项
RESTRICT
:若子表中有父表对应的关联数据,删除父表对应数据,会阻止删除。默认项NO ACTION
:在MySQL
中,同RESTRICT
。CASCADE
:级联删除。SET NULL
:父表对应数据被删除,子表对应数据项会设置为NULL。
from datetime import date
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('mysql+pymysql://root:root@localhost:3306/flask_db?charset=utf8mb4', echo=True)
Base = declarative_base(engine)
# 部门和员工之间,是一种典型的一(主)对多(从)
class Department(Base):
__tablename__ = 't_dept'
dept_no = Column(Integer, primary_key=True)
d_name = Column(String(255)) # 部门名字
city = Column(String(50))
# 员工表
class Employee(Base):
__tablename__ = 't_emp'
emp_no = Column(Integer, primary_key=True)
emp_name = Column(String(50)) # 员工名字
job = Column(String(50))
hire_time = Column(DATE) # 入职时间
sal = Column(DECIMAL(10, 2)) # 薪资,连两位小数共十位
# dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='NO ACTION'))
# dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='CASCADE')) # 级联删除
# dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='RESTRICT')) #
dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='SET NULL')) #
Base.metadata.drop_all()
Base.metadata.create_all()
d1 = Department(d_name='人事部', city='昆明')
e1 = Employee(emp_name='张三', job='经理', hire_time=date(2022, 12, 22), sal=6666.50, dept_no=1)
# 创建session对象
session = sessionmaker(engine)()
def add():
session.add(d1)
session.add(e1)
session.commit()
add()
# 删除
dept = session.query(Department).first()
session.delete(dept)
session.commit()
2、ORM中的一对多/多对一
mysql
表级别的外键,还不够爽,必须拿到一个表的外键,然后通过这个外键再去另外一张表中查找,这样太麻烦了。
SQLAlchemy
提供了一个 relationship
,这个类可以定义属性,以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问得到了。另外,可以通过 backref
来指定反向访问的属性名称。部门和员工之间的关系是一个“一对多”的关系。
# -*- coding: utf-8 -*-
"""
@file: 数据库表的外键关联.py
@author: 北极的三哈
@time: 2022/12/26 14:56
@email:flymeawei@163.com
@software: PyCharm2022.3
"""
from datetime import date
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
engine = create_engine('mysql+pymysql://root:root@localhost:3306/flask_db?charset=utf8mb4', echo=True)
Base = declarative_base(engine)
# 部门和员工之间,是一种典型的一(主)对多(从)
class Dep(Base):
__tablename__ = 't_dept'
dept_no = Column(Integer, primary_key=True)
d_name = Column(String(255)) # 部门名字
city = Column(String(50))
# 代表当前部门下所有员工的列表, 这种写法不是最优的, 最优的写法只要在其中一个对象中关联
emp = relationship("Emp") # 参数必须是另外一个相关联的类名
def __str__(self):
return 'DEP:<部门名字:{}, 城市:{}>'.format(self.d_name, self.city)
# 员工表
class Emp(Base):
__tablename__ = 't_emp'
emp_no = Column(Integer, primary_key=True)
emp_name = Column(String(50)) # 员工名字
job = Column(String(50))
hire_time = Column(DATE) # 入职时间
sal = Column(DECIMAL(10, 2)) # 薪资,连两位小数共十位
# dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='NO ACTION'))
# dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='CASCADE')) # 级联删除
dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='RESTRICT')) #
# dept_no = Column(Integer, ForeignKey('t_dept.dept_no', ondelete='SET NULL')) #
dept = relationship("Dep")
def __str__(self):
return "EMP:<员工编号:{}, 员工姓名:{}>".format(self.emp_no, self.emp_name)
Base.metadata.drop_all()
Base.metadata.create_all()
d1 = Dep(d_name='人事部', city='上海')
d2 = Dep(d_name='销售部', city='上海')
e1 = Emp(emp_name='张三', job='经理', hire_time=date(2022, 12, 22), sal=6666.50, dept_no=1)
e2 = Emp(emp_name='李四', job='经理', hire_time=date(2022, 12, 22), sal=6666.50, dept_no=1)
e3 = Emp(emp_name='王二', job='经理', hire_time=date(2022, 12, 22), sal=6666.50, dept_no=2)
e4 = Emp(emp_name='麻子', job='经理', hire_time=date(2022, 12, 22), sal=6666.50, dept_no=2)
# 创建session对象
session = sessionmaker(engine)()
def add():
session.add(d1)
session.add(d2)
session.add_all([e1, e2, e3, e4])
session.commit()
add()
# 查询一个部门,关联到部门的员工
d = session.query(Dep).first()
for e in d.emp:
print(e)
e = session.query(Emp).filter(Emp.emp_no == 4).first()
print(e)
print(e.dept)
3、ORM中的一对一
在sqlalchemy中,如果想要将两个模型映射成一对一的关系,那么应该在父模型中,指定引用的时候,要传递一个 uselist=False 这个参数进去。就是告诉父模型,以后引用这个从模型的时候,不再是一个列表了,而是一个
对象了。
方法一:参照一对多关联,加上uselist
把uselist=False
加在没有外键的对象中,其他和前面的一对多关联是一样的。
class Person(Base):
__tablename__ = 't_person'
# 在这个ORM模型中创建一些属性,来跟表中的字段进行 一一 映射。
# 这些属性必须是sqlalchemy给我们提供好的数据类型
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(255))
age = Column(Integer)
address = Column(String(255))
country = Column(String(50))
# 人对应的身份证
idcard = relationship('IDcard', uselist=False)
# 员工表
class IDcard(Base):
__tablename__ = 't_emp'
card_number = Column(String(18), primary_key=True)
p_id = Column(Integer, ForeignKey('t_person.id')) # 外键
# 该身份证对应的人
person = relationship('Person')
方法二:在主表中不需要维护关联关系(不用加关联属性),只要在从表对象中加上关联属性,并且加backref
class Person(Base):
__tablename__ = 't_person'
# 在这个ORM模型中创建一些属性,来跟表中的字段进行 一一 映射。
# 这些属性必须是sqlalchemy给我们提供好的数据类型
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(255))
age = Column(Integer)
address = Column(String(255))
country = Column(String(50))
# 人对应的身份证
# idcard = relationship('IDcard', uselist=False) # 不是一个列表
def __str__(self):
return "Person[id:{}, name:{}, age:{}]".format(self.id, self.name, self.age)
# 身份证号
class IDcard(Base):
__tablename__ = 't_idcard'
card_number = Column(String(18), primary_key=True)
p_id = Column(Integer, ForeignKey('t_person.id')) # 外键
# 该身份证对应的人
person = relationship('Person', backref=backref('idcard', uselist=False))
4、ORM中的多对多
-
多对多的关系需要通过一张中间表来绑定他们之间的关系。
-
先把两个需要做多对多的模型定义出来
-
使用
Table
定义一个中间表,中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”。 -
在两个需要做多对多的模型中随便选择一个模型,定义一个
relationship
属性,来绑定三者之间的关系,在使用relationship
的时候,需要传入一个secondary=中间表对象名。
# 学生和课程之间多对多关系
# 1.定义中间表
temp_tab = Table(
't_temp_tab',
Base.metadata,
# 3.定义中间表联合主键
Column('s_id', Integer, ForeignKey('t_student.id'), primary_key=True),
Column('c_id', Integer, ForeignKey('t_course.id'), primary_key=True)
)
# 2.创建多对多模型
# 学生表
class Student(Base):
__tablename__ = 't_student'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50))
age = Column(Integer)
# 4.定义relationship属性
course_list = relationship('Course', backref='student_list', secondary=temp_tab)
def __repr__(self):
return "Student:name=%s" % self.name
# 课程表
class Course(Base):
__tablename__ = 't_course'
id = Column(Integer, primary_key=True, autoincrement=True)
c_name = Column(String(50)) # 课程名字
def __repr__(self):
return "Course:c_name=%s" % self.c_name
# Base.metadata.drop_all()
# Base.metadata.create_all()
# 创建session对象
session = sessionmaker(engine)()
def save():
s1 = Student(name='张三', age=22)
s2 = Student(name='李四', age=22)
c1 = Course(c_name='Python')
c2 = Course(c_name='MySQL')
s1.course_list.append(c1)
s1.course_list.append(c2)
s2.course_list.append(c1)
s2.course_list.append(c2)
session.add(s1)
session.add(s2)
session.commit()
# save()
def queryDate():
s1 = session.query(Student).first()
print(s1)
print(s1.course_list)
queryDate()