SQLAlchemy_定義(一對一/一對多/多對多)關系


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.

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM