This module provides support for defining relationships between your Elixir entities. Elixir currently supports two syntaxes to do so: the default Attribute-based syntax which supports the following types of relationships: ManyToOne, OneToMany, OneToOne and ManyToMany, as well as a DSL-based syntax which provides the following statements: belongs_to, has_many, has_one and has_and_belongs_to_many.
The first argument to all these "normal" relationship classes is the name of the class (entity) you are relating to.
Following that first mandatory argument, any number of additional keyword arguments can be specified for advanced behavior. See each relationship type for a list of their specific keyword arguments. At this point, we'll just note that all the arguments that are not specifically processed by Elixir, as mentioned in the documentation below are passed on to the SQLAlchemy relation function. So, please refer to the SQLAlchemy relation function's documentation for further detail about which keyword arguments are supported.
You should keep in mind that the following keyword arguments are automatically generated by Elixir and should not be used unless you want to override the value provided by Elixir: uselist, remote_side, secondary, primaryjoin and secondaryjoin.
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.
Here is a detailed explanation of each relation type:
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): owner = ManyToOne('Person')
Behind the scene, assuming the primary key of the Person entity is an integer column named id, the ManyToOne 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, ManyToOne relationships accept the following optional arguments which will be directed to the created column:
|colname||Specify a custom name for the foreign key column(s). This argument accepts either a single string or a list of strings. The number of strings passed must match the number of primary key columns of the target entity. If this argument is not used, the name of the column(s) is generated with the pattern defined in options.FKCOL_NAMEFORMAT, which is, by default: "%(relname)s_%(key)s", where relname is the name of the ManyToOne relationship, and 'key' is the name (key) of the primary column in the target entity. That's with, in the above Pet/owner example, the name of the column would be: "owner_id".|
|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.|
|primary_key||Specify whether or not the column(s) created by this relationship should act as a primary_key. Defaults to False.|
|column_kwargs||A dictionary holding any other keyword argument you might want to pass to the Column.|
|target_column||Name (or list of names) of the target column(s). If this argument is not specified, the target entity primary key column(s) are used.|
The following optional arguments are also supported to customize the ForeignKeyConstraint that is created:
|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.|
|onupdate||Value for the foreign key constraint onupdate 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.|
In some cases, you may want to declare the foreign key column explicitly, instead of letting it be generated automatically. There are several reasons to that: it could be because you want to declare it with precise arguments and using column_kwargs makes your code ugly, or because the name of your column conflicts with the property name (in which case an error is thrown). In those cases, you can use the field argument to specify an already-declared field to be used for the foreign key column.
For example, for the Pet example above, if you want the database column (holding the foreign key) to be called 'owner', one should use the field parameter to specify the field manually.
class Pet(Entity): owner_id = Field(Integer, colname='owner') owner = ManyToOne('Person', field=owner_id)
|field||Specify the previously-declared field to be used for the foreign key column. Use of this parameter is mutually exclusive with the colname and column_kwargs arguments.|
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): parent = ManyToOne('Person') children = OneToMany('Person')
Note that a OneToMany relationship cannot exist without a corresponding ManyToOne relationship in the other way. This is because the OneToMany relationship needs the foreign key created by the ManyToOne relationship.
In addition to keyword arguments inherited from SQLAlchemy, OneToMany relationships accept the following optional (keyword) arguments:
|order_by||Specify which field(s) should be used to sort the results given by accessing the relation field. Note that this sort order is only applied when loading objects from the database. Objects appended to the collection afterwards are not re-sorted in-memory on the fly. This argument accepts either 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).|
Specify a filter criterion (as a clause element) for this relationship. This criterion will be and_'ed with the normal join criterion (primaryjoin) generated by Elixir for the relationship. For example: boston_addresses =
OneToMany('Address', filter=Address.city == 'Boston')
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): gear_stick = OneToOne('GearStick', inverse='car') class GearStick(Entity): car = ManyToOne('Car')
Note that a OneToOne relationship cannot exist without a corresponding ManyToOne relationship in the other way. This is because the OneToOne relationship needs the foreign_key created by the ManyToOne relationship.
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): tags = ManyToMany('Tag') class Tag(Entity): articles = ManyToMany('Article')
Behind the scene, the ManyToMany 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 your ManyToMany relationship is self-referencial, the entity containing it is autoloaded (and you don't intend to specify both the primaryjoin and secondaryjoin arguments manually), you must specify at least one of either the remote_colname or local_colname argument.
In addition to keyword arguments inherited from SQLAlchemy, ManyToMany relationships accept the following optional (keyword) arguments:
The following DSL statements provide an alternative way to define relationships between your entities. 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).
class Pet(Entity): belongs_to('feeder', of_kind='Person') belongs_to('owner', of_kind='Person', colname="owner_id")
class Person(Entity): belongs_to('parent', of_kind='Person') has_many('children', of_kind='Person')
There is also an alternate form of the has_many relationship that takes only two keyword arguments: through and via in order to encourage a richer form of many-to-many relationship that is an alternative to the has_and_belongs_to_many statement. Here is an example:
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')
In the above example, a Person has many projects through the Assignment relationship object, via a project attribute.
class Car(Entity): has_one('gear_stick', of_kind='GearStick', inverse='car') class GearStick(Entity): belongs_to('car', of_kind='Car')
class Article(Entity): has_and_belongs_to_many('tags', of_kind='Tag') class Tag(Entity): has_and_belongs_to_many('articles', of_kind='Article')