SQLAlchemy
中的ORM
也可以模拟这三种关系。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)
RESTRICT
:若子表
中有父表
对应的关联数据,删除父表对应数据,会阻止删除。默认项
NO ACTION
:在MySQL
中,同RESTRICT
。CASCADE
:级联删除。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')
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
几种的缩写。