一、外键的定义
(1)我的理解
两个内容表达的不同对象的表存在某种逻辑关系就需要用到外键。“内容表达的不同对象”的意思是指例如两个对象房东和房子,房东的属性假设有id,name等,而房子有属性id,area,price,address等,房子和房东是不相同的对象,但是房东和房子又有逻辑上的联系,即某一间房子属于某一个房东。不可能把所有的房东的信息和房子放在一个表中,因为现实中房东可能有多个房子,这样设计出来的表会很复杂,所以应该把房东和房子各建一个表并且用外键将两者连在一起,而外键的作用正是房东和房子之间的纽带。
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
DB_URI = "mysql://{username}:{password}@{host}:{port}/{db}".format(
username=USERNAME, password=PASSWORD, host=HOSTNAME, port=PORT, db=DATEBASE
)
engine = create_engine(DB_URI)
Base = declarative_base(engine)
session = sessionmaker(engine)()
class Landlord(Base):
__tablename__ = 'landlord'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False)
class House(Base):
__tablename__ = 'house'
id = Column(Integer, primary_key=True, autoincrement=True)
address = Column(String(50), nullable=False)
llid = Column(Integer, ForeignKey('landlord.id')) # 外键指向房东id
(2)注意事项
外键所指向的不一定是id也可以是别的属性,但是relationship第一个参数的类型要和对应的外键的数据类型要一致。尤其是索引设置成uuid的时候,更要小心。
二、外键约束
(1)设置原因
当某一个房东被删除的时候,被外键关联的房子发现它没有指向的元素,房子表的数据也会遭到破坏,通常默认情况是抛出异常,SQLAlchemy也给用户提供了其他的选择权利例如:当房东被删除后他名下的所有房子都会被删除,或者是只是把房东删除,房子的归属权设为空,正好对应了下面几种参数类型。在实际开发中通常只是设为逻辑上的空,正如很多的云产品,当你删除了所有账户信息,到了下一次加载还是能加载出来。在逻辑上通常会设置一个属性例如叫“is_delete”如果这个值为True就直接给客户返回空,但是数据还是存在数据库,当“is_delete”这个值被置为False的时候还是可以把数据返回给用户的。对于今天的我们已经不用再对内存斤斤计较的时候,这些保留的数据说不定还是能发挥其潜在的价值。
(2)可以设置的参数种类
- RESTRICT、NO ACTION:房东数据被删除,会阻止删除。NO ACTION在MySQL中,与RESTR功能一致。
- CASCADE: 房东的数据被删除,房子的数据也会被删除。
- SET NULL: 房东数据被删除,房子的数据会设置为空。
三、实现各种情况
(1)先添加元素
landlord1 = Landlord(name='Jonathan')
landlord2 = Landlord(name='Joestar')
session.add(landlord1)
session.add(landlord2)
session.commit()
hous1 = House(address='England ', id=1)# 有错误应该是llid
hous2 = House(address='England ', id=2)# 有错误应该是llid
session.add(hous1)
session.add(hous2)
session.commit()
(2)设置为‘RESTRICT’的情况:
在foreignkey中加上参数ondelete=‘参数值’。
llid = Column(Integer, ForeignKey('landlord.id', ondelete='RESTRICT'))
删除第一条数据后:
我竟然删掉了?查看数据库看见foreignkey没有值,推断关联的时候出现问题应该更正成llid。
hous1 = House(address='England ', llid=1)
hous2 = House(address='England ', llid=2)
结果:报错禁止删除。
(3)设置为‘SET NULL’的情况:
llid = Column(Integer, ForeignKey('landlord.id', ondelete='SET NULL'))
删除第一条数据:DELETE FROM landlord WHERE id=1;
结果:显示llid的值被置为空。
(4)设置为‘CASCADE’的情况:
llid = Column(Integer, ForeignKey('landlord.id', ondelete='CASCADE'))
删除第一条数据:DELETE FROM landlord WHERE id=1;
结果:两个数据都删除了。