diff options
Diffstat (limited to 'lib/sqlalchemy/ext/declarative.py')
-rw-r--r-- | lib/sqlalchemy/ext/declarative.py | 448 |
1 files changed, 290 insertions, 158 deletions
diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index 6d3941858..65c9df133 100644 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -1,13 +1,16 @@ -"""A simple declarative layer for SQLAlchemy ORM. - +""" Synopsis ======== -SQLAlchemy object-relational configuration involves the usage of :class:`~sqlalchemy.schema.Table`, -:func:`~sqlalchemy.orm.mapper`, and class objects to define the three areas of configuration. -``declarative`` moves these three types of configuration underneath the individual -mapped class. Regular SQLAlchemy schema and ORM constructs are used in most -cases:: +SQLAlchemy object-relational configuration involves the use of +:class:`~sqlalchemy.schema.Table`, :func:`~sqlalchemy.orm.mapper`, and +class objects to define the three areas of configuration. +:mod:`~sqlalchemy.ext.declarative` allows all three types of +configuration to be expressed declaratively on an individual +mapped class. Regular SQLAlchemy schema elements and ORM constructs +are used in most cases. + +As a simple example:: from sqlalchemy.ext.declarative import declarative_base @@ -18,16 +21,21 @@ cases:: id = Column(Integer, primary_key=True) name = Column(String(50)) -Above, the :func:`declarative_base` callable produces a new base class from which -all mapped classes inherit from. When the class definition is completed, a -new :class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.orm.mapper` have been generated, accessible via the -``__table__`` and ``__mapper__`` attributes on the ``SomeClass`` class. +Above, the :func:`declarative_base` callable returns a new base class from which +all mapped classes should inherit. When the class definition is completed, a +new :class:`~sqlalchemy.schema.Table` and +:class:`~sqlalchemy.orm.mapper` will have been generated, accessible +via the ``__table__`` and ``__mapper__`` attributes on the ``SomeClass`` class. Defining Attributes =================== -:class:`~sqlalchemy.schema.Column` objects may be explicitly named, -including using a different name than the attribute in which they are associated. +In the above example, the :class:`~sqlalchemy.schema.Column` objects are +automatically named with the name of the attribute to which they are +assigned. + +They can also be explicitly named, and that name does not have to be +the same as name assigned on the class. The column will be assigned to the :class:`~sqlalchemy.schema.Table` using the given name, and mapped to the class using the attribute name:: @@ -36,52 +44,54 @@ given name, and mapped to the class using the attribute name:: id = Column("some_table_id", Integer, primary_key=True) name = Column("name", String(50)) -Otherwise, you may omit the names from the Column definitions. -Declarative will set the ``name`` attribute on the column when the class -is initialized:: - - class SomeClass(Base): - __tablename__ = 'some_table' - id = Column(Integer, primary_key=True) - name = Column(String(50)) - Attributes may be added to the class after its construction, and they will be -added to the underlying :class:`~sqlalchemy.schema.Table` and :func:`~sqlalchemy.orm.mapper()` definitions as -appropriate:: +added to the underlying :class:`~sqlalchemy.schema.Table` and +:func:`~sqlalchemy.orm.mapper()` definitions as appropriate:: SomeClass.data = Column('data', Unicode) SomeClass.related = relation(RelatedInfo) -Classes which are mapped explicitly using :func:`~sqlalchemy.orm.mapper()` can interact freely -with declarative classes. It is recommended, though not required, that all tables -share the same underlying :class:`~sqlalchemy.schema.MetaData` object, so that -string-configured :class:`~sqlalchemy.schema.ForeignKey` references can be resolved without issue. +Classes which are mapped explicitly using +:func:`~sqlalchemy.orm.mapper()` can interact freely with declarative +classes. + +It is recommended, though not required, that all tables +share the same underlying :class:`~sqlalchemy.schema.MetaData` object, +so that string-configured :class:`~sqlalchemy.schema.ForeignKey` +references can be resolved without issue. Association of Metadata and Engine ================================== -The :func:`declarative_base` base class contains a :class:`~sqlalchemy.schema.MetaData` object where newly -defined :class:`~sqlalchemy.schema.Table` objects are collected. This is accessed via the -:class:`~sqlalchemy.schema.MetaData` class level accessor, so to create tables we can say:: +The :func:`declarative_base` base class contains a +:class:`~sqlalchemy.schema.MetaData` object where newly +defined :class:`~sqlalchemy.schema.Table` objects are collected. This +is accessed via the :class:`~sqlalchemy.schema.MetaData` class level +accessor, so to create tables we can say:: engine = create_engine('sqlite://') Base.metadata.create_all(engine) -The :class:`~sqlalchemy.engine.base.Engine` created above may also be directly associated with the -declarative base class using the ``bind`` keyword argument, where it will be -associated with the underlying :class:`~sqlalchemy.schema.MetaData` object and allow SQL operations +The :class:`~sqlalchemy.engine.base.Engine` created above may also be +directly associated with the declarative base class using the ``bind`` +keyword argument, where it will be associated with the underlying +:class:`~sqlalchemy.schema.MetaData` object and allow SQL operations involving that metadata and its tables to make use of that engine automatically:: Base = declarative_base(bind=create_engine('sqlite://')) -Or, as :class:`~sqlalchemy.schema.MetaData` allows, at any time using the ``bind`` attribute:: +Alternatively, by way of the normal +:class:`~sqlalchemy.schema.MetaData` behaviour, the ``bind`` attribute +of the class level accessor can be assigned at any time as follows:: Base.metadata.bind = create_engine('sqlite://') -The :func:`declarative_base` can also receive a pre-created :class:`~sqlalchemy.schema.MetaData` object, -which allows a declarative setup to be associated with an already existing -traditional collection of :class:`~sqlalchemy.schema.Table` objects:: +The :func:`declarative_base` can also receive a pre-created +:class:`~sqlalchemy.schema.MetaData` object, which allows a +declarative setup to be associated with an already +existing traditional collection of :class:`~sqlalchemy.schema.Table` +objects:: mymetadata = MetaData() Base = declarative_base(metadata=mymetadata) @@ -89,11 +99,12 @@ traditional collection of :class:`~sqlalchemy.schema.Table` objects:: Configuring Relations ===================== -Relations to other classes are done in the usual way, with the added feature -that the class specified to :func:`~sqlalchemy.orm.relation()` may be a string name. The "class -registry" associated with ``Base`` is used at mapper compilation time to -resolve the name into the actual class object, which is expected to have been -defined once the mapper configuration is used:: +Relations to other classes are done in the usual way, with the added +feature that the class specified to :func:`~sqlalchemy.orm.relation()` +may be a string name. The "class registry" associated with ``Base`` +is used at mapper compilation time to resolve the name into the actual +class object, which is expected to have been defined once the mapper +configuration is used:: class User(Base): __tablename__ = 'users' @@ -109,8 +120,9 @@ defined once the mapper configuration is used:: email = Column(String(50)) user_id = Column(Integer, ForeignKey('users.id')) -Column constructs, since they are just that, are immediately usable, as below -where we define a primary join condition on the ``Address`` class using them:: +Column constructs, since they are just that, are immediately usable, +as below where we define a primary join condition on the ``Address`` +class using them:: class Address(Base): __tablename__ = 'addresses' @@ -120,52 +132,60 @@ where we define a primary join condition on the ``Address`` class using them:: user_id = Column(Integer, ForeignKey('users.id')) user = relation(User, primaryjoin=user_id == User.id) -In addition to the main argument for :func:`~sqlalchemy.orm.relation`, other arguments -which depend upon the columns present on an as-yet undefined class -may also be specified as strings. These strings are evaluated as -Python expressions. The full namespace available within this -evaluation includes all classes mapped for this declarative base, -as well as the contents of the ``sqlalchemy`` package, including -expression functions like :func:`~sqlalchemy.sql.expression.desc` and :attr:`~sqlalchemy.sql.expression.func`:: +In addition to the main argument for :func:`~sqlalchemy.orm.relation`, +other arguments which depend upon the columns present on an as-yet +undefined class may also be specified as strings. These strings are +evaluated as Python expressions. The full namespace available within +this evaluation includes all classes mapped for this declarative base, +as well as the contents of the ``sqlalchemy`` package, including +expression functions like :func:`~sqlalchemy.sql.expression.desc` and +:attr:`~sqlalchemy.sql.expression.func`:: class User(Base): # .... - addresses = relation("Address", order_by="desc(Address.email)", - primaryjoin="Address.user_id==User.id") + addresses = relation("Address", + order_by="desc(Address.email)", + primaryjoin="Address.user_id==User.id") As an alternative to string-based attributes, attributes may also be defined after all classes have been created. Just add them to the target class after the fact:: - User.addresses = relation(Address, primaryjoin=Address.user_id == User.id) + User.addresses = relation(Address, + primaryjoin=Address.user_id==User.id) Configuring Many-to-Many Relations ================================== -There's nothing special about many-to-many with declarative. The ``secondary`` -argument to :func:`~sqlalchemy.orm.relation` still requires a :class:`~sqlalchemy.schema.Table` object, not a declarative class. -The :class:`~sqlalchemy.schema.Table` should share the same :class:`~sqlalchemy.schema.MetaData` object used by the declarative base:: - - keywords = Table('keywords', Base.metadata, - Column('author_id', Integer, ForeignKey('authors.id')), - Column('keyword_id', Integer, ForeignKey('keywords.id')) - ) +There's nothing special about many-to-many with declarative. The +``secondary`` argument to :func:`~sqlalchemy.orm.relation` still +requires a :class:`~sqlalchemy.schema.Table` object, not a declarative +class. The :class:`~sqlalchemy.schema.Table` should share the same +:class:`~sqlalchemy.schema.MetaData` object used by the declarative +base:: + + keywords = Table( + 'keywords', Base.metadata, + Column('author_id', Integer, ForeignKey('authors.id')), + Column('keyword_id', Integer, ForeignKey('keywords.id')) + ) class Author(Base): __tablename__ = 'authors' id = Column(Integer, primary_key=True) keywords = relation("Keyword", secondary=keywords) -You should generally **not** map a class and also specify its table in a many-to-many -relation, since the ORM may issue duplicate INSERT and DELETE statements. +You should generally **not** map a class and also specify its table in +a many-to-many relation, since the ORM may issue duplicate INSERT and +DELETE statements. Defining Synonyms ================= -Synonyms are introduced in :ref:`synonyms`. To define a getter/setter which -proxies to an underlying attribute, use :func:`~sqlalchemy.orm.synonym` with the -``descriptor`` argument:: +Synonyms are introduced in :ref:`synonyms`. To define a getter/setter +which proxies to an underlying attribute, use +:func:`~sqlalchemy.orm.synonym` with the ``descriptor`` argument:: class MyClass(Base): __tablename__ = 'sometable' @@ -185,8 +205,8 @@ class-level expression construct:: x.attr = "some value" session.query(MyClass).filter(MyClass.attr == 'some other value').all() -For simple getters, the :func:`synonym_for` decorator can be used in conjunction -with ``@property``:: +For simple getters, the :func:`synonym_for` decorator can be used in +conjunction with ``@property``:: class MyClass(Base): __tablename__ = 'sometable' @@ -198,8 +218,8 @@ with ``@property``:: def attr(self): return self._some_attr -Similarly, :func:`comparable_using` is a front end for the :func:`~sqlalchemy.orm.comparable_property` -ORM function:: +Similarly, :func:`comparable_using` is a front end for the +:func:`~sqlalchemy.orm.comparable_property` ORM function:: class MyClass(Base): __tablename__ = 'sometable' @@ -214,18 +234,21 @@ ORM function:: Table Configuration =================== -Table arguments other than the name, metadata, and mapped Column arguments -are specified using the ``__table_args__`` class attribute. This attribute -accommodates both positional as well as keyword arguments that are normally -sent to the :class:`~sqlalchemy.schema.Table` constructor. The attribute can be specified -in one of two forms. One is as a dictionary:: +Table arguments other than the name, metadata, and mapped Column +arguments are specified using the ``__table_args__`` class attribute. +This attribute accommodates both positional as well as keyword +arguments that are normally sent to the +:class:`~sqlalchemy.schema.Table` constructor. +The attribute can be specified in one of two forms. One is as a +dictionary:: class MyClass(Base): __tablename__ = 'sometable' __table_args__ = {'mysql_engine':'InnoDB'} -The other, a tuple of the form ``(arg1, arg2, ..., {kwarg1:value, ...})``, which -allows positional arguments to be specified as well (usually constraints):: +The other, a tuple of the form +``(arg1, arg2, ..., {kwarg1:value, ...})``, which allows positional +arguments to be specified as well (usually constraints):: class MyClass(Base): __tablename__ = 'sometable' @@ -235,12 +258,14 @@ allows positional arguments to be specified as well (usually constraints):: {'autoload':True} ) -Note that the dictionary is required in the tuple form even if empty. +Note that the keyword parameters dictionary is required in the tuple +form even if empty. -As an alternative to ``__tablename__``, a direct :class:`~sqlalchemy.schema.Table` -construct may be used. The :class:`~sqlalchemy.schema.Column` objects, which -in this case require their names, will be -added to the mapping just like a regular mapping to a table:: +As an alternative to ``__tablename__``, a direct +:class:`~sqlalchemy.schema.Table` construct may be used. The +:class:`~sqlalchemy.schema.Column` objects, which in this case require +their names, will be added to the mapping just like a regular mapping +to a table:: class MyClass(Base): __table__ = Table('my_table', Base.metadata, @@ -251,9 +276,14 @@ added to the mapping just like a regular mapping to a table:: Mapper Configuration ==================== -Mapper arguments are specified using the ``__mapper_args__`` class variable, -which is a dictionary that accepts the same names as the :class:`~sqlalchemy.orm.mapper` -function accepts as keywords:: +Configuration of mappers is done with the +:func:`~sqlalchemy.orm.mapper` function and all the possible mapper +configuration parameters can be found in the documentation for that +function. + +:func:`~sqlalchemy.orm.mapper` is still used by declaratively mapped +classes and keyword parameters to the function can be passed by +placing them in the ``__mapper_args__`` class variable:: class Widget(Base): __tablename__ = 'widgets' @@ -265,7 +295,7 @@ Inheritance Configuration ========================= Declarative supports all three forms of inheritance as intuitively -as possible. The ``inherits`` mapper keyword argument is not needed, +as possible. The ``inherits`` mapper keyword argument is not needed as declarative will determine this from the class itself. The various "polymorphic" keyword arguments are specified using ``__mapper_args__``. @@ -287,11 +317,12 @@ table:: id = Column(Integer, ForeignKey('people.id'), primary_key=True) primary_language = Column(String(50)) -Note that above, the ``Engineer.id`` attribute, since it shares the same -attribute name as the ``Person.id`` attribute, will in fact represent the ``people.id`` -and ``engineers.id`` columns together, and will render inside a query as ``"people.id"``. -To provide the ``Engineer`` class with an attribute that represents only the -``engineers.id`` column, give it a different attribute name:: +Note that above, the ``Engineer.id`` attribute, since it shares the +same attribute name as the ``Person.id`` attribute, will in fact +represent the ``people.id`` and ``engineers.id`` columns together, and +will render inside a query as ``"people.id"``. +To provide the ``Engineer`` class with an attribute that represents +only the ``engineers.id`` column, give it a different attribute name:: class Engineer(Person): __tablename__ = 'engineers' @@ -302,8 +333,9 @@ To provide the ``Engineer`` class with an attribute that represents only the Single Table Inheritance ~~~~~~~~~~~~~~~~~~~~~~~~ -Single table inheritance is defined as a subclass that does not have its -own table; you just leave out the ``__table__`` and ``__tablename__`` attributes:: +Single table inheritance is defined as a subclass that does not have +its own table; you just leave out the ``__table__`` and ``__tablename__`` +attributes:: class Person(Base): __tablename__ = 'people' @@ -315,28 +347,31 @@ own table; you just leave out the ``__table__`` and ``__tablename__`` attributes __mapper_args__ = {'polymorphic_identity': 'engineer'} primary_language = Column(String(50)) -When the above mappers are configured, the ``Person`` class is mapped to the ``people`` -table *before* the ``primary_language`` column is defined, and this column will not be included -in its own mapping. When ``Engineer`` then defines the ``primary_language`` -column, the column is added to the ``people`` table so that it is included in the mapping -for ``Engineer`` and is also part of the table's full set of columns. -Columns which are not mapped to ``Person`` are also excluded from any other -single or joined inheriting classes using the ``exclude_properties`` mapper argument. -Below, ``Manager`` will have all the attributes of ``Person`` and ``Manager`` but *not* -the ``primary_language`` attribute of ``Engineer``:: +When the above mappers are configured, the ``Person`` class is mapped +to the ``people`` table *before* the ``primary_language`` column is +defined, and this column will not be included in its own mapping. +When ``Engineer`` then defines the ``primary_language`` column, the +column is added to the ``people`` table so that it is included in the +mapping for ``Engineer`` and is also part of the table's full set of +columns. Columns which are not mapped to ``Person`` are also excluded +from any other single or joined inheriting classes using the +``exclude_properties`` mapper argument. Below, ``Manager`` will have +all the attributes of ``Person`` and ``Manager`` but *not* the +``primary_language`` attribute of ``Engineer``:: class Manager(Person): __mapper_args__ = {'polymorphic_identity': 'manager'} golf_swing = Column(String(50)) -The attribute exclusion logic is provided by the ``exclude_properties`` mapper argument, -and declarative's default behavior can be disabled by passing an explicit -``exclude_properties`` collection (empty or otherwise) to the ``__mapper_args__``. +The attribute exclusion logic is provided by the +``exclude_properties`` mapper argument, and declarative's default +behavior can be disabled by passing an explicit ``exclude_properties`` +collection (empty or otherwise) to the ``__mapper_args__``. Concrete Table Inheritance ~~~~~~~~~~~~~~~~~~~~~~~~~~ -Concrete is defined as a subclass which has its own table and sets the +Concrete is defined as a subclass which has its own table and sets the ``concrete`` keyword argument to ``True``:: class Person(Base): @@ -351,8 +386,8 @@ Concrete is defined as a subclass which has its own table and sets the primary_language = Column(String(50)) name = Column(String(50)) -Usage of an abstract base class is a little less straightforward as it requires -usage of :func:`~sqlalchemy.orm.util.polymorphic_union`:: +Usage of an abstract base class is a little less straightforward as it +requires usage of :func:`~sqlalchemy.orm.util.polymorphic_union`:: engineers = Table('engineers', Base.metadata, Column('id', Integer, primary_key=True), @@ -382,19 +417,78 @@ usage of :func:`~sqlalchemy.orm.util.polymorphic_union`:: __table__ = managers __mapper_args__ = {'polymorphic_identity':'manager', 'concrete':True} -Class Usage -=========== + +Mix-in Classes +============== + +A common need when using :mod:`~sqlalchemy.ext.declarative` is to +share some functionality, often a set of columns, across many +classes. The normal python idiom would be to put this common code into +a base class and have all the other classes subclass this class. + +When using :mod:`~sqlalchemy.ext.declarative`, this need is met by +using a "mix-in class". A mix-in class is one that isn't mapped to a +table and doesn't subclass the declarative :class:`Base`. For example:: + + class MyMixin(object): + + __table_args__ = {'mysql_engine':'InnoDB'} + __mapper_args__=dict(always_refresh=True) + id = Column(Integer, primary_key=True) + + def foo(self): + return 'bar'+str(self.id) + + class MyModel(Base,MyMixin): + __tablename__='test' + name = Column(String(1000), nullable=False, index=True) + +As the above example shows, ``__table_args__`` and ``__mapper_args__`` +can both be abstracted out into a mix-in if you use common values for +these across many classes. + +However, particularly in the case of ``__table_args__``, you may want +to combine some parameters from several mix-ins with those you wish to +define on the class iteself. To help with this, a +:func:`~sqlalchemy.util.classproperty` decorator is provided that lets +you implement a class property with a function. For example:: + + from sqlalchemy.util import classproperty + + class MySQLSettings: + __table_args__ = {'mysql_engine':'InnoDB'} + + class MyOtherMixin: + __table_args__ = {'info':'foo'} + + class MyModel(Base,MySQLSettings,MyOtherMixin): + __tablename__='my_model' + + @classproperty + def __table_args__(self): + args = dict() + args.update(MySQLSettings.__table_args__) + args.update(MyOtherMixin.__table_args__) + return args + + id = Column(Integer, primary_key=True) + +Class Constructor +================= As a convenience feature, the :func:`declarative_base` sets a default -constructor on classes which takes keyword arguments, and assigns them to the -named attributes:: +constructor on classes which takes keyword arguments, and assigns them +to the named attributes:: e = Engineer(primary_language='python') -Note that ``declarative`` has no integration built in with sessions, and is -only intended as an optional syntax for the regular usage of mappers and Table -objects. A typical application setup using :func:`~sqlalchemy.orm.scoped_session` might look -like:: +Sessions +======== + +Note that ``declarative`` does nothing special with sessions, and is +only intended as an easier way to configure mappers and +:class:`~sqlalchemy.schema.Table` objects. A typical application +setup using :func:`~sqlalchemy.orm.scoped_session` might look like:: engine = create_engine('postgresql://scott:tiger@localhost/test') Session = scoped_session(sessionmaker(autocommit=False, @@ -402,7 +496,8 @@ like:: bind=engine)) Base = declarative_base() -Mapped instances then make usage of :class:`~sqlalchemy.orm.session.Session` in the usual way. +Mapped instances then make usage of +:class:`~sqlalchemy.orm.session.Session` in the usual way. """ @@ -419,8 +514,8 @@ __all__ = 'declarative_base', 'synonym_for', 'comparable_using', 'instrument_dec def instrument_declarative(cls, registry, metadata): """Given a class, configure the class declaratively, - using the given registry (any dictionary) and MetaData object. - This operation does not assume any kind of class hierarchy. + using the given registry, which can be any dictionary, and + MetaData object. """ if '_decl_class_registry' in cls.__dict__: @@ -432,6 +527,31 @@ def instrument_declarative(cls, registry, metadata): _as_declarative(cls, cls.__name__, cls.__dict__) def _as_declarative(cls, classname, dict_): + + # this spelling enables these attributes to be descriptors + mapper_args = '__mapper_args__' in dict_ and cls.__mapper_args__ or {} + table_args = '__table_args__' in dict_ and cls.__table_args__ or None + + # dict_ will be a dictproxy, which we can't write to, and we need to! + dict_ = dict(dict_) + + column_copies = dict() + + for base in cls.__bases__: + names = dir(base) + if not _is_mapped_class(base): + for name in names: + obj = getattr(base,name) + if isinstance(obj, Column): + dict_[name]=column_copies[obj]=obj.copy() + mapper_args = mapper_args or getattr(base,'__mapper_args__',mapper_args) + table_args = table_args or getattr(base,'__table_args__',None) + + # make sure that column copies are used rather than the original columns + # from any mixins + for k, v in mapper_args.iteritems(): + mapper_args[k] = column_copies.get(v,v) + cls._decl_class_registry[classname] = cls our_stuff = util.OrderedDict() for k in dict_: @@ -474,7 +594,6 @@ def _as_declarative(cls, classname, dict_): if '__tablename__' in dict_: tablename = cls.__tablename__ - table_args = dict_.get('__table_args__') if isinstance(table_args, dict): args, table_kw = (), table_args elif isinstance(table_args, tuple): @@ -500,12 +619,9 @@ def _as_declarative(cls, classname, dict_): for c in cols: if not table.c.contains_column(c): raise exceptions.ArgumentError( - "Can't add additional column %r when specifying __table__" % key) + "Can't add additional column %r when specifying __table__" % key + ) - if '__mapper_args__' in dict_: - mapper_args = dict(dict_['__mapper_args__']) - else: - mapper_args = {} if 'inherits' not in mapper_args: for c in cls.__bases__: if _is_mapped_class(c): @@ -518,8 +634,10 @@ def _as_declarative(cls, classname, dict_): mapper_cls = mapper if table is None and 'inherits' not in mapper_args: - raise exceptions.InvalidRequestError("Class %r does not have a __table__ or __tablename__ " - "specified and does not inherit from an existing table-mapped class." % cls) + raise exceptions.InvalidRequestError( + "Class %r does not have a __table__ or __tablename__ " + "specified and does not inherit from an existing table-mapped class." % cls + ) elif 'inherits' in mapper_args and not mapper_args.get('concrete', False): inherited_mapper = class_mapper(mapper_args['inherits'], compile=False) @@ -536,25 +654,30 @@ def _as_declarative(cls, classname, dict_): if table is None: # single table inheritance. # ensure no table args - table_args = cls.__dict__.get('__table_args__') if table_args is not None: - raise exceptions.ArgumentError("Can't place __table_args__ on an inherited class with no table.") + raise exceptions.ArgumentError( + "Can't place __table_args__ on an inherited class with no table." + ) # add any columns declared here to the inherited table. for c in cols: if c.primary_key: - raise exceptions.ArgumentError("Can't place primary key columns on an inherited class with no table.") + raise exceptions.ArgumentError( + "Can't place primary key columns on an inherited class with no table." + ) inherited_table.append_column(c) # single or joined inheritance - # exclude any cols on the inherited table which are not mapped on the parent class, to avoid + # exclude any cols on the inherited table which are not mapped on the + # parent class, to avoid # mapping columns specific to sibling/nephew classes inherited_mapper = class_mapper(mapper_args['inherits'], compile=False) inherited_table = inherited_mapper.local_table if 'exclude_properties' not in mapper_args: mapper_args['exclude_properties'] = exclude_properties = \ - set([c.key for c in inherited_table.c if c not in inherited_mapper._columntoproperty]) + set([c.key for c in inherited_table.c + if c not in inherited_mapper._columntoproperty]) exclude_properties.difference_update([c.key for c in cols]) cls.__mapper__ = mapper_cls(cls, table, properties=our_stuff, **mapper_args) @@ -633,14 +756,16 @@ def _deferred_relation(cls, prop): return return_cls if isinstance(prop, PropertyLoader): - for attr in ('argument', 'order_by', 'primaryjoin', 'secondaryjoin', 'secondary', '_foreign_keys', 'remote_side'): + for attr in ('argument', 'order_by', 'primaryjoin', 'secondaryjoin', + 'secondary', '_foreign_keys', 'remote_side'): v = getattr(prop, attr) if isinstance(v, basestring): setattr(prop, attr, resolve_arg(v)) if prop.backref and isinstance(prop.backref, tuple): key, kwargs = prop.backref - for attr in ('primaryjoin', 'secondaryjoin', 'secondary', 'foreign_keys', 'remote_side', 'order_by'): + for attr in ('primaryjoin', 'secondaryjoin', 'secondary', + 'foreign_keys', 'remote_side', 'order_by'): if attr in kwargs and isinstance(kwargs[attr], basestring): kwargs[attr] = resolve_arg(kwargs[attr]) @@ -672,7 +797,8 @@ def synonym_for(name, map_column=False): def comparable_using(comparator_factory): """Decorator, allow a Python @property to be used in query criteria. - A decorator front end to :func:`~sqlalchemy.orm.comparable_property`, passes + This is a decorator front end to + :func:`~sqlalchemy.orm.comparable_property` that passes through the comparator_factory and the function being decorated:: @comparable_using(MyComparatorType) @@ -693,10 +819,12 @@ def comparable_using(comparator_factory): def _declarative_constructor(self, **kwargs): """A simple constructor that allows initialization from kwargs. - Sets kwargs on the constructed instance. Only keys that are present as - attributes of type(self) are allowed (for example, any mapped column or - relation). - + Sets attributes on the constructed instance using the names and + values in ``kwargs``. + + Only keys that are present as + attributes of the instance's class are allowed. These could be, + for example, any mapped columns or relations. """ for k in kwargs: if not hasattr(type(self), k): @@ -711,22 +839,25 @@ def declarative_base(bind=None, metadata=None, mapper=None, cls=object, metaclass=DeclarativeMeta): """Construct a base class for declarative class definitions. - The new base class will be given a metaclass that invokes - :func:`instrument_declarative()` upon each subclass definition, and routes - later Column- and Mapper-related attribute assignments made on the class - into Table and Mapper assignments. + The new base class will be given a metaclass that produces + appropriate :class:`~sqlalchemy.schema.Table` objects and makes + the appropriate :func:`~sqlalchemy.orm.mapper` calls based on the + information provided declaratively in the class and any subclasses + of the class. - :param bind: An optional :class:`~sqlalchemy.engine.base.Connectable`, will be assigned - the ``bind`` attribute on the :class:`~sqlalchemy.MetaData` instance. + :param bind: An optional + :class:`~sqlalchemy.engine.base.Connectable`, will be assigned + the ``bind`` attribute on the :class:`~sqlalchemy.MetaData` + instance. :param metadata: - An optional :class:`~sqlalchemy.MetaData` instance. All :class:`~sqlalchemy.schema.Table` - objects implicitly declared by + An optional :class:`~sqlalchemy.MetaData` instance. All + :class:`~sqlalchemy.schema.Table` objects implicitly declared by subclasses of the base will share this MetaData. A MetaData instance - will be create if none is provided. The MetaData instance will be - available via the `metadata` attribute of the generated declarative - base class. + will be created if none is provided. The + :class:`~sqlalchemy.MetaData` instance will be available via the + `metadata` attribute of the generated declarative base class. :param mapper: An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`. Will be @@ -734,7 +865,7 @@ def declarative_base(bind=None, metadata=None, mapper=None, cls=object, :param cls: Defaults to :class:`object`. A type to use as the base for the generated - declarative base class. May be a type or tuple of types. + declarative base class. May be a class or tuple of classes. :param name: Defaults to ``Base``. The display name for the generated @@ -742,11 +873,12 @@ def declarative_base(bind=None, metadata=None, mapper=None, cls=object, tracebacks and debugging. :param constructor: - Defaults to declarative._declarative_constructor, an __init__ - implementation that assigns \**kwargs for declared fields and relations - to an instance. If ``None`` is supplied, no __init__ will be installed - and construction will fall back to cls.__init__ with normal Python - semantics. + Defaults to + :func:`~sqlalchemy.ext.declarative._declarative_constructor`, an + __init__ implementation that assigns \**kwargs for declared + fields and relations to an instance. If ``None`` is supplied, + no __init__ will be provided and construction will fall back to + cls.__init__ by way of the normal Python semantics. :param metaclass: Defaults to :class:`DeclarativeMeta`. A metaclass or __metaclass__ |