PK—"8“×2EGG-INFO/zip-safe PK—"8΂V V EGG-INFO/SOURCES.txtCHANGES FAQ LICENSE README RELEASE TODO setup.cfg setup.py Elixir.egg-info/PKG-INFO Elixir.egg-info/SOURCES.txt Elixir.egg-info/dependency_links.txt Elixir.egg-info/requires.txt Elixir.egg-info/top_level.txt docs/contact.rst docs/download.rst docs/examples.rst docs/index.rst docs/tutorial.rst docs/html/images/background.jpg docs/html/images/logo.jpg docs/template/elixir.css docs/template/layout.html docs/template/package-index.html docs/template/images/background.jpg docs/template/images/logo.jpg elixir/__init__.py elixir/entity.py elixir/fields.py elixir/options.py elixir/relationships.py elixir/statements.py examples/__init__.py examples/videostore/dev.cfg examples/videostore/devdata.sqlite examples/videostore/setup.py examples/videostore/start-videostore.py examples/videostore/videostore/__init__.py examples/videostore/videostore/json.py examples/videostore/videostore/model.py examples/videostore/videostore/release.py examples/videostore/videostore.egg-info/PKG-INFO examples/videostore/videostore.egg-info/SOURCES.txt examples/videostore/videostore.egg-info/dependency_links.txt examples/videostore/videostore.egg-info/not-zip-safe examples/videostore/videostore.egg-info/paster_plugins.txt examples/videostore/videostore.egg-info/requires.txt examples/videostore/videostore.egg-info/sqlobject.txt examples/videostore/videostore.egg-info/top_level.txt examples/videostore/videostore/config/__init__.py examples/videostore/videostore/config/app.cfg examples/videostore/videostore/config/log.cfg examples/videostore/videostore/controllers/__init__.py examples/videostore/videostore/controllers/root.py examples/videostore/videostore/static/css/login.css examples/videostore/videostore/static/css/style.css examples/videostore/videostore/static/images/favicon.ico examples/videostore/videostore/static/images/info.png examples/videostore/videostore/static/images/ok.png examples/videostore/videostore/templates/__init__.py examples/videostore/videostore/templates/actor.kid examples/videostore/videostore/templates/director.kid examples/videostore/videostore/templates/index.kid examples/videostore/videostore/templates/login.kid examples/videostore/videostore/templates/master.kid examples/videostore/videostore/templates/movie.kid examples/videostore/videostore/tests/__init__.py examples/videostore/videostore/tests/test_controllers.py examples/videostore/videostore/tests/test_model.py tests/__init__.py tests/test_autoload.py tests/test_hasfield.py tests/test_inherit.py tests/test_movies.py tests/test_multi.py tests/test_nestedclass.py tests/test_oneway.py tests/test_options.py tests/test_order_by.py tests/test_selfref.py PK—"8“×2EGG-INFO/dependency_links.txt PK—"8Övª77EGG-INFO/PKG-INFOMetadata-Version: 1.0 Name: Elixir Version: 0.3.0 Summary: Declarative Mapper for SQLAlchemy Home-page: http://elixir.ematia.de Author: Gaetan de Menten, Daniel Haus and Jonathan LaCour Author-email: sqlelixir@googlegroups.com License: MIT License Description: Elixir ====== A declarative layer on top of SQLAlchemy. It is a fairly thin wrapper, which provides the ability to define model objects following the Active Record design pattern, and using a DSL syntax similar to that of the Ruby on Rails ActiveRecord system. Elixir does not intend to replace SQLAlchemy's core features, but instead focuses on providing a simpler syntax for defining model objects when you do not need the full expressiveness of SQLAlchemy's manual mapper definitions. Elixir is intended to replace the ActiveMapper SQLAlchemy extension, and the TurboEntity project. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Database :: Front-Ends Classifier: Topic :: Software Development :: Libraries :: Python Modules PK—"8ù´,‚žžEGG-INFO/requires.txtSQLAlchemy >= 0.3.0 [pudge] docutils>=0.4 elementtree>=1.2.6 kid>=0.9 Pygments==dev,>=0.7dev-r2661 pudge==dev,>=0.1.3dev-r134 buildutils==dev,>=0.1.2dev-r109PK—"8—ì§»EGG-INFO/top_level.txtelixir PK—"8… ûûelixir/fields.pyc;ò ôžæEc@s–dZdklZdklZdddgZdgZdefd„ƒYZdefd„ƒYZ d efd „ƒYZ ee ƒZ ee ƒZ d S( s Field statements for Elixir entities ====== Fields ====== This module contains DSL statements which allow you to declare which fields (columns) your Elixir entities have. There are currently two different statements that you can use to declare fields: `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. The following 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. | +-------------------+---------------------------------------------------------+ | ``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"`). | +-------------------+---------------------------------------------------------+ Any other keyword argument is passed on to the SQLAlchemy ``Column`` object. Please refer to the `SQLAlchemy Column object's documentation `_ for further detail about which keyword arguments are supported. Here is a quick example of how to use ``has_field``. :: class Person(Entity): has_field('id', Integer, primary_key=True) has_field('name', String(50)) `with_fields` ------------- The `with_fields` statement allows you to define fields all at once. Each keyword argument to this statement represents one field, which should be a `Field` object. The first argument to a Field object is its type. Following it, any number of keyword arguments can be specified for additional behavior. The `with_fields` statement supports the same keyword arguments than the `has_field` statement. Here is a quick example of how to use ``with_fields``. :: class Person(Entity): with_fields( id = Field(Integer, primary_key=True), name = Field(String(50)) ) (sColumn(s Statements has_fields with_fieldssFieldcBs,tZdZd„Zd„ZeeƒZRS(sV Represents the definition of a 'field' on an entity. This class represents a column on the table where the entity is stored. This object is only used with the `with_fields` syntax for defining all fields for an entity at the same time. The `has_field` syntax does not require the manual creation of this object. cOsƒ|idtƒ|_|idtƒ|_d|jo|idƒ |d|io|iii}n tƒ}|i|iƒ|SdS(N(sselfsparents _descriptorsall_relationshipssressdictsupdates relationships(sselfsres((s1build/bdist.darwin-8.0.1-x86/egg/elixir/entity.pysall_relationshipss   cCs?x8ttiƒD]'}|iƒotii|ƒqqWdS(N(slistsEntityDescriptorsuninitialized_relss relationshipssetupsremove(sclss relationship((s1build/bdist.darwin-8.0.1-x86/egg/elixir/entity.pyssetup_relationships"s (s__name__s __module__s__doc__ssetsuninitialized_relssNonescurrents__init__s setup_optionsssetupstranslate_order_bys setup_mappers setup_tablescreate_auto_primary_keys add_fieldsadd_constraintsFalsesget_inverse_relationsall_relationshipsspropertyssetup_relationshipss classmethod(((s1build/bdist.darwin-8.0.1-x86/egg/elixir/entity.pysEntityDescriptors"   *   % 5      s EntityMetacBstZdZd„ZRS(s Entity meta class. cCsZ|dtjodSnt|ƒ}|_|t_ti|ƒ|i ƒ|i ƒdS(Ni( sbasessobjectsEntityDescriptorsclssdescs _descriptorscurrents Statementsprocesss setup_optionsssetup(sclssnamesbasessdict_sdesc((s1build/bdist.darwin-8.0.1-x86/egg/elixir/entity.pys__init__-s   (s__name__s __module__s__doc__s__init__(((s1build/bdist.darwin-8.0.1-x86/egg/elixir/entity.pys EntityMeta(s cBstZdZeZRS(sz The base class for all entities All Elixir model objects should inherit from this class. Statements can appear within the body of the definition of an entity to define its fields, relationships, and other options. Here is an example: :: class Person(Entity): has_field('name', Unicode(128)) has_field('birthdate', DateTime, default=datetime.now) Please note, that if you don't specify any primary keys, Elixir will automatically create one called ``id``. For further information, please refer to the provided examples or tutorial. (s__name__s __module__s__doc__s EntityMetas __metaclass__(((s1build/bdist.darwin-8.0.1-x86/egg/elixir/entity.pysEntity?s ( s__doc__s sqlalchemysTablesIntegersdescsdeferredssqlalchemy.ext.assignmappers assign_mapperssqlalchemy.utils OrderedDictselixir.statementss Statements elixir.fieldssFieldselixir.optionssoptions_defaultsssets NameErrorssetssSetssysselixirs__all__s __pudge_all__sDEFAULT_AUTO_PRIMARYKEY_NAMEsDEFAULT_AUTO_PRIMARYKEY_TYPEsDEFAULT_VERSION_ID_COLsobjectsEntityDescriptorstypes EntityMetasEntity(ssets OrderedDicts EntityMetasFields StatementsEntityDescriptors__all__soptions_defaultssIntegersDEFAULT_VERSION_ID_COLsDEFAULT_AUTO_PRIMARYKEY_NAMEsDEFAULT_AUTO_PRIMARYKEY_TYPEssysselixirsdescsEntitysdeferreds assign_mappersTables __pudge_all__((s1build/bdist.darwin-8.0.1-x86/egg/elixir/entity.pys?s*        ÿ PK{<{6¸Ì¤òÐwÐwelixir/relationships.py''' Relationship statements for Elixir entities ============= 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``. .. _order_by: 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. | +--------------------+--------------------------------------------------------+ ''' from sqlalchemy import relation, ForeignKeyConstraint, Column, \ Table, and_ from elixir.statements import Statement from elixir.fields import Field from elixir.entity import EntityDescriptor import sys __all__ = ['belongs_to', 'has_one', 'has_many', 'has_and_belongs_to_many'] __pudge_all__ = [] class Relationship(object): ''' Base class for relationships. ''' def __init__(self, entity, name, *args, **kwargs): self.entity = entity self.name = name self.of_kind = kwargs.pop('of_kind') self.inverse_name = kwargs.pop('inverse', None) self._target = None self._inverse = None self.property = None # sqlalchemy property #TODO: unused for now self.args = args self.kwargs = kwargs self.entity._descriptor.relationships[self.name] = self def create_keys(self): ''' Subclasses (ie. concrete relationships) may override this method to create foreign keys. ''' def create_tables(self): ''' Subclasses (ie. concrete relationships) may override this method to create secondary tables. ''' def create_properties(self): ''' Subclasses (ie. concrete relationships) may override this method to add properties to the involved entities. ''' def setup(self): ''' Sets up the relationship, creates foreign keys and secondary tables. ''' if not self.target: return False if self.property: return True self.create_keys() self.create_tables() self.create_properties() return True def target(self): if not self._target: path = self.of_kind.rsplit('.', 1) classname = path.pop() if path: # do we have a fully qualified entity name? module = sys.modules[path.pop()] else: # if not, try the same module as the source module = self.entity._descriptor.module self._target = getattr(module, classname, None) if not self._target: # This is ugly but we need it because the class which is # currently being defined (we have to keep in mind we are in # its metaclass code) is not yet available in the module # namespace, so the getattr above fails. And unfortunately, # this doesn't only happen for the owning entity of this # relation since we might be setting up a deferred relation. e = EntityDescriptor.current.entity if classname == e.__name__ or \ self.of_kind == e.__module__ +'.'+ e.__name__: self._target = e else: return None return self._target target = property(target) def inverse(self): if not self._inverse: if self.inverse_name: desc = self.target._descriptor # we use all_relationships so that relationships from parent # entities are included too inverse = desc.all_relationships.get(self.inverse_name, None) if inverse is None: raise Exception( "Couldn't find a relationship named '%s' in " "entity '%s' or its parent entities." % (self.inverse_name, self.target.__name__)) assert self.match_type_of(inverse) else: inverse = self.target._descriptor.get_inverse_relation(self) if inverse: self._inverse = inverse inverse._inverse = self return self._inverse inverse = property(inverse) def match_type_of(self, other): t1, t2 = type(self), type(other) if t1 is HasAndBelongsToMany: return t1 is t2 elif t1 in (HasOne, HasMany): return t2 is BelongsTo elif t1 is BelongsTo: return t2 in (HasMany, HasOne) else: return False def is_inverse(self, other): return other is not self and \ self.match_type_of(other) and \ self.entity == other.target and \ other.entity == self.target and \ (self.inverse_name == other.name or not self.inverse_name) and \ (other.inverse_name == self.name or not other.inverse_name) class BelongsTo(Relationship): ''' ''' def __init__(self, entity, name, *args, **kwargs): self.colname = kwargs.pop('colname', []) if self.colname and not isinstance(self.colname, list): self.colname = [self.colname] self.column_kwargs = kwargs.pop('column_kwargs', {}) if 'required' in kwargs: self.column_kwargs['nullable'] = not kwargs.pop('required') self.constraint_kwargs = kwargs.pop('constraint_kwargs', {}) if 'use_alter' in kwargs: self.constraint_kwargs['use_alter'] = kwargs.pop('use_alter') if 'ondelete' in kwargs: self.constraint_kwargs['ondelete'] = kwargs.pop('ondelete') self.foreign_key = list() self.primaryjoin_clauses = list() super(BelongsTo, self).__init__(entity, name, *args, **kwargs) def create_keys(self): ''' Find all primary keys on the target and create foreign keys on the source accordingly. ''' source_desc = self.entity._descriptor target_desc = self.target._descriptor if source_desc.autoload: #TODO: test if this works when colname is a list if self.colname: self.primaryjoin_clauses = \ _build_join_clauses(self.entity.table, self.colname, None, self.target.table)[0] if not self.primaryjoin_clauses: raise Exception( "Couldn't find a foreign key constraint in table " "'%s' using the following columns: %s." % (self.entity.table.name, ', '.join(self.colname))) else: fk_refcols = list() fk_colnames = list() if self.colname and \ len(self.colname) != len(target_desc.primary_keys): raise Exception( "The number of column names provided in the colname " "keyword argument of the '%s' relationship of the " "'%s' entity is not the same as the number of columns " "of the primary key of '%s'." % (self.name, self.entity.__name__, self.target.__name__)) for key_num, key in enumerate(target_desc.primary_keys): pk_col = key.column if self.colname: colname = self.colname[key_num] else: colname = '%s_%s' % (self.name, pk_col.name) # we use a Field here instead of using a Column directly # because of add_field field = Field(pk_col.type, colname=colname, index=True, **self.column_kwargs) source_desc.add_field(field) # build the list of local columns which will be part of # the foreign key self.foreign_key.append(field.column) # store the names of those columns fk_colnames.append(colname) # build the list of columns the foreign key will point to fk_refcols.append("%s.%s" % (target_desc.entity.table.name, pk_col.name)) # build up the primary join. This is needed when you have # several belongs_to relations between two objects self.primaryjoin_clauses.append(field.column == pk_col) # In some databases (at lease MySQL) the constraint name needs to # be unique for the whole database, instead of per table. fk_name = "%s_%s_fk" % (self.entity.table.name, '_'.join(fk_colnames)) source_desc.add_constraint(ForeignKeyConstraint( fk_colnames, fk_refcols, name=fk_name, **self.constraint_kwargs)) def create_properties(self): kwargs = self.kwargs if self.entity.table is self.target.table: if self.entity._descriptor.autoload: cols = [col for col in self.target.table.primary_key.columns] else: cols = [k.column for k in self.target._descriptor.primary_keys] kwargs['remote_side'] = cols if self.primaryjoin_clauses: kwargs['primaryjoin'] = and_(*self.primaryjoin_clauses) kwargs['uselist'] = False self.property = relation(self.target, **kwargs) self.entity.mapper.add_property(self.name, self.property) class HasOne(Relationship): uselist = False def create_keys(self): # make sure an inverse relationship exists if self.inverse is None: raise Exception( "Couldn't find any relationship in '%s' which " "match as inverse of the '%s' relationship " "defined in the '%s' entity. If you are using " "inheritance you " "might need to specify inverse relationships " "manually by using the inverse keyword." % (self.target.__name__, self.name, self.entity.__name__)) # make sure it is set up because it creates the foreign key we'll need self.inverse.setup() def create_properties(self): kwargs = self.kwargs #TODO: for now, we don't break any test if we remove those 2 lines. # So, we should either complete the selfref test to prove that they # are indeed useful, or remove them. It might be they are indeed # useless because of the primaryjoin, and that the remote_side is # already setup in the other way (belongs_to). if self.entity.table is self.target.table: #FIXME: IF this code is of any use, it will probably break for # autoloaded tables kwargs['remote_side'] = self.inverse.foreign_key if self.inverse.primaryjoin_clauses: kwargs['primaryjoin'] = and_(*self.inverse.primaryjoin_clauses) kwargs['uselist'] = self.uselist self.property = relation(self.target, **kwargs) self.entity.mapper.add_property(self.name, self.property) class HasMany(HasOne): uselist = True def create_properties(self): if 'order_by' in self.kwargs: self.kwargs['order_by'] = \ self.target._descriptor.translate_order_by( self.kwargs['order_by']) super(HasMany, self).create_properties() class HasAndBelongsToMany(Relationship): def __init__(self, entity, name, *args, **kwargs): self.user_tablename = kwargs.pop('tablename', None) self.local_side = kwargs.pop('local_side', []) if self.local_side and not isinstance(self.local_side, list): self.local_side = [self.local_side] self.remote_side = kwargs.pop('remote_side', []) if self.remote_side and not isinstance(self.remote_side, list): self.remote_side = [self.remote_side] self.secondary_table = None self.primaryjoin_clauses = list() self.secondaryjoin_clauses = list() super(HasAndBelongsToMany, self).__init__(entity, name, *args, **kwargs) def create_tables(self): if self.inverse: if self.inverse.secondary_table: self.secondary_table = self.inverse.secondary_table self.primaryjoin_clauses = self.inverse.secondaryjoin_clauses self.secondaryjoin_clauses = self.inverse.primaryjoin_clauses if not self.secondary_table: e1_desc = self.entity._descriptor e2_desc = self.target._descriptor # First, we compute the name of the table. Note that some of the # intermediary variables are reused later for the constraint # names. # We use the name of the relation for the first entity # (instead of the name of its primary key), so that we can # have two many-to-many relations between the same objects # without having a table name collision. source_part = "%s_%s" % (e1_desc.tablename, self.name) # And we use only the name of the table of the second entity # when there is no inverse, so that a many-to-many relation # can be defined without an inverse. if self.inverse: target_part = "%s_%s" % (e2_desc.tablename, self.inverse.name) else: target_part = e2_desc.tablename if self.user_tablename: tablename = self.user_tablename else: # We need to keep the table name consistent (independant of # whether this relation or its inverse is setup first). if self.inverse and e1_desc.tablename < e2_desc.tablename: tablename = "%s__%s" % (target_part, source_part) else: tablename = "%s__%s" % (source_part, target_part) if e1_desc.autoload: self._reflect_table(tablename) else: # We pre-compute the names of the foreign key constraints # pointing to the source (local) entity's table and to the # target's table # In some databases (at lease MySQL) the constraint names need # to be unique for the whole database, instead of per table. source_fk_name = "%s_fk" % source_part if self.inverse: target_fk_name = "%s_fk" % target_part else: target_fk_name = "%s_inverse_fk" % source_part columns = list() constraints = list() joins = (self.primaryjoin_clauses, self.secondaryjoin_clauses) for num, desc, fk_name in ((0, e1_desc, source_fk_name), (1, e2_desc, target_fk_name)): fk_colnames = list() fk_refcols = list() for key in desc.primary_keys: pk_col = key.column colname = '%s_%s' % (desc.tablename, pk_col.name) # In case we have a many-to-many self-reference, we # need to tweak the names of the columns so that we # don't end up with twice the same column name. if self.entity is self.target: colname += str(num + 1) col = Column(colname, pk_col.type) columns.append(col) # Build the list of local columns which will be part # of the foreign key. fk_colnames.append(colname) # Build the list of columns the foreign key will point # to. fk_refcols.append(desc.tablename + '.' + pk_col.name) # Build join clauses (in case we have a self-ref) if self.entity is self.target: joins[num].append(col == pk_col) constraints.append( ForeignKeyConstraint(fk_colnames, fk_refcols, name=fk_name)) args = columns + constraints self.secondary_table = Table(tablename, e1_desc.metadata, *args) def _reflect_table(self, tablename): if not self.target._descriptor.autoload: raise Exception( "Entity '%s' is autoloaded and its '%s' " "has_and_belongs_to_many relationship points to " "the '%s' entity which is not autoloaded" % (self.entity.__name__, self.name, self.target.__name__)) self.secondary_table = Table(tablename, self.entity._descriptor.metadata, autoload=True) # In the case we have a self-reference, we need to build join clauses if self.entity is self.target: #CHECKME: maybe we should try even harder by checking if that # information was defined on the inverse relationship) if not self.local_side and not self.remote_side: raise Exception( "Self-referential has_and_belongs_to_many " "relationships in autoloaded entities need to have at " "least one of either 'local_side' or 'remote_side' " "argument specified. The '%s' relationship in the '%s' " "entity doesn't have either." % (self.name, self.entity.__name__)) self.primaryjoin_clauses, self.secondaryjoin_clauses = \ _build_join_clauses(self.secondary_table, self.local_side, self.remote_side, self.entity.table) def create_properties(self): kwargs = self.kwargs if self.target is self.entity: kwargs['primaryjoin'] = and_(*self.primaryjoin_clauses) kwargs['secondaryjoin'] = and_(*self.secondaryjoin_clauses) if 'order_by' in kwargs: kwargs['order_by'] = \ self.target._descriptor.translate_order_by(kwargs['order_by']) self.property = relation(self.target, secondary=self.secondary_table, uselist=True, **kwargs) self.entity.mapper.add_property(self.name, self.property) def is_inverse(self, other): return super(HasAndBelongsToMany, self).is_inverse(other) and \ (self.user_tablename == other.user_tablename or (not self.user_tablename and not other.user_tablename)) def _build_join_clauses(local_table, local_cols1, local_cols2, target_table): primary_join, secondary_join = [], [] cols1 = local_cols1[:] cols1.sort() cols1 = tuple(cols1) if local_cols2 is not None: cols2 = local_cols2[:] cols2.sort() cols2 = tuple(cols2) else: cols2 = None constraint_map = {} for constraint in local_table.constraints: if isinstance(constraint, ForeignKeyConstraint): use_constraint = False fk_colnames = [] for fk in constraint.elements: fk_colnames.append(fk.parent.name) if fk.references(target_table): use_constraint = True if use_constraint: fk_colnames.sort() constraint_map[tuple(fk_colnames)] = constraint # Either the fk column names match explicitely with the columns given for # one of the joins (primary or secondary), or we assume the current # columns match because the columns for this join were not given and we # know the other join is either not used (is None) or has an explicit # match. for cols, constraint in constraint_map.iteritems(): if cols == cols1 or (cols != cols2 and not cols1 and (cols2 in constraint_map or cols2 is None)): join = primary_join elif cols == cols2 or (cols2 == () and cols1 in constraint_map): join = secondary_join else: continue for fk in constraint.elements: join.append(fk.parent == fk.column) return primary_join, secondary_join belongs_to = Statement(BelongsTo) has_one = Statement(HasOne) has_many = Statement(HasMany) has_and_belongs_to_many = Statement(HasAndBelongsToMany) PK¼$a6œJJelixir/fields.py''' Field statements for Elixir entities ====== Fields ====== This module contains DSL statements which allow you to declare which fields (columns) your Elixir entities have. There are currently two different statements that you can use to declare fields: `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. The following 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. | +-------------------+---------------------------------------------------------+ | ``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"`). | +-------------------+---------------------------------------------------------+ Any other keyword argument is passed on to the SQLAlchemy ``Column`` object. Please refer to the `SQLAlchemy Column object's documentation `_ for further detail about which keyword arguments are supported. Here is a quick example of how to use ``has_field``. :: class Person(Entity): has_field('id', Integer, primary_key=True) has_field('name', String(50)) `with_fields` ------------- The `with_fields` statement allows you to define fields all at once. Each keyword argument to this statement represents one field, which should be a `Field` object. The first argument to a Field object is its type. Following it, any number of keyword arguments can be specified for additional behavior. The `with_fields` statement supports the same keyword arguments than the `has_field` statement. Here is a quick example of how to use ``with_fields``. :: class Person(Entity): with_fields( id = Field(Integer, primary_key=True), name = Field(String(50)) ) ''' from sqlalchemy import Column from elixir.statements import Statement __all__ = ['has_field', 'with_fields', 'Field'] __pudge_all__ = ['Field'] class Field(object): ''' Represents the definition of a 'field' on an entity. This class represents a column on the table where the entity is stored. This object is only used with the `with_fields` syntax for defining all fields for an entity at the same time. The `has_field` syntax does not require the manual creation of this object. ''' def __init__(self, type, *args, **kwargs): self.colname = kwargs.pop('colname', None) self.deferred = kwargs.pop('deferred', False) if 'required' in kwargs: kwargs['nullable'] = not kwargs.pop('required') self.type = type self.primary_key = kwargs.get('primary_key', False) self.args = args self.kwargs = kwargs def column(self): ''' Returns the corresponding sqlalchemy-column ''' if not hasattr(self, '_column'): self._column = Column(self.colname, self.type, *self.args, **self.kwargs) return self._column column = property(column) class HasField(object): def __init__(self, entity, name, *args, **kwargs): field = Field(*args, **kwargs) field.colname = name entity._descriptor.add_field(field) class WithFields(object): def __init__(self, entity, *args, **fields): columns = list() desc = entity._descriptor for colname, field in fields.iteritems(): if not field.colname: field.colname = colname desc.add_field(field) has_field = Statement(HasField) with_fields = Statement(WithFields) PKält6ÂéoÌ Ì elixir/__init__.py''' Elixir package A declarative layer on top of SQLAlchemy, which is intended to replace the ActiveMapper SQLAlchemy extension, and the TurboEntity project. Elixir is a fairly thin wrapper around SQLAlchemy, which provides the ability to define model objects following the Active Record design pattern, and using a DSL syntax similar to that of the Ruby on Rails ActiveRecord system. Elixir does not intend to replace SQLAlchemy's core features, but instead focuses on providing a simpler syntax for defining model objects when you do not need the full expressiveness of SQLAlchemy's manual mapper definitions. For an example of how to use Elixir, please refer to the examples directory and the unit tests. The examples directory includes a TurboGears application with full identity support called 'videostore'. ''' import sqlalchemy from sqlalchemy.ext.sessioncontext import SessionContext from sqlalchemy.types import * from elixir.options import * from elixir.entity import Entity, EntityDescriptor from elixir.fields import * from elixir.relationships import * try: set except NameError: from sets import Set as set __version__ = '0.3.0' __all__ = ['Entity', 'Field', 'has_field', 'with_fields', 'belongs_to', 'has_one', 'has_many', 'has_and_belongs_to_many', 'using_options', 'using_table_options', 'using_mapper_options', 'options_defaults', 'metadata', 'objectstore', 'create_all', 'drop_all', 'setup_all', 'cleanup_all', 'delay_setup'] + \ sqlalchemy.types.__all__ __pudge_all__ = ['create_all', 'drop_all', 'setup_all', 'cleanup_all', 'metadata', 'objectstore', 'delay_setup'] # connect metadata = sqlalchemy.DynamicMetaData('elixir', threadlocal=False) try: # this only happens when the threadlocal extension is used objectstore = sqlalchemy.objectstore except AttributeError: # thread local SessionContext class Objectstore(object): def __init__(self, *args, **kwargs): self.context = SessionContext(*args, **kwargs) def __getattr__(self, name): return getattr(self.context.current, name) session = property(lambda s:s.context.current) objectstore = Objectstore(sqlalchemy.create_session) metadatas = set() def create_all(): 'Create all necessary tables for all declared entities' for md in metadatas: md.create_all() def drop_all(): 'Drop all tables for all declared entities' for md in metadatas: md.drop_all() delayed_entities = set() delay_setup = False def setup_all(): '''Setup the table and mapper for all entities which have been delayed. This should be used in conjunction with setting ``delay_setup`` to ``True`` before defining your entities. ''' for entity in delayed_entities: entity.setup_table() for entity in delayed_entities: entity.setup_mapper() # setup all relationships for entity in delayed_entities: for rel in entity.relationships.itervalues(): rel.setup() delayed_entities.clear() # issue the "CREATE" SQL statements create_all() def cleanup_all(): '''Drop table and clear mapper for all entities, and clear all metadatas. ''' drop_all() for md in metadatas: md.clear() metadatas.clear() EntityDescriptor.uninitialized_rels.clear() objectstore.clear() sqlalchemy.clear_mappers() PK—"8[çWäjäjelixir/relationships.pyc;ò ª Fc@sdZdklZlZlZlZlZdklZdk l Z dk l Z dk Z dddd gZgZd efd „ƒYZd efd „ƒYZdefd„ƒYZdefd„ƒYZdefd„ƒYZd„ZeeƒZeeƒZeeƒZeeƒZdS(s% Relationship statements for Elixir entities ============= 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``. .. _order_by: 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. | +--------------------+--------------------------------------------------------+ (srelationsForeignKeyConstraintsColumnsTablesand_(s Statement(sField(sEntityDescriptorNs belongs_toshas_oneshas_manyshas_and_belongs_to_manys RelationshipcBswtZdZd„Zd„Zd„Zd„Zd„Zd„Ze eƒZd„Z e e ƒZ d„Z d „Z RS( s' Base class for relationships. cOs€||_||_|idƒ|_|idtƒ|_t|_t|_ t|_ ||_ ||_||ii i |i|iD]3} |i| iiƒ| i|ƒo t} qŸqŸW| o|iƒ||t|ƒ`_. You might also be interested in the section about `constraints `_. `using_mapper_options` ---------------------- The 'using_mapper_options' DSL statement allows you to set up some additional options on your entity mapper. It is meant only to handle the options which are not supported directly by the 'using_options' statement. By opposition to the 'using_options' statement, these options are passed directly to the underlying SQLAlchemy mapper (as keyword arguments) without any processing. For further information, please refer to the `SQLAlchemy mapper function's documentation `_. ''' from elixir.statements import Statement __all__ = ['using_options', 'using_table_options', 'using_mapper_options', 'options_defaults'] __pudge_all__ = ['options_defaults'] options_defaults = dict( inheritance='single', autoload=None, shortnames=False, tablename=None, auto_primarykey=True, version_id_col=False, mapper_options=dict(), table_options=dict(), ) class UsingOptions(object): valid_options = ( 'inheritance', 'metadata', 'autoload', 'tablename', 'shortnames', 'auto_primarykey', 'version_id_col', 'order_by', ) def __init__(self, entity, *args, **kwargs): desc = entity._descriptor for kwarg in kwargs: if kwarg in UsingOptions.valid_options: setattr(desc, kwarg, kwargs[kwarg]) else: raise Exception("'%s' is not a valid option for Elixir " "entities." % kwarg) class UsingTableOptions(object): def __init__(self, entity, *args, **kwargs): entity._descriptor.table_args = list(args) entity._descriptor.table_options.update(kwargs) class UsingMapperOptions(object): def __init__(self, entity, *args, **kwargs): entity._descriptor.mapper_options.update(kwargs) using_options = Statement(UsingOptions) using_table_options = Statement(UsingTableOptions) using_mapper_options = Statement(UsingMapperOptions) PKØ,v6azŠDDelixir/statements.pyimport sys __pudge_all__ = [] STATEMENTS = '__elixir_statements__' class Statement(object): ''' DSL-style syntax A ``Statement`` object represents a DSL term. ''' def __init__(self, target): ''' target is the class which will handle this statement. For example, the BelongsTo class handles the belongs_to statement. ''' self.target = target def __call__(self, *args, **kwargs): # jam this statement into the class's statement list class_locals = sys._getframe(1).f_locals statements = class_locals.setdefault(STATEMENTS, []) statements.append((self, args, kwargs)) def process(cls, entity): ''' Apply all statements to the given entity. ''' # loop over all statements in the class's statement list # and apply them, i.e. instanciate the corresponding classes for statement, args, kwargs in getattr(entity, STATEMENTS, []): statement.target(entity, *args, **kwargs) process = classmethod(process) PK3 {6‘‹V.V.elixir/entity.py''' Entity baseclass, metaclass and descriptor ''' from sqlalchemy import Table, Integer, desc, deferred from sqlalchemy.ext.assignmapper import assign_mapper from sqlalchemy.util import OrderedDict from elixir.statements import Statement from elixir.fields import Field from elixir.options import options_defaults try: set except NameError: from sets import Set as set import sys import elixir __all__ = ['Entity'] __pudge_all__ = __all__ DEFAULT_AUTO_PRIMARYKEY_NAME = "id" DEFAULT_AUTO_PRIMARYKEY_TYPE = Integer DEFAULT_VERSION_ID_COL = "row_version" class EntityDescriptor(object): ''' EntityDescriptor describes fields and options needed for table creation. ''' uninitialized_rels = set() current = None def __init__(self, entity): entity.table = None entity.mapper = None self.entity = entity self.module = sys.modules[entity.__module__] self.primary_keys = list() self.parent = None for base in entity.__bases__: if issubclass(base, Entity) and base is not Entity: if self.parent: raise Exception('%s entity inherits from several entities,' ' and this is not supported.' % self.entity.__name__) else: self.parent = base self.fields = OrderedDict() self.relationships = dict() self.constraints = list() #CHECKME: this is a workaround for the "current" descriptor/target # property ugliness. The problem is that this workaround is ugly too. # I'm not sure if this is a safe practice. It works but...? # setattr(self.module, entity.__name__, entity) # set default value for options self.order_by = None self.table_args = list() self.metadata = getattr(self.module, 'metadata', elixir.metadata) for option in ('inheritance', 'autoload', 'tablename', 'shortnames', 'auto_primarykey', 'version_id_col'): setattr(self, option, options_defaults[option]) for option_dict in ('mapper_options', 'table_options'): setattr(self, option_dict, options_defaults[option_dict].copy()) def setup_options(self): ''' Setup any values that might depend on using_options. For example, the tablename or the metadata. ''' elixir.metadatas.add(self.metadata) entity = self.entity if not self.tablename: if self.shortnames: self.tablename = entity.__name__.lower() else: modulename = entity.__module__.replace('.', '_') tablename = "%s_%s" % (modulename, entity.__name__) self.tablename = tablename.lower() elif callable(self.tablename): self.tablename = self.tablename(entity) def setup(self): ''' Create tables, keys, columns that have been specified so far and assign a mapper. Will be called when an instance of the entity is created or a mapper is needed to access one or many instances of the entity. It will try to initialize the entity's relationships (along with any delayed relationship) but some of them might be delayed. ''' if elixir.delay_setup: elixir.delayed_entities.add(self) return self.setup_table() self.setup_mapper() # This marks all relations of the entity (or, at least those which # have been added so far by statements) as being uninitialized EntityDescriptor.uninitialized_rels.update( self.relationships.values()) # try to setup all uninitialized relationships EntityDescriptor.setup_relationships() def translate_order_by(self, order_by): if isinstance(order_by, basestring): order_by = [order_by] order = list() for field in order_by: col = self.fields[field.strip('-')].column if field.startswith('-'): col = desc(col) order.append(col) return order def setup_mapper(self): ''' Initializes and assign an (empty!) mapper to the entity. ''' if self.entity.mapper: return session = getattr(self.module, 'session', elixir.objectstore) kwargs = self.mapper_options if self.order_by: kwargs['order_by'] = self.translate_order_by(self.order_by) if self.version_id_col: kwargs['version_id_col'] = self.fields[self.version_id_col].column if self.parent: if self.inheritance == 'single': # at this point, we don't know whether the parent relationships # have already been processed or not. Some of them might be, # some other might not. if not self.parent.mapper: self.parent._descriptor.setup_mapper() kwargs['inherits'] = self.parent.mapper properties = dict() for field in self.fields.itervalues(): if field.deferred: group = None if isinstance(field.deferred, basestring): group = field.deferred properties[field.column.name] = deferred(field.column, group=group) assign_mapper(session.context, self.entity, self.entity.table, properties=properties, **kwargs) def setup_table(self): ''' Create a SQLAlchemy table-object with all columns that have been defined up to this point. ''' if self.entity.table: return if self.parent: if self.inheritance == 'single': # reuse the parent's table if not self.parent.table: self.parent._descriptor.setup_table() self.entity.table = self.parent.table self.primary_keys = self.parent._descriptor.primary_keys # re-add the entity fields to the parent entity so that they # are added to the parent's table (whether the parent's table # is setup already or not). for field in self.fields.itervalues(): self.parent._descriptor.add_field(field) return # elif self.inheritance == 'concrete': # do not reuse parent table, but copy all fields # the problem is that, at this points, all "plain" fields # are known, but not those generated by relations # for field in self.fields.itervalues(): # self.add_field(field) if self.version_id_col: if not isinstance(self.version_id_col, basestring): self.version_id_col = DEFAULT_VERSION_ID_COL self.add_field(Field(Integer, colname=self.version_id_col)) if not self.autoload: if not self.primary_keys and self.auto_primarykey: self.create_auto_primary_key() # create list of columns and constraints args = [field.column for field in self.fields.itervalues()] \ + self.constraints + self.table_args # specify options kwargs = self.table_options if self.autoload: kwargs['autoload'] = True self.entity.table = Table(self.tablename, self.metadata, *args, **kwargs) def create_auto_primary_key(self): ''' Creates a primary key ''' assert not self.primary_keys and self.auto_primarykey if isinstance(self.auto_primarykey, basestring): colname = self.auto_primarykey else: colname = DEFAULT_AUTO_PRIMARYKEY_NAME self.add_field(Field(DEFAULT_AUTO_PRIMARYKEY_TYPE, colname=colname, primary_key=True)) def add_field(self, field): self.fields[field.colname] = field if field.primary_key: self.primary_keys.append(field) table = self.entity.table if table: table.append_column(field.column) def add_constraint(self, constraint): self.constraints.append(constraint) table = self.entity.table if table: table.append_constraint(constraint) def get_inverse_relation(self, rel, reverse=False): ''' Return the inverse relation of rel, if any, None otherwise. ''' matching_rel = None for other_rel in self.relationships.itervalues(): if other_rel.is_inverse(rel): if matching_rel is None: matching_rel = other_rel else: raise Exception( "Several relations match as inverse of the '%s' " "relation in entity '%s'. You should specify " "inverse relations manually by using the inverse " "keyword." % (rel.name, rel.entity.__name__)) # When a matching inverse is found, we check that it has only # one relation matching as its own inverse. We don't need the result # of the method though. But we do need to be careful not to start an # infinite recursive loop. if matching_rel and not reverse: rel.entity._descriptor.get_inverse_relation(matching_rel, True) return matching_rel def all_relationships(self): if self.parent: res = self.parent._descriptor.all_relationships else: res = dict() res.update(self.relationships) return res all_relationships = property(all_relationships) def setup_relationships(cls): for relationship in list(EntityDescriptor.uninitialized_rels): if relationship.setup(): EntityDescriptor.uninitialized_rels.remove(relationship) setup_relationships = classmethod(setup_relationships) class EntityMeta(type): """ Entity meta class. """ def __init__(cls, name, bases, dict_): # only process subclasses of Entity, not Entity itself if bases[0] is object: return # create the entity descriptor desc = cls._descriptor = EntityDescriptor(cls) EntityDescriptor.current = desc # process statements Statement.process(cls) # setup misc options here (like tablename etc.) desc.setup_options() # create table & assign (empty) mapper desc.setup() class Entity(object): ''' The base class for all entities All Elixir model objects should inherit from this class. Statements can appear within the body of the definition of an entity to define its fields, relationships, and other options. Here is an example: :: class Person(Entity): has_field('name', Unicode(128)) has_field('birthdate', DateTime, default=datetime.now) Please note, that if you don't specify any primary keys, Elixir will automatically create one called ``id``. For further information, please refer to the provided examples or tutorial. ''' __metaclass__ = EntityMeta PK—"8Êè¿elixir/__init__.pyc;ò M*Fc@s}dZdkZdklZdkTdkTdklZlZdk Tdk Tye Wn e j odk lZ nXdZddd d d d d ddddddddddddgeiiZdddddddgZeiddeƒZy eiZWn8ej o,defd„ƒYZeeiƒZnXe ƒZd„Zd„Ze ƒZeZd „Z d!„Z!dS("s. Elixir package A declarative layer on top of SQLAlchemy, which is intended to replace the ActiveMapper SQLAlchemy extension, and the TurboEntity project. Elixir is a fairly thin wrapper around SQLAlchemy, which provides the ability to define model objects following the Active Record design pattern, and using a DSL syntax similar to that of the Ruby on Rails ActiveRecord system. Elixir does not intend to replace SQLAlchemy's core features, but instead focuses on providing a simpler syntax for defining model objects when you do not need the full expressiveness of SQLAlchemy's manual mapper definitions. For an example of how to use Elixir, please refer to the examples directory and the unit tests. The examples directory includes a TurboGears application with full identity support called 'videostore'. N(sSessionContext(s*(sEntitysEntityDescriptor(sSets0.3.0sEntitysFields has_fields with_fieldss belongs_toshas_oneshas_manyshas_and_belongs_to_manys using_optionssusing_table_optionssusing_mapper_optionssoptions_defaultssmetadatas objectstores create_allsdrop_alls setup_alls cleanup_alls delay_setupselixirs threadlocals ObjectstorecBs)tZd„Zd„Zed„ƒZRS(NcOst||Ž|_dS(N(sSessionContextsargsskwargssselfscontext(sselfsargsskwargs((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pys__init__8scCst|ii|ƒSdS(N(sgetattrsselfscontextscurrentsname(sselfsname((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pys __getattr__;scCs |iiS(N(ssscontextscurrent(ss((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pys=s(s__name__s __module__s__init__s __getattr__spropertyssession(((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pys Objectstore6s  cCsxtD]}|iƒqWdS(s5Create all necessary tables for all declared entitiesN(s metadatassmds create_all(smd((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pys create_allDscCsxtD]}|iƒqWdS(s)Drop all tables for all declared entitiesN(s metadatassmdsdrop_all(smd((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pysdrop_allJscCs€xtD]}|iƒqWxtD]}|iƒq"Wx2tD]*}x!|iiƒD]}|iƒqSWq=Wtiƒt ƒdS(s½Setup the table and mapper for all entities which have been delayed. This should be used in conjunction with setting ``delay_setup`` to ``True`` before defining your entities. N( sdelayed_entitiessentitys setup_tables setup_mappers relationshipss itervaluessrelssetupsclears create_all(srelsentity((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pys setup_allSs cCsQtƒxtD]}|iƒqWtiƒtiiƒtiƒtiƒdS(sKDrop table and clear mapper for all entities, and clear all metadatas. N( sdrop_alls metadatassmdsclearsEntityDescriptorsuninitialized_relss objectstores sqlalchemys clear_mappers(smd((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pys cleanup_allis   ("s__doc__s sqlalchemyssqlalchemy.ext.sessioncontextsSessionContextssqlalchemy.typesselixir.optionss elixir.entitysEntitysEntityDescriptors elixir.fieldsselixir.relationshipsssets NameErrorssetssSets __version__stypess__all__s __pudge_all__sDynamicMetaDatasFalsesmetadatas objectstoresAttributeErrorsobjects Objectstorescreate_sessions metadatass create_allsdrop_allsdelayed_entitiess delay_setups setup_alls cleanup_all(ssets Objectstores create_alls cleanup_alls setup_alls metadatass__all__s objectstoresEntityDescriptorsdelayed_entitiessdrop_allsmetadatas sqlalchemys delay_setups __version__sEntitys __pudge_all__sSessionContext((s3build/bdist.darwin-8.0.1-x86/egg/elixir/__init__.pys?s6  I       PK—"8½ºñ¦#¦#elixir/options.pyc;ò ºÞFc@s×dZdklZddddgZdgZeddded ed ed ed ed eƒdeƒƒZ de fd„ƒYZ de fd„ƒYZ de fd„ƒYZ ee ƒZee ƒZee ƒZdS(s/ Option statements for Elixir entities ======= Options ======= This module provides DSL statements for defining options on your Elixir entities. There are three different kinds of options that can be set up, and for this there are three different statements: using_options_, using_table_options_ and using_mapper_options_. Alternatively, options can be set on all Elixir entities by modifying the `options_defaults` dictionary before defining any entity. `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: :: class Person(Entity): has_field('name', Unicode(64)) using_options(shortnames=True, order_by='name') The list of supported arguments are as follows: +---------------------+-------------------------------------------------------+ | Option Name | Description | +=====================+=======================================================+ | ``inheritance`` | Specify the type of inheritance this entity must use. | | | It can be one of ``single``, ``concrete`` or | | | ``multi``. | | | **For now, only the single type is implemented.** | +---------------------+-------------------------------------------------------+ | ``metadata`` | Specify a custom MetaData | +---------------------+-------------------------------------------------------+ | ``autoload`` | Automatically load column definitions from the | | | existing database table. | | | Using autoloaded tables implies setting | | | ``delay_setup`` to ``True`` before defining your | | | entities. | +---------------------+-------------------------------------------------------+ | ``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. | +---------------------+-------------------------------------------------------+ | ``shortnames`` | Usually tablenames include the full module-path | | | to the entity, but lower-cased and separated by | | | underscores ("_"), eg.: "project1_model_myentity" | | | for an entity named "MyEntity" in the module | | | "project1.model". If shortnames is True, the | | | tablename will just be the entity's classname | | | lower-cased, ie. "myentity". | +---------------------+-------------------------------------------------------+ | ``auto_primarykey`` | If given as string, it will represent the | | | auto-primary-key's column name. If this option | | | is True, it will allow auto-creation of a primary | | | key if there's no primary key defined for the | | | corresponding entity. If this option is False, | | | it will disallow auto-creation of a primary key. | +---------------------+-------------------------------------------------------+ | ``version_id_col`` | If this option is True, it will create a version | | | column automatically using the default name. If given | | | as string, it will create the column using that name. | | | This can be used to prevent concurrent modifications | | | to the entity's table rows (i.e. it will raise an | | | exception if it happens). | +---------------------+-------------------------------------------------------+ | ``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 (descending order). | +---------------------+-------------------------------------------------------+ For examples, please refer to the examples and unit tests. `using_table_options` --------------------- The 'using_table_options' DSL statement allows you to set up some additional options on your entity table. It is meant only to handle the options which are not supported directly by the 'using_options' statement. By opposition to the 'using_options' statement, these options are passed directly to the underlying SQLAlchemy Table object (both non-keyword arguments and keyword arguments) without any processing. For further information, please refer to the `SQLAlchemy table's documentation `_. You might also be interested in the section about `constraints `_. `using_mapper_options` ---------------------- The 'using_mapper_options' DSL statement allows you to set up some additional options on your entity mapper. It is meant only to handle the options which are not supported directly by the 'using_options' statement. By opposition to the 'using_options' statement, these options are passed directly to the underlying SQLAlchemy mapper (as keyword arguments) without any processing. For further information, please refer to the `SQLAlchemy mapper function's documentation `_. (s Statements using_optionssusing_table_optionssusing_mapper_optionssoptions_defaultss inheritancessinglesautoloads shortnamess tablenamesauto_primarykeysversion_id_colsmapper_optionss table_optionss UsingOptionscBs/tZddddddddfZd „ZRS( Ns inheritancesmetadatasautoloads tablenames shortnamessauto_primarykeysversion_id_colsorder_bycOsV|i}xF|D]>}|tijot||||ƒqtd|ƒ‚qWdS(Ns/'%s' is not a valid option for Elixir entities.( sentitys _descriptorsdescskwargsskwargs UsingOptionss valid_optionsssetattrs Exception(sselfsentitysargsskwargsskwargsdesc((s2build/bdist.darwin-8.0.1-x86/egg/elixir/options.pys__init__Žs  (s__name__s __module__s valid_optionss__init__(((s2build/bdist.darwin-8.0.1-x86/egg/elixir/options.pys UsingOptions‚s sUsingTableOptionscBstZd„ZRS(NcOs)t|ƒ|i_|iii|ƒdS(N(slistsargssentitys _descriptors table_argss table_optionssupdateskwargs(sselfsentitysargsskwargs((s2build/bdist.darwin-8.0.1-x86/egg/elixir/options.pys__init__›s(s__name__s __module__s__init__(((s2build/bdist.darwin-8.0.1-x86/egg/elixir/options.pysUsingTableOptions™ssUsingMapperOptionscBstZd„ZRS(NcOs|iii|ƒdS(N(sentitys _descriptorsmapper_optionssupdateskwargs(sselfsentitysargsskwargs((s2build/bdist.darwin-8.0.1-x86/egg/elixir/options.pys__init__¢s(s__name__s __module__s__init__(((s2build/bdist.darwin-8.0.1-x86/egg/elixir/options.pysUsingMapperOptions sN(s__doc__selixir.statementss Statements__all__s __pudge_all__sdictsNonesFalsesTruesoptions_defaultssobjects UsingOptionssUsingTableOptionssUsingMapperOptionss using_optionssusing_table_optionssusing_mapper_options( sUsingTableOptionssusing_table_optionss__all__soptions_defaultssusing_mapper_optionss UsingOptionss StatementsUsingMapperOptionss __pudge_all__s using_options((s2build/bdist.darwin-8.0.1-x86/egg/elixir/options.pys?ns"      PK—"8€ÿ”þ55elixir/statements.pyc;ò ¹\Fc@s/dkZgZdZdefd„ƒYZdS(Ns__elixir_statements__s StatementcBs5tZdZd„Zd„Zd„ZeeƒZRS(sQ DSL-style syntax A ``Statement`` object represents a DSL term. cCs ||_dS(s’ target is the class which will handle this statement. For example, the BelongsTo class handles the belongs_to statement. N(stargetsself(sselfstarget((s5build/bdist.darwin-8.0.1-x86/egg/elixir/statements.pys__init__scOs>tidƒi}|itgƒ}|i|||fƒdS(Ni( ssyss _getframesf_localss class_localss setdefaults STATEMENTSs statementssappendsselfsargsskwargs(sselfsargsskwargss statementss class_locals((s5build/bdist.darwin-8.0.1-x86/egg/elixir/statements.pys__call__scCs=x6t|tgƒD]"\}}}|i|||ŽqWdS(s; Apply all statements to the given entity. N(sgetattrsentitys STATEMENTSs statementsargsskwargsstarget(sclssentitysargss statementskwargs((s5build/bdist.darwin-8.0.1-x86/egg/elixir/statements.pysprocesss(s__name__s __module__s__doc__s__init__s__call__sprocesss classmethod(((s5build/bdist.darwin-8.0.1-x86/egg/elixir/statements.pys Statements    (ssyss __pudge_all__s STATEMENTSsobjects Statement(ssyss __pudge_all__s STATEMENTSs Statement((s5build/bdist.darwin-8.0.1-x86/egg/elixir/statements.pys?s PK—"8“×2¤EGG-INFO/zip-safePK—"8΂V V ¤0EGG-INFO/SOURCES.txtPK—"8“×2¤¸ EGG-INFO/dependency_links.txtPK—"8Övª77¤ô EGG-INFO/PKG-INFOPK—"8ù´,‚žž¤ZEGG-INFO/requires.txtPK—"8—ì§»¤+EGG-INFO/top_level.txtPK—"8… ûû¤felixir/fields.pycPK—"8òêò$00¤*elixir/entity.pycPK{<{6¸Ì¤òÐwÐw¤ÑZelixir/relationships.pyPK¼$a6œJJ¤ÖÒelixir/fields.pyPKält6ÂéoÌ Ì ¤Næelixir/__init__.pyPK—"8[çWäjäj¤Jôelixir/relationships.pycPKã {6ˆZ†S¤d_elixir/options.pyPKØ,v6azŠDD¤¦}elixir/statements.pyPK3 {6‘‹V.V.¤‚elixir/entity.pyPK—"8Ê迤 °elixir/__init__.pycPK—"8½ºñ¦#¦#¤íÄelixir/options.pycPK—"8€ÿ”þ55¤Ãèelixir/statements.pycPKœ+ð