| 1 | ''' |
|---|
| 2 | Field statements for Elixir entities |
|---|
| 3 | |
|---|
| 4 | ====== |
|---|
| 5 | Fields |
|---|
| 6 | ====== |
|---|
| 7 | |
|---|
| 8 | This module contains DSL statements which allow you to declare which |
|---|
| 9 | fields (columns) your Elixir entities have. There are currently two |
|---|
| 10 | different statements that you can use to declare fields: |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | `has_field` |
|---|
| 14 | ----------- |
|---|
| 15 | The `has_field` statement allows you to define fields one at a time. |
|---|
| 16 | |
|---|
| 17 | The first argument is the name of the field, the second is its type. Following |
|---|
| 18 | these, any number of keyword arguments can be specified for additional |
|---|
| 19 | behavior. The following arguments are supported: |
|---|
| 20 | |
|---|
| 21 | +-------------------+---------------------------------------------------------+ |
|---|
| 22 | | Argument Name | Description | |
|---|
| 23 | +===================+=========================================================+ |
|---|
| 24 | | ``required`` | Specify whether or not this field can be set to None | |
|---|
| 25 | | | (left without a value). Defaults to ``False``, unless | |
|---|
| 26 | | | the field is a primary key. | |
|---|
| 27 | +-------------------+---------------------------------------------------------+ |
|---|
| 28 | | ``deferred`` | Specify whether this particular column should be | |
|---|
| 29 | | | fetched by default (along with the other columns) when | |
|---|
| 30 | | | an instance of the entity is fetched from the database | |
|---|
| 31 | | | or rather only later on when this particular column is | |
|---|
| 32 | | | first referenced. This can be useful when one wants to | |
|---|
| 33 | | | avoid loading a large text or binary field into memory | |
|---|
| 34 | | | when its not needed. Individual columns can be lazy | |
|---|
| 35 | | | loaded by themselves (by using ``deferred=True``) | |
|---|
| 36 | | | or placed into groups that lazy-load together (by using | |
|---|
| 37 | | | ``deferred`` = `"group_name"`). | |
|---|
| 38 | +-------------------+---------------------------------------------------------+ |
|---|
| 39 | |
|---|
| 40 | Any other keyword argument is passed on to the SQLAlchemy |
|---|
| 41 | ``Column`` object. Please refer to the `SQLAlchemy Column object's |
|---|
| 42 | documentation <http://www.sqlalchemy.org/docs/docstrings.myt |
|---|
| 43 | #docstrings_sqlalchemy.schema_Column>`_ for further detail about which |
|---|
| 44 | keyword arguments are supported. |
|---|
| 45 | |
|---|
| 46 | Here is a quick example of how to use ``has_field``. |
|---|
| 47 | |
|---|
| 48 | :: |
|---|
| 49 | |
|---|
| 50 | class Person(Entity): |
|---|
| 51 | has_field('id', Integer, primary_key=True) |
|---|
| 52 | has_field('name', String(50)) |
|---|
| 53 | |
|---|
| 54 | |
|---|
| 55 | `with_fields` |
|---|
| 56 | ------------- |
|---|
| 57 | The `with_fields` statement allows you to define fields all at once. |
|---|
| 58 | |
|---|
| 59 | Each keyword argument to this statement represents one field, which should |
|---|
| 60 | be a `Field` object. The first argument to a Field object is its type. |
|---|
| 61 | Following it, any number of keyword arguments can be specified for |
|---|
| 62 | additional behavior. The `with_fields` statement supports the same keyword |
|---|
| 63 | arguments than the `has_field` statement. |
|---|
| 64 | |
|---|
| 65 | Here is a quick example of how to use ``with_fields``. |
|---|
| 66 | |
|---|
| 67 | :: |
|---|
| 68 | |
|---|
| 69 | class Person(Entity): |
|---|
| 70 | with_fields( |
|---|
| 71 | id = Field(Integer, primary_key=True), |
|---|
| 72 | name = Field(String(50)) |
|---|
| 73 | ) |
|---|
| 74 | ''' |
|---|
| 75 | |
|---|
| 76 | from sqlalchemy import Column |
|---|
| 77 | from elixir.statements import Statement |
|---|
| 78 | |
|---|
| 79 | __all__ = ['has_field', 'with_fields', 'Field'] |
|---|
| 80 | |
|---|
| 81 | __pudge_all__ = ['Field'] |
|---|
| 82 | |
|---|
| 83 | class Field(object): |
|---|
| 84 | ''' |
|---|
| 85 | Represents the definition of a 'field' on an entity. |
|---|
| 86 | |
|---|
| 87 | This class represents a column on the table where the entity is stored. |
|---|
| 88 | This object is only used with the `with_fields` syntax for defining all |
|---|
| 89 | fields for an entity at the same time. The `has_field` syntax does not |
|---|
| 90 | require the manual creation of this object. |
|---|
| 91 | ''' |
|---|
| 92 | |
|---|
| 93 | def __init__(self, type, *args, **kwargs): |
|---|
| 94 | self.colname = kwargs.pop('colname', None) |
|---|
| 95 | self.deferred = kwargs.pop('deferred', False) |
|---|
| 96 | if 'required' in kwargs: |
|---|
| 97 | kwargs['nullable'] = not kwargs.pop('required') |
|---|
| 98 | self.type = type |
|---|
| 99 | self.primary_key = kwargs.get('primary_key', False) |
|---|
| 100 | |
|---|
| 101 | self.args = args |
|---|
| 102 | self.kwargs = kwargs |
|---|
| 103 | |
|---|
| 104 | def column(self): |
|---|
| 105 | ''' |
|---|
| 106 | Returns the corresponding sqlalchemy-column |
|---|
| 107 | ''' |
|---|
| 108 | |
|---|
| 109 | if not hasattr(self, '_column'): |
|---|
| 110 | self._column = Column(self.colname, self.type, |
|---|
| 111 | *self.args, **self.kwargs) |
|---|
| 112 | return self._column |
|---|
| 113 | column = property(column) |
|---|
| 114 | |
|---|
| 115 | |
|---|
| 116 | class HasField(object): |
|---|
| 117 | |
|---|
| 118 | def __init__(self, entity, name, *args, **kwargs): |
|---|
| 119 | field = Field(*args, **kwargs) |
|---|
| 120 | field.colname = name |
|---|
| 121 | entity._descriptor.add_field(field) |
|---|
| 122 | |
|---|
| 123 | |
|---|
| 124 | class WithFields(object): |
|---|
| 125 | |
|---|
| 126 | def __init__(self, entity, *args, **fields): |
|---|
| 127 | columns = list() |
|---|
| 128 | desc = entity._descriptor |
|---|
| 129 | |
|---|
| 130 | for colname, field in fields.iteritems(): |
|---|
| 131 | if not field.colname: |
|---|
| 132 | field.colname = colname |
|---|
| 133 | desc.add_field(field) |
|---|
| 134 | |
|---|
| 135 | has_field = Statement(HasField) |
|---|
| 136 | with_fields = Statement(WithFields) |
|---|