Module: relationships
This module provides support for defining relationships between your Elixir entities. Elixir supports the following types of relationships: belongs_to, has_one, has_many and has_and_belongs_to_many.
The first argument to all those statements is the name of the relationship, the second is the 'kind' of object you are relating to (it is usually given using the of_kind keyword).
Additionally, if you want a bidirectionnal relationship, you should define the inverse relationship on the other entity explicitly (as opposed to how SQLAlchemy's backrefs are defined). In non-ambiguous situations, Elixir will match relationships together automatically. If there are several relationships of the same type between two entities, Elixir is not able to determine which relationship is the inverse of which, so you have to disambiguate the situation by giving the name of the inverse relationship in the inverse keyword argument.
Following these "common" arguments, any number of additional keyword arguments can be specified for advanced behavior. The keyword arguments are passed on to the SQLAlchemy relation function. Please refer to the SQLAlchemy relation function's documentation for further detail about which keyword arguments are supported, but you should keep in mind, the following keyword arguments are taken care of by Elixir and should not be used: uselist, remote_side, secondary, primaryjoin and secondaryjoin.
Also, as for standard SQLAlchemy relations, the order_by keyword argument can be used to sort the results given by accessing a relation field (this only makes sense for has_many and has_and_belongs_to_many relationships). The value of that argument is different though: you can either use a string or a list of strings, each corresponding to the name of a field in the target entity. These field names can optionally be prefixed by a minus (for descending order).
Here is a detailed explanation of each relation type:
belongs_to
Describes the child's side of a parent-child relationship. For example, a Pet object may belong to its owner, who is a Person. This could be expressed like so:
class Pet(Entity): belongs_to('owner', of_kind='Person')
Behind the scene, assuming the primary key of the Person entity is an integer column named id, the belongs_to relationship will automatically add an integer column named owner_id to the entity, with a foreign key referencing the id column of the Person entity.
In addition to the keyword arguments inherited from SQLAlchemy's relation function, belongs_to relationships accept the following optional arguments which will be directed to the created column:
| Option Name | Description |
|---|---|
| colname | Specify a custom column name. |
| required | Specify whether or not this field can be set to None (left without a value). Defaults to False, unless the field is a primary key. |
| column_kwargs | A dictionary holding any other keyword argument you might want to pass to the Column. |
The following optional arguments are also supported to customize the ForeignKeyConstraint that is created:
| Option Name | Description |
|---|---|
| use_alter | If True, SQLAlchemy will add the constraint in a second SQL statement (as opposed to within the create table statement). This permits to define tables with a circular foreign key dependency between them. |
| ondelete | Value for the foreign key constraint ondelete clause. May be one of: cascade, restrict, set null, or set default. |
| constraint_kwargs | A dictionary holding any other keyword argument you might want to pass to the Constraint. |
has_one
Describes the parent's side of a parent-child relationship when there is only one child. For example, a Car object has one gear stick, which is represented as a GearStick object. This could be expressed like so:
class Car(Entity): has_one('gear_stick', of_kind='GearStick', inverse='car') class GearStick(Entity): belongs_to('car', of_kind='Car')
Note that an has_one relationship cannot exist without a corresponding belongs_to relationship in the other way. This is because the has_one relationship needs the foreign_key created by the belongs_to relationship.
has_many
Describes the parent's side of a parent-child relationship when there can be several children. For example, a Person object has many children, each of them being a Person. This could be expressed like so:
class Person(Entity): belongs_to('parent', of_kind='Person') has_many('children', of_kind='Person')
Note that an has_many relationship cannot exist without a corresponding belongs_to relationship in the other way. This is because the has_many relationship needs the foreign key created by the belongs_to relationship.
has_and_belongs_to_many
Describes a relationship in which one kind of entity can be related to several objects of the other kind but the objects of that other kind can be related to several objects of the first kind. For example, an Article can have several tags, but the same Tag can be used on several articles.
class Article(Entity): has_and_belongs_to_many('tags', of_kind='Tag') class Tag(Entity): has_and_belongs_to_many('articles', of_kind='Article')
Behind the scene, the has_and_belongs_to_many relationship will automatically create an intermediate table to host its data.
Note that you don't necessarily need to define the inverse relationship. In our example, even though we want tags to be usable on several articles, we might not be interested in which articles correspond to a particular tag. In that case, we could have omitted the Tag side of the relationship.
If the entity containg your has_and_belongs_to_many relationship is autoloaded, you must specify at least one of either the remote_side or local_side argument.
In addition to the order_by keyword argument, and the other keyword arguments inherited from SQLAlchemy, has_and_belongs_to_many relationships accept the following optional (keyword) arguments:
| Option Name | Description |
|---|---|
| tablename | Specify a custom name for the intermediary table. This can be used both when the tables needs to be created and when the table is autoloaded/reflected from the database. |
| remote_side | A column name or list of column names specifying which column(s) in the intermediary table are used for the "remote" part of a self-referential relationship. This argument has an effect only when your entities are autoloaded. |
| local_side | A column name or list of column names specifying which column(s) in the intermediary table are used for the "local" part of a self-referential relationship. This argument has an effect only when your entities are autoloaded. |
