当前位置: 首页 > 工具软件 > Elixir ORM > 使用案例 >

elixir的关系定义

唐高卓
2023-12-01

elixir的版本为0.7.1,依赖的sqlalchemy版本为0.7.8,转载请注明出处。

    Elixir是基于python界有名的ORM库SQLAlchemy做的封装。而且是轻量级的封装,它提供了更简单的方式来创建Python类并直接映射到关系数据库表(即通常所说的Active Record设计模式),类似于Django中的ORM。

下面为relationships.py源码中对四种关系的说明。

  这个模块为在Elixir的实体之间定义映射关系提供支持。Elixir目前提供两种语法来定义关系:

  默认的是使用基于属性的语法,它提供有如下几种关系:

    ManyToOne, OneToMany, OneToOne and ManyToMany

  另外一种就是基于领域特定语言(Domain Specific Language)的语法,相应提供有如下几种关系:

    belongs_to, has_many, has_one and has_and_belongs_to_many

======================

基于属性的语法

======================  

  这些负责建立关系映射的方法接受的第一个参数,是需要被关系到的实体类(entity class)的类名。

  在这个强制参数之后,其他参数都用来描述一些特定高级行为,这可以通过查看每个关系类型的特定关键参数列表来了解。在这里需要指出的是,所有在下面文档里提到的那些不是专门由Elixir负责处理的参数都将被传递给SQLAlchemyrelation函数处理,请查看SQLAlchemySQLAlchemyrelation函数的文档,以了解更多这些参数的详细内容<http://www.sqlalchemy.org/docs/05/reference/orm/mapping.html#sqlalchemy.orm.relation>

  记住下面这些参数是Elixir自动生成的,在你没必要重定义Elixir提供的这些参数的值的时候,切记不要使用这些参数:

  `uselist`,`remote_side`, `secondary`, `primaryjoin` and `secondaryjoin`

  另外,你在定义一个双向关系的时候,你必须在另外那个实体明确定义一个反向关系(inverse),这正好与SQLAlchemybackrefs的定义方式不同。在没有歧义的情况下,Elixir将自动匹配实体之间定义的关系。但如果同一类型多个的关系在两个实体分别都作定义,那么Elixir将无法确定那个关系是关系反向的一方。所以,你需要为另外那个实体中定义的关系的inverse参数提供被反向的关系名,以明确一个反向关系。

  下面是每种关系的详解:

`ManyToOne`

-----------

  在子实体的一边,描述一个有多个子实体的父子关系(parent-child relationship)

  比如,描述某个Pet类实体的所有者是一个Person类实体,那么可以表达为:

    class Pet(Entity):
        owner = ManyToOne('Person')

  在这里,我们背后假设了Person实体的主键是一个叫`id`的整型列,那么定义的ManyToOne关系将会在Pet实体的中自动添加一个名为`owner_id`的整型列。

  此外,除了从SQLAlchemyrelation函数关键字继承的参数,ManyToOne关系还支持下面这些可选参数,他们将指导新列的创建过程:

可选参数名

描述

``colname``

为外键列指定一个自定名称。

这个参数接受一个字符串或者一个字符串列表,字符串表的字符数必须与被关联到的实体的主键的列数相等。

如果没有使用这个参数,那么新建的外键列的名称的将采用在options.FKCOL_NAMEFORMAT中定义的默认方式,形式为:"%(relname)s_%(key)s",这里的'relname'是实体所定义的ManyToOne关系字段的名称,'key'是被关联到的目标实体的主键列的名称。因此,在上面的例子中,外键列的名称当然就是"owner_id"

``required``

指定这个字段是否可以为空(即不给定该列的值),除非为所在实体的主键,一般默认值为`False`

``primary_key``

指定该关系所创建的列是否作为所在实体的主键列。

``column_kwargs``

保存一个需要传递给列的其它参数名及其值的字典。

``target_column``

指定被关联到的目标实体中的作为该关系所在实体的外键的列。

默认使用被关联到的实体的主键列。

 

下面的可选参数用于自定义所创建的外键约束(ForeignKeyConstraint)

可选参数名

描述

``use_alter``

如果为True,sqlalchemy将会在第二个SQL语句中添加这个约束(这与创建表的语句不同),这允许在表间定义一个环状外键依赖关系。

``ondelete``

外键约束ondelete条款。

可以是以下之一: `cascade`, `restrict`, `set null`或者`set default`

``onupdate``

外键约束onupdate条款。

可以是以下之一: `cascade`, `restrict`, `set null`或者`set default`

``column_kwargs``

一个保存有其余需要传递给约束的参数名及其值的字典。

 

  在某些时候,你可能想要明确地声明外键列,而不是自动生成,可能的原因如:你想要精确的声明外键列的相关参数,以及使用column_kwargs参数使你的代码变得有些丑,或者因为你的外键列名与你的属性名相冲突(这种情况会抛出一个异常)。这时,你可以使用`field`参数来指定一个已经声明的字段来作为外键。比如:

     class Pet(Entity):
        owner_id = Field(Integer, colname='owner')
        owner = ManyToOne('Person', field=owner_id) 

可选参数名       

描述

``field``

指定一个预先声明的字段来作为外键列,这个参数与colnamecolumn_kwargs不要同时使用。

 

  此外,Elixir支持使用belongs_to语句来代替ManyToOne,这是一个基于DSL的语法形式而已。

`OneToMany`

-----------

  在父实体的一边,描述一个有多个子实体的父子关系(parent-child relationship)

  比如,如果要描述一个Person类实体有多个children类实体,而每一个children类实体又都是一个Person类实体,可以表达如下:

    class Person(Entity):
        parent = ManyToOne('Person')
        children = OneToMany('Person')

  注意,`OneToMany`关系不能在没有定义与之对应的`ManyToOne`关系的情况下存在,因为`OneToMany`关系需要使用由`ManyToOne`关系创建的外键(注:他们不一定像上面这样总是同时定义在一个实体种,大多数时候还是在两个不同的实体中定义)。

  此外,除了从SQLAlchemyrelation函数关键字继承的参数,`OneToMany`关系支持下面这些可选参数:

可选参数名

描述

``order_by``

指定对通过被关联到的实体,对用来定义ManyToOne关系的字段的访问结果的集合,将依据哪个字段进行排序。注意,这个排序只在从数据库加载实体对象集合的时侯被应用,后来追加入实体集合的对象,将不会在运行于内存中的集合里得到重新排序。

这个参数接受一个字符串或者一个字符串列表,字符串列表中的每个字符串都与结果集合中实体的字段相对应。这些字段名前都可以加上一个可选的负号前缀代表降序排列。

``filter``

为关系指定一个过滤准则(作为一个条款),这个准则可以用由Elixir为关系创建的`and_`方式连接两个普通过滤准则(类似于主键的连接)。例如:boston_addresses = OneToMany('Address', filter=Address.city == 'Boston')

|

  此外,Elixir支持使用has_many语句来代替OneToMany,这是一个基于DSL的语法形式。

`OneToOne`

----------

  在父实体的一边,定义一个仅有一个子实体时父子关系(parent-child relationship)

  比如一个`Car`对象只有一个变速操纵杆(gear stick),用`GearStick`对象代表。那么这个关系可以表述如下:

    class Car(Entity):
        gear_stick = OneToOne('GearStick', inverse='car')

    class GearStick(Entity):
        car = ManyToOne('Car')

  注意`OneToOne`关系不能在没有与之对应的`ManyToOne`关系的情况下存在,因为`OneToOne`关系需要使用由`ManyToOne`关系创建的外键。

  此外,Elixir支持使用has_one语句来代替OneToOne,这是一个基于DSL的语法形式。

`ManyToMany`

------------

  描述如下关系:一个类型的实体可以是关联到多个另一个类型的对象,而另一个类型的对象也可以关联到多个这个类型的对象。

  比如,一个`Article`对象可以个多个标签(tags),而一个标签可以被使用到多个文章(articles)对象中。这可以表达如下:

    class Article(Entity):
        tags = ManyToMany('Tag')

    class Tag(Entity):
        articles = ManyToMany('Article')

  在这个`ManyToMany`关系背后,将会自动创建一个中间表格来为这些数据提供支持。

  注意,你在其实不必总是定义一个反向关系。比如在我们这个例子里,尽管我们想让标签在多个文章中使用,但我们也许不会对某个特定标签究竟在哪些文章中使用感兴趣。因此,我们可以省略关系`Tag`实体的一边的定义。

  如果你的`ManyToMany`是一个自引用的实体,当然包含它的实体已被自动加载(并且你不想手动指定primaryjoinsecondaryjoin参数),那么你还必须指定`remote_colname``local_colname`的其中之一。

  此外,除了从SQLAlchemyrelation函数关键字继承的参数,`OneToMany`关系支持下面这些可选参数:

可选参数名

描述

``tablename``

为中间表指定一个表名。无论是新创建,还是从数据库自动加载/反射的,均可以使用这个参数。

如果不使用这个参数,则Elixir将根据关系中两个实体的表格自动生成一个表名。如果存在这个参数,则根据这个参数生成表名。

尽管这是一个可选参数,但是在你不能准确的知道自动生成的表名的情况下,建议你最好能使用这个参数。

``schema``

为中间表格指定一个模式(schema)

无论是新创建,还是从数据库自动加载/反射的,均可以使用这个参数。

``remote_colname``

一个引用自远程(或者说被关联到的)实体表的,用来指定中间表列名称的字符串或字符串列表。

``local_colname``

一个引用自远程(或者说被关联到的)实体表的,用来指定中间表列名称的字符串或字符串列表。

``table``

使用一个已创建的表。

如果使用这个参数,Elixir将使用一个给定的表,而不再为关系自动生成一个表。

``order_by``

指定对通过被关联到的实体,访问定义ManyToMany关系的字段的结果的集合,将依据哪个字段进行排序。注意,这个排序只在从数据库加载实体对象集合的时侯被应用,后来追加入实体集合的对象,将不会在运行于内存中的集合里得到重新排序。                                         

这个参数接受一个字符串或者一个字符串列表,字符串表中的每个字符串都与结果集合中实体的字段相对应。这些字段名前都可以加上一个可选的负号前缀代表降序排列。

``ondelete``

外键约束ondelete条款。

可以是以下之一: `cascade`, `restrict`, `set null`或者`set default`

``onupdate``

外键约束onupdate条款。

可以是以下之一: `cascade`, `restrict`, `set null`或者`set default`

``column_kwargs``

一个保存有其余需要传递给约束的参数名及其值的字典。

``column_format``

将被弃除的参数。一个用来代替为映射表格里的列指定名称的格式字符串。默认值定义在`elixir.options.M2MCOL_NAMEFORMAT`中。

你可以为格式字符串传递`tablename`, `key`,  `entity`等参数。

 

================

DSL-based 语法

================

  DSL语句提供一个在实体间定义各种关系的替代方式。这些定义语句的第一个参数是关系的名称。第二个参数是被关联的对象的类型('kind'),一般使用`of_kind`作为关键字。

 `belongs_to`

------------

  belongs_to语句是ManyToOne关系的DSL语法等价形式。它支持的参数与ManyToOne相同。eg:

    class Pet(Entity):
        belongs_to('feeder', of_kind='Person')
        belongs_to('owner', of_kind='Person', colname="owner_id")

`has_many`

----------

  has_many语句是OneToMany关系的DSL语法等价形式。它支持的参数与OneToMany相同。eg: 

    class Person(Entity):
        belongs_to('parent', of_kind='Person')
        has_many('children', of_kind='Person')  

  这里还有一个仅需要多使用两个参数`through``via`的复杂形式,即可替代`has_and_belongs_to_many`语句,定义一个many-to-many关系。下面有一个例子:

    class Person(Entity):
        has_field('name', Unicode)
        has_many('assignments', of_kind='Assignment')
        has_many('projects', through='assignments', via='project')

    class Assignment(Entity):
        has_field('start_date', DateTime)
        belongs_to('person', of_kind='Person')
        belongs_to('project', of_kind='Project')

    class Project(Entity):
        has_field('title', Unicode)
        has_many('assignments', of_kind='Assignment')

  在上面的例子中,一个Person实体通过(through)中间关系对象Assignmentproject属性(via='project')拥有了多个Project,即实现了对projects关系的定义。

`has_one`

---------

   has_one语句是OneToOne关系的DSL语法等价形式。它支持的参数与OneToOne相同。

    class Car(Entity):
        has_one('gear_stick', of_kind='GearStick', inverse='car')

    class GearStick(Entity):
        belongs_to('car', of_kind='Car')

`has_and_belongs_to_many`

-------------------------

  has_and_belongs_to_many语句是ManyToMany关系的DSL语法等价形式。它支持的参数与ManyToMany相同。eg

 

    class Article(Entity):
        has_and_belongs_to_many('tags', of_kind='Tag')

    class Tag(Entity):
        has_and_belongs_to_many('articles', of_kind='Article')
 类似资料: