diff options
Diffstat (limited to 'lib/sqlalchemy/ext/declarative.py')
| -rw-r--r-- | lib/sqlalchemy/ext/declarative.py | 160 |
1 files changed, 82 insertions, 78 deletions
diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index f06f16059..4778b9eba 100644 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -2,9 +2,9 @@ SQLAlchemy object-relational configuration involves the usage of Table, 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:: +declarative moves these three types of configuration underneath the individual +mapped class. Regular SQLAlchemy schema and ORM constructs are used in most +cases:: from sqlalchemy.ext.declarative import declarative_base @@ -15,11 +15,10 @@ used in most cases:: id = Column('id', Integer, primary_key=True) name = Column('name', String(50)) -Above, the ``declarative_base`` callable produces a new base class from -which all mapped classes inherit from. When the class definition is -completed, a new ``Table`` and ``mapper()`` have been generated, accessible -via the ``__table__`` and ``__mapper__`` attributes on the ``SomeClass`` -class. +Above, the ``declarative_base`` callable produces a new base class from which +all mapped classes inherit from. When the class definition is completed, a +new ``Table`` and ``mapper()`` have been generated, accessible via the +``__table__`` and ``__mapper__`` attributes on the ``SomeClass`` class. You may omit the names from the Column definitions. Declarative will fill them in for you:: @@ -29,29 +28,28 @@ them in for you:: 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 ``Table`` and ``mapper()`` definitions as +Attributes may be added to the class after its construction, and they will be +added to the underlying ``Table`` and ``mapper()`` definitions as appropriate:: SomeClass.data = Column('data', Unicode) SomeClass.related = relation(RelatedInfo) Classes which are mapped explicitly using ``mapper()`` can interact freely -with declarative classes. +with declarative classes. -The ``declarative_base`` base class contains a -``MetaData`` object where newly defined ``Table`` objects are collected. -This is accessed via the ``metadata`` class level accessor, so to -create tables we can say:: +The ``declarative_base`` base class contains a ``MetaData`` object where newly +defined ``Table`` objects are collected. This is accessed via the +``metadata`` class level accessor, so to create tables we can say:: engine = create_engine('sqlite://') Base.metadata.create_all(engine) -The ``Engine`` created above may also be directly associated with the -declarative base class using the ``engine`` keyword argument, where it will -be associated with the underlying ``MetaData`` object and allow SQL -operations involving that metadata and its tables to make use of that -engine automatically:: +The ``Engine`` created above may also be directly associated with the +declarative base class using the ``engine`` keyword argument, where it will be +associated with the underlying ``MetaData`` object and allow SQL operations +involving that metadata and its tables to make use of that engine +automatically:: Base = declarative_base(engine=create_engine('sqlite://')) @@ -59,8 +57,9 @@ Or, as ``MetaData`` allows, at any time using the ``bind`` attribute:: Base.metadata.bind = create_engine('sqlite://') -The ``declarative_base`` can also receive a pre-created ``MetaData`` -object, which allows a declarative setup to be associated with an already existing traditional collection of ``Table`` objects:: +The ``declarative_base`` can also receive a pre-created ``MetaData`` object, +which allows a declarative setup to be associated with an already existing +traditional collection of ``Table`` objects:: mymetadata = MetaData() Base = declarative_base(metadata=mymetadata) @@ -68,8 +67,8 @@ object, which allows a declarative setup to be associated with an already existi Relations to other classes are done in the usual way, with the added feature that the class specified to ``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:: +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' @@ -85,9 +84,8 @@ been 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' @@ -95,21 +93,19 @@ using them:: id = Column(Integer, primary_key=True) email = Column(String(50)) user_id = Column(Integer, ForeignKey('users.id')) - user = relation(User, primaryjoin=user_id==User.id) - -When an explicit join condition or other configuration which depends -on multiple classes cannot be defined immediately due to some classes -not yet being available, these can be defined after all classes have -been created. Attributes which are added to the class after -its creation are associated with the Table/mapping in the same -way as if they had been defined inline:: - - User.addresses = relation(Address, primaryjoin=Address.user_id==User.id) - -Synonyms are one area where ``declarative`` needs to slightly change the -usual SQLAlchemy configurational syntax. To define a getter/setter which -proxies to an underlying attribute, use ``synonym`` with the ``descriptor`` -argument:: + user = relation(User, primaryjoin=user_id == User.id) + +When an explicit join condition or other configuration which depends on +multiple classes cannot be defined immediately due to some classes not yet +being available, these can be defined after all classes have been created. +Attributes which are added to the class after its creation are associated with +the Table/mapping in the same way as if they had been defined inline:: + + User.addresses = relation(Address, primaryjoin=Address.user_id == User.id) + +Synonyms are one area where ``declarative`` needs to slightly change the usual +SQLAlchemy configurational syntax. To define a getter/setter which proxies to +an underlying attribute, use ``synonym`` with the ``descriptor`` argument:: class MyClass(Base): __tablename__ = 'sometable' @@ -130,8 +126,8 @@ class-level expression construct:: session.query(MyClass).filter(MyClass.attr == 'some other value').all() As an alternative to ``__tablename__``, a direct ``Table`` construct may be -used. The ``Column`` objects, which in this case require their names, -will be added to the mapping just like a regular mapping to a table:: +used. The ``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, @@ -145,43 +141,45 @@ This is the preferred approach when using reflected tables, as below:: __table__ = Table('my_table', Base.metadata, autoload=True) Mapper arguments are specified using the ``__mapper_args__`` class variable. -Note that the column objects declared on the class are immediately usable, -as in this joined-table inheritance example:: +Note that the column objects declared on the class are immediately usable, as +in this joined-table inheritance example:: class Person(Base): __tablename__ = 'people' id = Column(Integer, primary_key=True) discriminator = Column(String(50)) - __mapper_args__ = {'polymorphic_on':discriminator} + __mapper_args__ = {'polymorphic_on': discriminator} class Engineer(Person): __tablename__ = 'engineers' - __mapper_args__ = {'polymorphic_identity':'engineer'} + __mapper_args__ = {'polymorphic_identity': 'engineer'} id = Column(Integer, ForeignKey('people.id'), primary_key=True) primary_language = Column(String(50)) For single-table inheritance, the ``__tablename__`` and ``__table__`` class -variables are optional on a class when the class inherits from another -mapped class. +variables are optional on a class when the class inherits from another mapped +class. As a convenience feature, the ``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 ``scoped_session`` might -look like:: +only intended as an optional syntax for the regular usage of mappers and Table +objects. A typical application setup using ``scoped_session`` might look +like:: engine = create_engine('postgres://scott:tiger@localhost/test') - Session = scoped_session(sessionmaker(transactional=True, autoflush=False, bind=engine)) + Session = scoped_session(sessionmaker(transactional=True, + autoflush=False, + bind=engine)) Base = declarative_base() Mapped instances then make usage of ``Session`` in the usual way. -""" +""" from sqlalchemy.schema import Table, Column, MetaData from sqlalchemy.orm import synonym as _orm_synonym, mapper, comparable_property from sqlalchemy.orm.interfaces import MapperProperty @@ -190,8 +188,7 @@ from sqlalchemy import util, exceptions from sqlalchemy.sql import util as sql_util -__all__ = ['declarative_base', 'synonym_for', 'comparable_using', - 'declared_synonym'] +__all__ = 'declarative_base', 'synonym_for', 'comparable_using' class DeclarativeMeta(type): @@ -213,9 +210,10 @@ class DeclarativeMeta(type): continue prop = _deferred_relation(cls, value) our_stuff[k] = prop - + # set up attributes in the order they were created - our_stuff.sort(lambda x, y: cmp(our_stuff[x]._creation_order, our_stuff[y]._creation_order)) + our_stuff.sort(lambda x, y: cmp(our_stuff[x]._creation_order, + our_stuff[y]._creation_order)) table = None if '__table__' not in cls.__dict__: @@ -248,17 +246,21 @@ class DeclarativeMeta(type): if inherits: mapper_args['inherits'] = inherits if not mapper_args.get('concrete', False) and table: - # figure out the inherit condition with relaxed rules about nonexistent tables, - # to allow for ForeignKeys to not-yet-defined tables (since we know for sure that our parent - # table is defined within the same MetaData) - mapper_args['inherit_condition'] = sql_util.join_condition(inherits.__table__, table, ignore_nonexistent_tables=True) - + # figure out the inherit condition with relaxed rules + # about nonexistent tables, to allow for ForeignKeys to + # not-yet-defined tables (since we know for sure that our + # parent table is defined within the same MetaData) + mapper_args['inherit_condition'] = sql_util.join_condition( + inherits.__table__, table, + ignore_nonexistent_tables=True) + if hasattr(cls, '__mapper_cls__'): mapper_cls = util.unbound_method_to_callable(cls.__mapper_cls__) else: mapper_cls = mapper - cls.__mapper__ = mapper_cls(cls, table, properties=our_stuff, **mapper_args) + cls.__mapper__ = mapper_cls(cls, table, properties=our_stuff, + **mapper_args) return type.__init__(cls, classname, bases, dict_) def __setattr__(cls, key, value): @@ -275,22 +277,22 @@ class DeclarativeMeta(type): type.__setattr__(cls, key, value) def _deferred_relation(cls, prop): - if isinstance(prop, PropertyLoader) and isinstance(prop.argument, basestring): + if (isinstance(prop, PropertyLoader) and + isinstance(prop.argument, basestring)): arg = prop.argument def return_cls(): try: return cls._decl_class_registry[arg] except KeyError: - raise exceptions.InvalidRequestError("When compiling mapper %s, could not locate a declarative class named %r. Consider adding this property to the %r class after both dependent classes have been defined." % (prop.parent, arg, prop.parent.class_)) + raise exceptions.InvalidRequestError( + "When compiling mapper %s, could not locate a declarative " + "class named %r. Consider adding this property to the %r " + "class after both dependent classes have been defined." % ( + prop.parent, arg, prop.parent.class_)) prop.argument = return_cls return prop -def declared_synonym(prop, name): - """Deprecated. Use synonym(name, descriptor=prop).""" - return _orm_synonym(name, descriptor=prop) -declared_synonym = util.deprecated(None, False)(declared_synonym) - def synonym_for(name, map_column=False): """Decorator, make a Python @property a query synonym for a column. @@ -303,8 +305,8 @@ def synonym_for(name, map_column=False): def prop(self): return 'special sauce' - The regular ``synonym()`` is also usable directly in a declarative - setting and may be convenient for read/write properties:: + The regular ``synonym()`` is also usable directly in a declarative setting + and may be convenient for read/write properties:: prop = synonym('col', descriptor=property(_read_prop, _write_prop)) @@ -329,6 +331,7 @@ def comparable_using(comparator_factory): declarative setting and may be convenient for read/write properties:: prop = comparable_property(MyComparatorType) + """ def decorate(fn): return comparable_property(comparator_factory, fn) @@ -347,8 +350,9 @@ def declarative_base(engine=None, metadata=None, mapper=None): def __init__(self, **kwargs): for k in kwargs: if not hasattr(type(self), k): - raise TypeError('%r is an invalid keyword argument for %s' % - (k, type(self).__name__)) + raise TypeError( + "%r is an invalid keyword argument for %s" % + (k, type(self).__name__)) setattr(self, k, kwargs[k]) return Base |
