Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

add Elixir documentation

  • Loading branch information...
commit 140250e191bb79bd6c3642b60185ab073d59c6a4 1 parent 5f6d9f9
Erik Janssens authored
View
8 camelot/core/orm/entity.py
@@ -1,3 +1,11 @@
+"""
+This module provides the :class:`camelot.core.orm.entity.EntityBase` declarative base class,
+as well as its metaclass :class:`camelot.core.orm.entity.EntityMeta`. Those are the building
+blocks for creating the :class:`camelot.core.orm.Entity`.
+
+These classes can be reused if a custom base class is needed.
+"""
+
import sys
from sqlalchemy import orm, schema, sql
View
94 camelot/core/orm/fields.py
@@ -3,6 +3,100 @@
from . properties import Property
from . statements import ClassMutator
+"""
+This module provides support for defining the fields (columns) of your
+entities. This module sole reason of existence is to keep existing Elixir
+model definitions working. Do not use it when writing new code, instead
+use Declarative directly.
+
+Two syntaxes are supported, the default attribute-based syntax as well as
+the `has_field` DSL statement.
+
+Attribute-based syntax
+----------------------
+
+Here is a quick example of how to use the object-oriented syntax.
+
+.. sourcecode:: python
+
+ class Person(Entity):
+ id = Field(Integer, primary_key=True)
+ name = Field(String(50), required=True)
+ ssn = Field(String(50), unique=True)
+ biography = Field(Text)
+ join_date = Field(DateTime, default=datetime.datetime.now)
+ photo = Field(Binary, deferred=True)
+ _email = Field(String(20), colname='email', synonym='email')
+
+ def _set_email(self, email):
+ self._email = email
+ def _get_email(self):
+ return self._email
+
+ email = property(_get_email, _set_email)
+
+
+The Field class takes one mandatory argument, which is its type. Please refer
+to SQLAlchemy documentation for a list of `types supported by SQLAlchemy
+<http://docs.sqlalchemy.org/en/rel_0_7/core/types.html>`_.
+
+Following that first mandatory argument, fields can take any number of
+optional keyword arguments. Please note that all the **arguments** that are
+**not specifically processed by the Camelot orm module**, as mentioned in the
+documentation below **are passed on to the SQLAlchemy ``Column`` object**.
+Please refer to the `SQLAlchemy Column object's documentation
+<http://docs.sqlalchemy.org/en/rel_0_7/core/schema.html>`_ for more details
+about other supported keyword arguments.
+
+The following non SQLAlchemy-specific arguments are supported:
+
++-------------------+---------------------------------------------------------+
+| Argument Name | Description |
++===================+=========================================================+
+| ``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. |
++-------------------+---------------------------------------------------------+
+| ``colname`` | Specify a custom name for the column of this field. By |
+| | default the column will have the same name as the |
+| | attribute. |
++-------------------+---------------------------------------------------------+
+| ``deferred`` | Specify whether this particular column should be |
+| | fetched by default (along with the other columns) when |
+| | an instance of the entity is fetched from the database |
+| | or rather only later on when this particular column is |
+| | first referenced. This can be useful when one wants to |
+| | avoid loading a large text or binary field into memory |
+| | when its not needed. Individual columns can be lazy |
+| | loaded by themselves (by using ``deferred=True``) |
+| | or placed into groups that lazy-load together (by using |
+| | ``deferred`` = `"group_name"`). |
++-------------------+---------------------------------------------------------+
+| ``synonym`` | Specify a synonym name for this field. The field will |
+| | also be usable under that name in keyword-based Query |
+| | functions such as filter_by. The Synonym class (see the |
+| | `properties` module) provides a similar functionality |
+| | with an (arguably) nicer syntax, but a limited scope. |
++-------------------+---------------------------------------------------------+
+
+has_field
+---------
+
+The `has_field` statement allows you to define fields one at a time.
+
+The first argument is the name of the field, the second is its type. Following
+these, any number of keyword arguments can be specified for additional
+behavior.
+
+Here is a quick example of how to use ``has_field``.
+
+.. sourcecode:: python
+
+ class Person(Entity):
+ has_field('id', Integer, primary_key=True)
+ has_field('name', String(50))
+"""
+
class Field(Property):
'''
Represents the definition of a 'field' on an entity.
View
67 camelot/core/orm/options.py
@@ -1,3 +1,67 @@
+"""
+This module provides support for defining several options on your
+entities.
+
+`using_options`
+---------------
+The 'using_options' DSL statement allows you to set up some additional
+behaviors on your model objects, including table names, ordering, and
+more. To specify an option, simply supply the option as a keyword
+argument onto the statement, as follows:
+
+.. sourcecode:: python
+
+ class Person(Entity):
+ using_options(tablename='person', order_by='name')
+ name = Field(Unicode(64))
+
+
+
+The list of supported arguments are as follows:
+
++---------------------+-------------------------------------------------------+
+| Option Name | Description |
++=====================+=======================================================+
+| ``metadata`` | Specify a custom MetaData for this entity. |
+| | By default, entities uses the global |
+| | ``elixir.metadata``. |
+| | This option can also be set for all entities of a |
+| | module by setting the ``__metadata__`` attribute of |
+| | that module. | |
++---------------------+-------------------------------------------------------+
+| ``tablename`` | Specify a custom tablename. You can either provide a |
+| | plain string or a callable. The callable will be |
+| | given the entity (ie class) as argument and must |
+| | return a string representing the name of the table |
+| | for that entity. By default, the tablename is |
+| | automatically generated: it is a concatenation of the |
+| | full module-path to the entity and the entity (class) |
+| | name itself. The result is lower-cased and separated |
+| | by underscores ("_"), eg.: for an entity named |
+| | "MyEntity" in the module "project1.model", the |
+| | generated table name will be |
+| | "project1_model_myentity". |
++---------------------+-------------------------------------------------------+
+| ``order_by`` | How to order select results. Either a string or a |
+| | list of strings, composed of the field name, |
+| | optionally lead by a minus (for descending order). |
++---------------------+-------------------------------------------------------+
+| ``session`` | Specify a custom contextual session for this entity. |
+| | By default, entities uses the global |
+| | ``elixir.session``. |
+| | This option takes a ``ScopedSession`` object or |
+| | ``None``. In the later case your entity will be |
+| | mapped using a non-contextual mapper which requires |
+| | manual session management, as seen in pure SQLAlchemy.|
+| | This option can also be set for all entities of a |
+| | module by setting the ``__session__`` attribute of |
+| | that module. |
++---------------------+-------------------------------------------------------+
+
+For examples, please refer to the examples and unit tests.
+
+"""
+
from sqlalchemy import types
from . statements import ClassMutator
@@ -37,6 +101,9 @@ def default_m2m_column_formatter(data):
]
class using_options( ClassMutator ):
+ """This statement its sole reason of existence is to keep existing Elixir
+ model definitions working. Do not use it when writing new code, instead
+ use Declarative directly."""
def process( self, entity_dict, tablename = None, order_by = None, **kwargs ):
if tablename:
View
60 camelot/core/orm/properties.py
@@ -1,4 +1,34 @@
-
+"""
+This module provides support for defining properties on your entities. It both
+provides, the `Property` class which acts as a building block for common
+properties such as fields and relationships (for those, please consult the
+corresponding modules), but also provides some more specialized properties,
+such as `ColumnProperty` and `Synonym`. It also provides the GenericProperty
+class which allows you to wrap any SQLAlchemy property, and its DSL-syntax
+equivalent: has_property_.
+
+`has_property`
+--------------
+The ``has_property`` statement allows you to define properties which rely on
+their entity's table (and columns) being defined before they can be declared
+themselves. The `has_property` statement takes two arguments: first the name of
+the property to be defined and second a function (often given as an anonymous
+lambda) taking one argument and returning the desired SQLAlchemy property. That
+function will be called whenever the entity table is completely defined, and
+will be given the .c attribute of the entity as argument (as a way to access
+the entity columns).
+
+Here is a quick example of how to use ``has_property``.
+
+.. sourcecode:: python
+
+ class OrderLine(Entity):
+ has_field('quantity', Float)
+ has_field('unit_price', Float)
+ has_property('price',
+ lambda c: column_property(
+ (c.quantity * c.unit_price).label('price')))
+"""
from sqlalchemy import orm
from . statements import ClassMutator
@@ -143,6 +173,34 @@ def _config( self, cls, mapper, key ):
setattr( cls, key, prop_value )
class ColumnProperty( GenericProperty ):
+ """A specialized form of the GenericProperty to generate SQLAlchemy
+ ``column_property``'s.
+
+ It takes a function (often given as an anonymous lambda) as its first
+ argument. Other arguments and keyword arguments are forwarded to the
+ column_property construct. That first-argument function must accept exactly
+ one argument and must return the desired (scalar-returning) SQLAlchemy
+ ClauseElement.
+
+ The function will be called whenever the entity table is completely
+ defined, and will be given
+ the .c attribute of the table of the entity as argument (as a way to
+ access the entity columns). The ColumnProperty will first wrap your
+ ClauseElement in an
+ "empty" label (ie it will be labelled automatically during queries),
+ then wrap that in a column_property.
+
+ .. sourcecode:: python
+
+ class OrderLine(Entity):
+ quantity = Field(Float)
+ unit_price = Field(Numeric)
+ price = ColumnProperty(lambda c: c.quantity * c.unit_price,
+ deferred=True)
+
+ Please look at the `corresponding SQLAlchemy
+ documentation <http://docs.sqlalchemy.org/en/rel_0_7/orm/mapper_config.html#sql-expressions-as-mapped-attributes>`_
+ for details."""
def evaluate_property( self, prop ):
return orm.column_property( prop.label( None ), *self.args, **self.kwargs )
View
400 camelot/core/orm/relationships.py
@@ -1,3 +1,403 @@
+'''
+This module provides support for defining relationships between your
+entities. Two syntaxes are supported 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_.
+
+======================
+Attribute-based syntax
+======================
+
+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, as
+mentioned in the documentation below are passed on to the SQLAlchemy
+``relationship`` function. So, please refer to the `SQLAlchemy relationship function's
+documentation <http://docs.sqlalchemy.org/en/rel_0_7/orm/tutorial.html
+#building-a-relationship>`_ for further detail about which
+keyword arguments are supported.
+
+You should keep in mind that the following
+keyword arguments are automatically generated and should not be used
+unless you want to override the default value provided : ``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, relationships will
+be matched together automatically. If there are several relationships
+of the same type between two entities, 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:
+
+`ManyToOne`
+-----------
+
+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:
+
+.. sourcecode:: python
+
+ 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:
+
++----------------------+------------------------------------------------------+
+| Option Name | Description |
++======================+======================================================+
+| ``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:
+
++----------------------+------------------------------------------------------+
+| 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``. |
++----------------------+------------------------------------------------------+
+| ``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.
+
+.. sourcecode:: python
+
+ class Pet(Entity):
+ owner_id = Field(Integer, colname='owner')
+ owner = ManyToOne('Person', field=owner_id)
+
++----------------------+------------------------------------------------------+
+| Option Name | Description |
++======================+======================================================+
+| ``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. |
++----------------------+------------------------------------------------------+
+
+
+Additionally, the belongs_to_ statement is supported as an alternative,
+DSL-based, syntax to define ManyToOne_ relationships.
+
+
+`OneToMany`
+-----------
+
+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:
+
+.. sourcecode:: python
+
+ 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:
+
++--------------------+--------------------------------------------------------+
+| Option Name | Description |
++====================+========================================================+
+| ``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). |
++--------------------+--------------------------------------------------------+
+| ``filter`` | 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') |
++--------------------+--------------------------------------------------------+
+
+Additionally, an alternate DSL-based is supported, syntax to define
+OneToMany_ relationships, with the has_many_ statement.
+
+
+`OneToOne`
+----------
+
+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:
+
+.. sourcecode:: python
+
+ 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.
+
+Additionally, an alternate DSL-based syntax is supported to define
+OneToOne_ relationships, with the has_one_ statement.
+
+
+`ManyToMany`
+------------
+
+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.
+
+.. sourcecode:: python
+
+ 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:
+
++--------------------+--------------------------------------------------------+
+| 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. If this argument is not used, a name will be |
+| | automatically generated by Elixir depending on the name|
+| | of the tables of the two entities of the relationship, |
+| | the name of the relationship, and, if present, the name|
+| | of its inverse. Even though this argument is optional, |
+| | it is wise to use it if you are not sure what are the |
+| | exact consequence of using a generated table name. |
++--------------------+--------------------------------------------------------+
+| ``schema`` | Specify a custom schema for the intermediate table. |
+| | This can be used both when the tables needs to |
+| | be created and when the table is autoloaded/reflected |
+| | from the database. |
++--------------------+--------------------------------------------------------+
+| ``remote_colname`` | A string or list of strings specifying the names of |
+| | the column(s) in the intermediary table which |
+| | reference the "remote"/target entity's table. |
++--------------------+--------------------------------------------------------+
+| ``local_colname`` | A string or list of strings specifying the names of |
+| | the column(s) in the intermediary table which |
+| | reference the "local"/current entity's table. |
++--------------------+--------------------------------------------------------+
+| ``table`` | Use a manually created table. If this argument is |
+| | used, Elixir won't generate a table for this |
+| | relationship, and use the one given instead. |
++--------------------+--------------------------------------------------------+
+| ``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). |
++----------------------+------------------------------------------------------+
+| ``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``. |
++--------------------+--------------------------------------------------------+
+| ``table_kwargs`` | A dictionary holding any other keyword argument you |
+| | might want to pass to the underlying Table object. |
++--------------------+--------------------------------------------------------+
+
+
+================
+DSL-based syntax
+================
+
+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).
+
+`belongs_to`
+------------
+
+The ``belongs_to`` statement is the DSL syntax equivalent to the ManyToOne_
+relationship. As such, it supports all the same arguments as ManyToOne_
+relationships.
+
+.. sourcecode:: python
+
+ class Pet(Entity):
+ belongs_to('feeder', of_kind='Person')
+ belongs_to('owner', of_kind='Person', colname="owner_id")
+
+
+`has_many`
+----------
+
+The ``has_many`` statement is the DSL syntax equivalent to the OneToMany_
+relationship. As such, it supports all the same arguments as OneToMany_
+relationships.
+
+.. sourcecode:: python
+
+ 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:
+
+.. sourcecode:: python
+
+ 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.
+
+
+`has_one`
+---------
+
+The ``has_one`` statement is the DSL syntax equivalent to the OneToOne_
+relationship. As such, it supports all the same arguments as OneToOne_
+relationships.
+
+.. sourcecode:: python
+
+ 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`
+-------------------------
+
+The ``has_and_belongs_to_many`` statement is the DSL syntax equivalent to the
+ManyToMany_ relationship. As such, it supports all the same arguments as
+ManyToMany_ relationships.
+
+.. sourcecode:: python
+
+ class Article(Entity):
+ has_and_belongs_to_many('tags', of_kind='Tag')
+
+ class Tag(Entity):
+ has_and_belongs_to_many('articles', of_kind='Article')
+
+'''
+
from sqlalchemy import schema, sql
from sqlalchemy.orm import relationship, backref, class_mapper
View
2  camelot/core/orm/statements.py
@@ -4,7 +4,7 @@
class ClassMutator( object ):
"""Class to create DSL statements such as `using_options`. This is used
- to transform Elixir like DSL statements in Declarative class attributes.
+ to transform DSL statements in Declarative class attributes.
The use of these statements is discouraged in any new code, and exists for
compatibility with Elixir model definitions"""
View
10 doc/sphinx/source/doc/models.rst
@@ -13,10 +13,9 @@ simple or sophisticated datastructures. The `SQLAlchemy website <http://www.sqla
documentation on all these features. An important part of Camelot is providing an easy way to
create views for objects mapped through SQLAlchemy.
-SQLAlchemy comes with the *Declarative* extension to make it easy to define an ORM mapping using
-the Active Record Pattern. This is used through the documentation and in the example code.
-An alternative to *Declarative* is `Elixir <http://elixir.ematia.de/trac/wiki/TutorialDivingIn>`_,
-which was used in previous *Camelot* versions, and is still supported.
+SQLAlchemy comes with the `Declarative <http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html>`_
+extension to make it easy to define an ORM mapping using the Active Record Pattern. This is used through the
+documentation and in the example code.
To use *Declarative*, threre are some base classes that should be imported:
@@ -26,7 +25,7 @@ To use *Declarative*, threre are some base classes that should be imported:
Those are :
- * :class:`camelot.core.orm.Entity` is the base class for all classes that are mapped to the database
+ * :class:`camelot.core.orm.Entity` is the declarative base class provided by Camelot for all classes that are mapped to the database
* :class:`camelot.admin.entity_admin.EntityAdmin` is the base class that describes how an `Entity` subclass should be represented in the GUI
@@ -60,5 +59,6 @@ can be used as a place to start the model definition.
.. toctree::
fields.rst
+ relations.rst
calculated_fields.rst
views.rst
Please sign in to comment.
Something went wrong with that request. Please try again.