root / elixir / tags / 0.3.0 / elixir / fields.py

Revision 82, 4.8 kB (checked in by ged, 5 years ago)

- Added support for autoloading/reflecting databases with

has_and_belongs_to_many relationships. The tablename argument is now
optional, but still recommended, otherwise you'll have to use the same exact
name for your intermediary table than the one generated.

- Made the colname argument optional for belongs_to relationships in

autoloaded entities. It is only required to specify it if you have several
belongs_to relationships between two entities/tables.

- Fixed bug preventing having entities without any statement.
- Made some PEP8 tweaks in many places. Used the pep8 script provided with

Cheesecake.

- Foreign key names generated by belongs_to relationships use column names

instead of relation names in case we have a relation with the same name
defined in several entities inheriting from the same entity using single-
table inheritance (and we set a custom column name in one of them to avoid
a column-name conflict).

- Actually make the code python 2.3 compatible (Robin's patch was based on

0.1.0 while I had introduced more decorators in the trunk in the mean time).

- Some cleanup/useless code removal (namely the foreign_key stuff in

relationships).

Line 
1'''
2Field statements for Elixir entities
3
4======
5Fields
6======
7
8This module contains DSL statements which allow you to declare which
9fields (columns) your Elixir entities have.  There are currently two
10different statements that you can use to declare fields:
11
12
13`has_field`
14-----------
15The `has_field` statement allows you to define fields one at a time.
16
17The first argument is the name of the field, the second is its type. Following
18these, any number of keyword arguments can be specified for additional
19behavior. 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
40Any other keyword argument is passed on to the SQLAlchemy
41``Column`` object. Please refer to the `SQLAlchemy Column object's
42documentation <http://www.sqlalchemy.org/docs/docstrings.myt
43#docstrings_sqlalchemy.schema_Column>`_ for further detail about which
44keyword arguments are supported.
45
46Here 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-------------
57The `with_fields` statement allows you to define fields all at once.
58
59Each keyword argument to this statement represents one field, which should
60be a `Field` object. The first argument to a Field object is its type.
61Following it, any number of keyword arguments can be specified for
62additional behavior. The `with_fields` statement supports the same keyword
63arguments than the `has_field` statement.
64
65Here 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
76from sqlalchemy             import Column
77from elixir.statements      import Statement
78
79__all__ = ['has_field', 'with_fields', 'Field']
80
81__pudge_all__ = ['Field']
82
83class 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
116class 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
124class 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
135has_field = Statement(HasField)
136with_fields = Statement(WithFields)
Note: See TracBrowser for help on using the browser.