SQLALchemy之外键


表关系

  • 表之间的关系存在三种:一对一、一对多、多对多。
  • SQLAlchemy中的ORM也可以模拟这三种关系。
  • 一对一其实在SQLAlchemy中底层是通过一对多的方式模拟的。

SQLAlchemy创建外键

  • 从表中增加一个字段,指定这个字段外键的是哪个表的哪个字段
  • 从表中外键的字段,必须和主表的主键字段类型保持一致。

# 主表(user) / 从表(news) 

class User(Base): 
	__tablename__ = 'user' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	uname = Column(String(50),nullable=False) 

	def __repr__(self): 
		return "" % self.uname 

class News(Base): 
	__tablename__ = 'news' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	title = Column(String(50),nullable=False) 
	content = Column(Text,nullable=False) 
	uid = Column(Integer,ForeignKey("user.id")) 

	def __repr__(self): 
	return "" % (self.title,self.content)
	

外键约束

  1. RESTRICT:若子表中有父表对应的关联数据,删除父表对应数据,会阻止删除。默认项
  2. NO ACTION:在MySQL中,同RESTRICT
  3. CASCADE:级联删除。
  4. SET NULL:父表对应数据被删除,子表对应数据项会设置为NULL

from sqlalchemy import create_engine,Column,Integer,Float,Boolean,DECIMAL,Enum,\
	 Date,DateTime,Time,String,Text,func,or_,and_,ForeignKey  
from sqlalchemy.dialects.mysql import LONGTEXT 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker 
import random 

HOSTNAME = '127.0.0.1' 
PORT = '3306' 
DATABASE = 'first_sqlalchemy' 
USERNAME = 'root' 
PASSWORD = 'root' 
DB_URI ="mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".\
	format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE)

engine = create_engine(DB_URI) 

Base = declarative_base(engine) 

session = sessionmaker(engine)() 

# 父表/从表 # user/news 
class User(Base): 
	__tablename__ = 'user' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	uname = Column(String(50),nullable=False) 

	def __repr__(self): 
		return "" % self.uname 

class News(Base): 
	__tablename__ = 'news' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	title = Column(String(50),nullable=False) 
	content = Column(Text,nullable=False) 
	# uid = Column(Integer,ForeignKey("user.id",ondelete='RESTRICT')) 
	# uid = Column(Integer,ForeignKey("user.id",ondelete='NO ACTION')) 
	# uid = Column(Integer,ForeignKey("user.id",ondelete='CASCADE')) 
	uid = Column(Integer,ForeignKey("user.id",ondelete='SET NULL')) 

	def __repr__(self): 
		return "" % (self.title,self.content)

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

user = User(uname='wukong') 

session.add(user) 
session.commit() 

news1= News(title='AAA',content='123',uid=1) 
news2= News(title='BBB',content='456',uid=1) 

session.add_all([news1,news2]) 
session.commit()


一对多关系

  • SQLAlchemy提供了一个relationship,这个类可以定义属性
  • 以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问到。
  • 通常会把它通过反向声明的方式写在“”的那一方
  • 通过backref来指定反向访问的属性名称

#主表(user)/从表(news) 
#一对多关系

class User(Base): 
	__tablename__ = 'user' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	uname = Column(String(50),nullable=False) 

	def __repr__(self): 
		return "" % self.uname 

class News(Base): 
	__tablename__ = 'news' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	title = Column(String(50),nullable=False) 
	content = Column(Text,nullable=False) 

	#外键 
	uid = Column(Integer,ForeignKey("user.id")) 

	#正向
	author = relationship("User") 

	#正向 和 反向在一起 表明两个模型之间的关系 
	author = relationship("User",backref="newss") 

	def __repr__(self): 
		return "" % (self.title,self.content) 


一对一关系

  • sqlalchemy中,要将两个模型映射成一对一的关系,应在模型中,指定引用时,传递一个uselist=False这个参数
  • 作用是模型引用模型时,不再是一个列表,而是一个对象
  • 需要导入backref
  • from sqlalchemy.orm import backref

class User(Base): 
	__tablename__ = 'user' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	uname = Column(String(50),nullable=False) 

class UserExtend(Base): 
	__tablename__ = 'user_extend' 
	id = Column(Integer, primary_key=True, autoincrement=True) 
	school = Column(String(50)) 

	uid = Column(Integer,ForeignKey("user.id")) 
	
	user = relationship("User",backref=backref("extend",uselist=False))


多对多关系

  • 多对多的关系需要通过一张中间表来绑定他们之间的关系。
  • 先把两个需要做多对多的模型定义出来
  • 使用Table定义一个中间表,中间表一般就是包含两个模型的外键字段,并让它们两个来作为一复合主键
  • 在两个需要做多对多的模型中随便选择一个模型,定义一个relationship属性,来绑定三者之间的关系
  • 在使用relationship的时候,需要传入一个secondary=中间表对象名

#表3 中间表 
news_tag = Table( "news_tag", Base.metadata,
	Column("news_id",Integer,ForeignKey("news.id"),primary_key=True),
	Column("tag_id",Integer,ForeignKey("tag.id"),primary_key=True) ) 

#表1 
class News(Base): 
	__tablename__ = 'news' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	title = Column(String(50),nullable=False) 

	def __repr__(self): 
		return "" % self.title 

#表2 
class Tag(Base): 
	__tablename__ = 'tag' 
	id = Column(Integer, primary_key=True, autoincrement=True) 
	name = Column(String(50), nullable=False) 
	# 产生关系
	newss = relationship("News",backref="tags",secondary=news_tag) 
	def __repr__(self): 
		return "" % self.name 


删除数据

  • ORM层面删除数据,会无视mysql级别的外键约束。

  • 直接会将对应的数据删除,然后将从表中的那个外键设置为NULL

  • 如果想要避免这种行为,应该将从表中的外键的nullable=False


class User(Base): 
	__tablename__ = 'user' 
	id = Column(Integer,primary_key=True,autoincrement=True) 
	uname = Column(String(50),nullable=False) 

class News(Base): 
	__tablename__ = 'news' 
	id = Column(Integer, primary_key=True, autoincrement=True) 
	title = Column(String(50),nullable=False) 
	uid = Column(Integer,ForeignKey("user.id"),nullable=False) 
	author = relationship("User",backref='newss') 


关键字参数cascade

  • SQLAlchemy,只要将一个数据添加到session中,和它相关联的数据都可以一起存入到数据库中了。
  • 其实是通过relationship的时候,有一个关键字参数cascade.

cascade属性值

  • save-update:默认选项。在添加一条数据的时候,会把其他和他相关联的数据都添加到数据库中。这种行为就是save-update属性影响的。
  • delete:表示当删除某一个模型中的数据的时候,是否也删掉使用relationship和他关联的数据。
  • delete-orphan:表示当对一个ORM对象解除了父表中的关联对象的时候,自己便会被删除掉。当然如果父表中的数据被删除,自己也会被删除。这个选项只能用在一对多上,并且还需要在子模型中的relationship中,增加一个single_parent=True的参数。
  • merge:默认选项。当在使用session.merge,合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操作。
  • expunge:移除操作的时候,会将相关联的对象也进行移除。这个操作只是从session中移除,并不会真正的从数据库中删除。
  • all:是对save-update, merge, refresh-expire, expunge, delete几种的缩写。

你可能感兴趣的:(SQLALchemy之外键)