SQLAlchemy_定义(一对一/一对多/多对多)关系

时间:2021-10-27 10:28:17

Basic Relationship Patterns

基本关系模式 
The imports used for each of the following sections is as follows: 
下列的 import 语句,应用到接下来所有的代章节中:

from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

One To Many

A one to many relationship places a foreign key on the child table referencing the parent. 
表示一对多的关系时,在子表类中通过 foreign key (外键)引用父表类。 
relationship() is then specified on the parent, as referencing a collection of items represented by the child: 
然后,在父表类中通过 relationship() 方法来引用子表的类:

class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child")
# 在父表类中通过 relationship() 方法来引用子表的类集合 class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
# 在子表类中通过 foreign key (外键)引用父表的参考字段

To establish a bidirectional relationship in one-to-many, where the “reverse” side is a many to one, 
一对多的关系中建立双向的关系,这样的话在对方看来这就是一个多对一的关系, 
specify an additional relationship() and connect the two using the relationship.back_populates parameter: 
在子表类中附加一个 relationship() 方法,并且在双方的 relationship() 方法中使用 relationship.back_populates 方法参数:

class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child", back_populates="parent") class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship("Parent", back_populates="children")
# 子表类中附加一个 relationship() 方法
# 并且在(父)子表类的 relationship() 方法中使用 relationship.back_populates 参数

Child will get a parent attribute with many-to-one semantics. 
这样的话子表将会在多对一的关系中获得父表的属性

Alternatively, the backref option may be used on a single relationship() instead of using back_populates: 
或者,可以在单一的 relationship() 方法中使用 backref 参数来代替 back_populates 参数:

class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child", backref="parent") class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))

One To One

One To One is essentially a bidirectional relationship with a scalar attribute on both sides. 
一对一是两张表之间本质上的双向关系。 
To achieve this, the uselist flag indicates the placement of a scalar attribute instead of a collection on the “many” side of the relationship. 
要做到这一点,只需要在一对多关系基础上的父表中使用 uselist 参数来表示。 
To convert one-to-many into one-to-one:

class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child = relationship("Child", uselist=False, back_populates="parent") class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship("Parent", back_populates="child")

To convert many-to-one into one-to-one:

class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", back_populates="parent") class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent = relationship("Parent", back_populates="child", uselist=False)

As always, the relationship.backref and backref() functions may be used in lieu of the relationship.back_populates approach; to specifyuselist on a backref, use the backref() function:

同样的,可以使用下面这种方式:

from sqlalchemy.orm import backref

class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", backref=backref("parent", uselist=False)) class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))

Many To Many

Many to Many adds an association table between two classes. 
多对多关系会在两个类之间增加一个关联的表。 
The association table is indicated by the secondary argument to relationship()
这个关联的表在 relationship() 方法中通过 secondary 参数来表示。 
Usually, the Table uses the MetaData object associated with the declarative base class, 
通常的,这个表会通过 MetaData 对象来与声明基类关联, 
so that the ForeignKey directives can locate the remote tables with which to link: 
所以这个 ForeignKey 指令会使用链接来定位到远程的表:

定义中间表可以定义中间关系表相关的类,也可以直接通过Base.metdata生成对应关系表对象,不过基于code first准则,还是推荐将中间关系写成类。

构建第三张关系类实现多对多。

Base = declarative_base() #生成sqlorm基础类

class HostUserToGroup(Base):
__tablename__ = 'hostuser_to_group' # 表名hostuser_to_group
nid = Column(Integer, primary_key=True,autoincrement=True)
hostuser_id = Column(Integer,ForeignKey('host_user.id'),primary_key=True)# 外键关联host_user表的id字段
group_id = Column(Integer,ForeignKey('group.id'),primary_key=True) # 外键关联group表的id字段 class UserProfileToGroup(Base):
__tablename__ = 'userprofile_to_group'# 表名userprofile_to_group
nid = Column(Integer, primary_key=True,autoincrement=True)
userprofile_id = Column(Integer,ForeignKey('user_profile.id'),primary_key=True)# 外键关联user_profile表的id字段
group_id = Column(Integer,ForeignKey('group.id'),primary_key=True)# 外键关联group表的id字段 class UserProfileToHostUser(Base):
__tablename__ = 'userprofile_to_hostuser'# 表名userprofile_to_hostuser
nid = Column(Integer, primary_key=True,autoincrement=True)
userprofile_id = Column(Integer,ForeignKey('user_profile.id'),primary_key=True)# 外键关联user_profile表的id字段
hostuser_id = Column(Integer,ForeignKey('host_user.id'),primary_key=True)# 外键关联host_user表的id字段 class Host(Base):
__tablename__ = 'host' #表名host
id = Column(Integer, primary_key= True, autoincrement= True)# id字段,主键,自动增长
hostname = Column(String(64),unique= True,nullable= False)# hostname字段,唯一,不能为空
ip_addr = Column(String(64),unique= True,nullable= False)#ip_addr字段,唯一,不能为空
port = Column(Integer, default = 22) # port字段,整形,默认22
def __repr__(self):
return "<Hostobject: id=%s, hostname=%s, ip_addr=%s, port=%s>" %(self.id, self.hostname, self.ip_addr, self.port) class Group(Base):
__tablename__ = 'group' # 表名group
id = Column(Integer, primary_key = True) # id字段,主键,自动增长
name = Column(String(64), unique = True, nullable = False) # name字段,唯一,不为空
def __repr__(self):
return "<Group object: id=%s, name=%s>" %(self.id, self.name) class UserProfile(Base):
__tablename__ = 'user_profile' # 表名user_profile
id = Column(Integer, primary_key = True) # id字段,主键,自动增长
username = Column(String(64), unique = True, nullable = False) # username字段,唯一,不为空
password = Column(String(255), nullable = False) # password字段,不为空
hostusers = relationship('HostUser', secondary = UserProfileToHostUser.__tablename__, backref = 'user_profiles') # 多对多关联HostUser表类(注意不是表名),中间表类UserProfileToHostUser(注意不是表名),反向字段为user_profiles
groups = relationship('Group', secondary = UserProfileToGroup.__tablename__, backref = 'user_profiles') # 多对多关联Group表类(注意不是表名),中间表类UserProfileToGroup(注意不是表名),反向字段为user_profiles def __repr__(self):
return "<UserProfile object: id=%s, username=%s>" %(self.id, self.username) class HostUser(Base):
__tablename__ = 'host_user' # 表名host_user
id = Column(Integer, primary_key = True) # id字段,主键,自动增长
host_id = Column(Integer, ForeignKey('host.id')) # host_id,外键关联host表的id字段
AuthTypes = [
(u'ssh-password', u'SSH/Password'),
(u'ssh-key', u'SSH/Key'),
] # 选项列表
auth_type = Column(ChoiceType(AuthTypes)) # auth_type字段,只能是选项列表里规定的值
username = Column(String(64), nullable = True) # username字段,不为空
password = Column(String(255)) # password字段
host = relationship('Host', backref = 'host_users')
groups = relationship('Group', secondary = HostUserToGroup.__tablename__, backref = 'host_users') # 多对多关联Group表类(注意不是表名),中间表类HostUserToGroup(注意不是表名),反向字段为host_users
__table_args = (UniqueConstraint('host_id', 'username', name = '_host_username_uc')) # host_id和username组成联合唯一约束 def __repr__(self):
return "<HostUser object: id=%s, host_id=%s, username=%s>" %(self.id, self.host_id, self.username)

构建关系表实现多对多实例如下:

from sqlalchemy import create_engine,and_,or_,func,Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String,\
ForeignKey, UniqueConstraint, DateTime
from sqlalchemy.orm import sessionmaker,relationship
from sqlalchemy_utils import ChoiceType,PasswordType
from datetime import datetime Base = declarative_base() #生成sqlorm基础类 HostUserToGroup = Table('hostuser_to_group', Base.metadata, # 表名hostuser_to_group
Column('hostuser_id', ForeignKey('host_user.id'), primary_key = True), # 外键关联host_user表的id字段
Column('group_id', ForeignKey('group.id'), primary_key = True), # 外键关联group表的id字段
) UserProfileToGroup = Table('userprofile_to_group', Base.metadata, # 表名userprofile_to_group
Column('userprofile_id', ForeignKey('user_profile.id'), primary_key = True), # 外键关联user_profile表的id字段
Column('group_id', ForeignKey('group.id'), primary_key = True), # 外键关联group表的id字段
) UserProfileToHostUser = Table('userprofile_to_hostuser', Base.metadata, # 表名userprofile_to_hostuser
Column('userprofile_id', ForeignKey('user_profile.id'), primary_key = True), # 外键关联user_profile表的id字段
Column('hostuser_id', ForeignKey('host_user.id'), primary_key = True), # 外键关联host_user表的id字段
) class Host(Base):
__tablename__ = 'host' #表名host
id = Column(Integer, primary_key= True, autoincrement= True)# id字段,主键,自动增长
hostname = Column(String(64),unique= True,nullable= False)# hostname字段,唯一,不能为空
ip_addr = Column(String(64),unique= True,nullable= False)#ip_addr字段,唯一,不能为空
port = Column(Integer, default = 22) # port字段,整形,默认22
def __repr__(self):
return "<Hostobject: id=%s, hostname=%s, ip_addr=%s, port=%s>" %(self.id, self.hostname, self.ip_addr, self.port) class Group(Base):
__tablename__ = 'group' # 表名group
id = Column(Integer, primary_key = True) # id字段,主键,自动增长
name = Column(String(64), unique = True, nullable = False) # name字段,唯一,不为空
def __repr__(self):
return "<Group object: id=%s, name=%s>" %(self.id, self.name) class UserProfile(Base):
__tablename__ = 'user_profile' # 表名user_profile
id = Column(Integer, primary_key = True) # id字段,主键,自动增长
username = Column(String(64), unique = True, nullable = False) # username字段,唯一,不为空
password = Column(String(255), nullable = False) # password字段,不为空
hostusers = relationship('HostUser', secondary = UserProfileToHostUser, backref = 'user_profiles') # 多对多关联HostUser表类(注意不是表名),中间表类UserProfileToHostUser(注意不是表名),反向字段为user_profiles
groups = relationship('Group', secondary = UserProfileToGroup, backref = 'user_profiles') # 多对多关联Group表类(注意不是表名),中间表类UserProfileToGroup(注意不是表名),反向字段为user_profiles def __repr__(self):
return "<UserProfile object: id=%s, username=%s>" %(self.id, self.username) class HostUser(Base):
__tablename__ = 'host_user' # 表名host_user
id = Column(Integer, primary_key = True) # id字段,主键,自动增长
host_id = Column(Integer, ForeignKey('host.id')) # host_id,外键关联host表的id字段
AuthTypes = [
(u'ssh-password', u'SSH/Password'),
(u'ssh-key', u'SSH/Key'),
] # 选项列表
auth_type = Column(ChoiceType(AuthTypes)) # auth_type字段,只能是选项列表里规定的值
username = Column(String(64), nullable = True) # username字段,不为空
password = Column(String(255)) # password字段
host = relationship('Host', backref = 'host_users')
groups = relationship('Group', secondary = HostUserToGroup, backref = 'host_users') # 多对多关联Group表类(注意不是表名),中间表类HostUserToGroup(注意不是表名),反向字段为host_users
__table_args = (UniqueConstraint('host_id', 'username', name = '_host_username_uc')) # host_id和username组成联合唯一约束 def __repr__(self):
return "<HostUser object: id=%s, host_id=%s, username=%s>" %(self.id, self.host_id, self.username)

Linking Relationships with Backref

简单来说, relationship函数是sqlalchemy对关系之间提供的一种便利的调用方式, backref参数则对关系提供反向引用的声明。

The backref keyword argument was first introduced in Object Relational Tutorial, and has been mentioned through- out many of the examples here. What does it actually do ? Let’s start with the canonical User and Address scenario:

from sqlalchemy import Integer, ForeignKey, String, Column from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True) name = Column(String)
addresses = relationship("Address", backref="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
email = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))

The above configuration establishes a collection of Address objects on User called User.addresses. It also establishes a .user attribute on Address which will refer to the parent User object.

In fact, the backref keyword is only a common shortcut for placing a second relationship() onto the Address mapping, including the establishment of an event listener on both sides which will mirror attribute op- erations in both directions. The above configuration is equivalent to:

from sqlalchemy import Integer, ForeignKey, String, Column 
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True) name = Column(String)
addresses = relationship("Address", back_populates="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
email = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship("User", back_populates="addresses")

Above, we add a .user relationship to Address explicitly. On both relationships, the back_populates directive tells each relationship about the other one, indicating that they should establish “bidirectional” behavior between each other. The primary effect of this configuration is that the relationship adds event handlers to both attributes which have the behavior of “when an append or set event occurs here, set ourselves onto the incoming attribute using this particular attribute name”. The behavior is illustrated as follows. Start with a User and an Address instance. The .addresses collection is empty, and the .user attribute is None:

下面来看下backref相关的源码

def backref(name, **kwargs):
"""Create a back reference with explicit keyword arguments, which are the same arguments one can send to :func:`relationship`. Used with the ``backref`` keyword argument to :func:`relationship` in
place of a string argument, e.g.:: 'items':relationship(
SomeItem, backref=backref('parent', lazy='subquery')) .. seealso:: :ref:`relationships_backref` """ return (name, kwargs)
param backref:
indicates the string name of a property to be placed on the related
mapper's class that will handle this relationship in the other
direction. The other property will be created automatically
when the mappers are configured. Can also be passed as a
:func:`.backref` object to control the configuration of the
new relationship.

SQLAlchemy_定义(一对一/一对多/多对多)关系的更多相关文章

  1. Python进阶----表与表之间的关系&lpar;一对一&comma;一对多&comma;多对多&rpar;&comma;增删改查操作

    Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作,单表查询,多表查询 一丶表与表之间的关系 背景: ​ ​ ​  ​ ​ 由于如果只使用一张表存储所有的数据,就会操作数 ...

  2. SSAS中事实表中的数据如果因为一对多或多对多关系复制了多份,在维度上聚合的时候还是只算一份

    SSAS事实表中的数据,有时候会因为一对多或多对多关系发生复制变成多份,如下图所示: 图1 我们可以从上面图片中看到,在这个例子中,有三个事实表Fact_People_Money(此表用字段Money ...

  3. JPA实体关系映射:&commat;ManyToMany多对多关系、&commat;OneToMany&commat;ManyToOne一对多多对一关系和&commat;OneToOne的深度实例解析

    JPA实体关系映射:@ManyToMany多对多关系.@OneToMany@ManyToOne一对多多对一关系和@OneToOne的深度实例解析 今天程序中遇到的错误一 org.hibernate.A ...

  4. JPA级联(一对一 一对多 多对多)注解【实际项目中摘取的】并非自己实际应用

    下面把项目中的用户类中有个:一对一  一对多  多对多的注解对应关系列取出来用于学习      说明:项目运行正常 问题类:一对多.一对一.多对多 ============一对多 一方的设置 @One ...

  5. mybatis 一对一 一对多 多对多

    一对一 一对多 多对多

  6. day 69-70 一对一 一对多 多对一联表查询

    day 69 orm操作之表关系,多对多,多对一 多对一/一对多, 多对多{类中的定义方法} day69 1. 昨日内容回顾 1. 单表增删改查 2. 单表查询API 返回QuerySet对象的: 1 ...

  7. 使用NHibernate(7)-- 一对一 &amp&semi;&amp&semi; 一对多 &amp&semi;&amp&semi; 多对多

    1, 一对一. 对于数据量比较大的时候,考虑查询的性能,肯能会把一个对象的属性分到两个表中存放:比如用户和用户资料,经常使用的一般是Id和用户名,用户资料(学校,籍贯等)是不经常被查询的,所以就会分成 ...

  8. JPA 一对一 一对多 多对一 多对多配置

    1 JPA概述 1.1 JPA是什么 JPA (Java Persistence API) Java持久化API.是一套Sun公司 Java官方制定的ORM 方案,是规范,是标准 ,sun公司自己并没 ...

  9. hibernate中一对多多对一关系设计的理解

    1.单向多对一和双向多对一的区别? 只需要从一方获取另一方的数据时 就使用单向关联双方都需要获取对方数据时 就使用双向关系 部门--人员 使用人员时如果只需要获取对应部门信息(user.getdept ...

随机推荐

  1. python 零散记录&lpar;七&rpar;(上) 面向对象 类 类的私有化

    python面向对象的三大特性: 多态,封装,继承 多态: 在不知道对象到底是什么类型.又想对其做一些操作时,就会用到多态 如 'abc'.count('a') #对字符串使用count函数返回a的数 ...

  2. Android中GridView拖拽的效果

    最 近看到联想,摩托罗拉等,手机launcher中有个效果,进入mainmenu后,里面的应用程序的图标可以拖来拖去,所以我也参照网上给的代码,写了 一个例子.还是很有趣的,实现的流畅度没有人家的那么 ...

  3. lr11 录制脚本时候,无法自动启动ie&comma;查了网上很多方法都未解决?

    解决办法是把杀毒软件.防火墙都关闭,再重新运行一次,就可以了

  4. 什么是xss盲打

    什么是xss盲打? 盲打仅仅是一种惯称的说法,就是不知道后台不知道有没有xss存在的情况下,不顾一切的输入xss代码在留言啊,feedback啊之类的地方,尽可能多的尝试xss的语句与语句的存在方式, ...

  5. js动态设置输入框字体&sol;颜色

    动态设置文本框颜色: 主要是利用javascript中的触发事件onfocus和onblur <script language="javascript" type=&quot ...

  6. Java双等号,Equals&lpar;&rpar;,HashCode&lpar;&rpar;小结

    默认情况 - 双等号==,比较的是内存地址. - equals(),默认比较的是内存地址. - hashCode(),默认返回的是object的内存地址. String中方法改写的情况 经常会遇到需要 ...

  7. git reflog

    http://www.softwhy.com/article-8573-1.html https://www.cnblogs.com/irocker/p/git-reflog.html https:/ ...

  8. webservice之wsdl

    最近项目中需要将原来的通信协议改成webservice,由于业务需要,我们需要向server端传送数据,故server方提供给我们一个.wsdl文件,内容如下: WSDL即Web Services D ...

  9. 并发基础(十) 线程局部副本ThreadLocal之正解

      本文将介绍ThreadLocal的用法,并且指出大部分人对ThreadLocal 的误区. 先来看一下ThreadLocal的API: 1.构造方法摘要 ThreadLocal(): 创建一个线程 ...

  10. Git笔记——01

    Git - 幕布 Git   教程:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b00 ...