root/galaxy-central/eggs/SQLAlchemy-0.5.6_dev_r6498-py2.6.egg/sqlalchemy/ext/declarative.py @ 3

リビジョン 3, 30.3 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1"""A simple declarative layer for SQLAlchemy ORM.
2
3Synopsis
4========
5
6SQLAlchemy object-relational configuration involves the usage of :class:`~sqlalchemy.schema.Table`,
7:func:`~sqlalchemy.orm.mapper`, and class objects to define the three areas of configuration.
8``declarative`` moves these three types of configuration underneath the individual
9mapped class.  Regular SQLAlchemy schema and ORM constructs are used in most
10cases::
11
12    from sqlalchemy.ext.declarative import declarative_base
13
14    Base = declarative_base()
15
16    class SomeClass(Base):
17        __tablename__ = 'some_table'
18        id = Column(Integer, primary_key=True)
19        name =  Column(String(50))
20
21Above, the :func:`declarative_base` callable produces a new base class from which
22all mapped classes inherit from.  When the class definition is completed, a
23new :class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.orm.mapper` have been generated, accessible via the
24``__table__`` and ``__mapper__`` attributes on the ``SomeClass`` class.
25
26Defining Attributes
27===================
28
29:class:`~sqlalchemy.schema.Column` objects may be explicitly named,
30including using a different name than the attribute in which they are associated.
31The column will be assigned to the :class:`~sqlalchemy.schema.Table` using the
32given name, and mapped to the class using the attribute name::
33
34    class SomeClass(Base):
35        __tablename__ = 'some_table'
36        id = Column("some_table_id", Integer, primary_key=True)
37        name = Column("name", String(50))
38   
39Otherwise, you may omit the names from the Column definitions. 
40Declarative will set the ``name`` attribute on the column when the class
41is initialized::
42
43    class SomeClass(Base):
44        __tablename__ = 'some_table'
45        id = Column(Integer, primary_key=True)
46        name = Column(String(50))
47
48Attributes may be added to the class after its construction, and they will be
49added to the underlying :class:`~sqlalchemy.schema.Table` and :func:`~sqlalchemy.orm.mapper()` definitions as
50appropriate::
51
52    SomeClass.data = Column('data', Unicode)
53    SomeClass.related = relation(RelatedInfo)
54
55Classes which are mapped explicitly using :func:`~sqlalchemy.orm.mapper()` can interact freely
56with declarative classes.   It is recommended, though not required, that all tables
57share the same underlying :class:`~sqlalchemy.schema.MetaData` object, so that
58string-configured :class:`~sqlalchemy.schema.ForeignKey` references can be resolved without issue.
59
60Association of Metadata and Engine
61==================================
62
63The :func:`declarative_base` base class contains a :class:`~sqlalchemy.schema.MetaData` object where newly
64defined :class:`~sqlalchemy.schema.Table` objects are collected.  This is accessed via the
65:class:`~sqlalchemy.schema.MetaData` class level accessor, so to create tables we can say::
66
67    engine = create_engine('sqlite://')
68    Base.metadata.create_all(engine)
69
70The :class:`~sqlalchemy.engine.base.Engine` created above may also be directly associated with the
71declarative base class using the ``bind`` keyword argument, where it will be
72associated with the underlying :class:`~sqlalchemy.schema.MetaData` object and allow SQL operations
73involving that metadata and its tables to make use of that engine
74automatically::
75
76    Base = declarative_base(bind=create_engine('sqlite://'))
77
78Or, as :class:`~sqlalchemy.schema.MetaData` allows, at any time using the ``bind`` attribute::
79
80    Base.metadata.bind = create_engine('sqlite://')
81
82The :func:`declarative_base` can also receive a pre-created :class:`~sqlalchemy.schema.MetaData` object,
83which allows a declarative setup to be associated with an already existing
84traditional collection of :class:`~sqlalchemy.schema.Table` objects::
85
86    mymetadata = MetaData()
87    Base = declarative_base(metadata=mymetadata)
88
89Configuring Relations
90=====================
91
92Relations to other classes are done in the usual way, with the added feature
93that the class specified to :func:`~sqlalchemy.orm.relation()` may be a string name.  The "class
94registry" associated with ``Base`` is used at mapper compilation time to
95resolve the name into the actual class object, which is expected to have been
96defined once the mapper configuration is used::
97
98    class User(Base):
99        __tablename__ = 'users'
100
101        id = Column(Integer, primary_key=True)
102        name = Column(String(50))
103        addresses = relation("Address", backref="user")
104
105    class Address(Base):
106        __tablename__ = 'addresses'
107
108        id = Column(Integer, primary_key=True)
109        email = Column(String(50))
110        user_id = Column(Integer, ForeignKey('users.id'))
111
112Column constructs, since they are just that, are immediately usable, as below
113where we define a primary join condition on the ``Address`` class using them::
114
115    class Address(Base):
116        __tablename__ = 'addresses'
117
118        id = Column(Integer, primary_key=True)
119        email = Column(String(50))
120        user_id = Column(Integer, ForeignKey('users.id'))
121        user = relation(User, primaryjoin=user_id == User.id)
122
123In addition to the main argument for :func:`~sqlalchemy.orm.relation`, other arguments
124which depend upon the columns present on an as-yet undefined class
125may also be specified as strings.  These strings are evaluated as
126Python expressions.  The full namespace available within this
127evaluation includes all classes mapped for this declarative base,
128as well as the contents of the ``sqlalchemy`` package, including
129expression functions like :func:`~sqlalchemy.sql.expression.desc` and :attr:`~sqlalchemy.sql.expression.func`::
130
131    class User(Base):
132        # ....
133        addresses = relation("Address", order_by="desc(Address.email)",
134            primaryjoin="Address.user_id==User.id")
135
136As an alternative to string-based attributes, attributes may also be
137defined after all classes have been created.  Just add them to the target
138class after the fact::
139
140    User.addresses = relation(Address, primaryjoin=Address.user_id == User.id)
141
142Configuring Many-to-Many Relations
143==================================
144
145There's nothing special about many-to-many with declarative.  The ``secondary``
146argument to :func:`~sqlalchemy.orm.relation` still requires a :class:`~sqlalchemy.schema.Table` object, not a declarative class.
147The :class:`~sqlalchemy.schema.Table` should share the same :class:`~sqlalchemy.schema.MetaData` object used by the declarative base::
148
149    keywords = Table('keywords', Base.metadata,
150                        Column('author_id', Integer, ForeignKey('authors.id')),
151                        Column('keyword_id', Integer, ForeignKey('keywords.id'))
152                )
153               
154    class Author(Base):
155        __tablename__ = 'authors'
156        id = Column(Integer, primary_key=True)
157        keywords = relation("Keyword", secondary=keywords)
158
159You should generally **not** map a class and also specify its table in a many-to-many
160relation, since the ORM may issue duplicate INSERT and DELETE statements.
161
162
163Defining Synonyms
164=================
165
166Synonyms are introduced in :ref:`synonyms`. To define a getter/setter which
167proxies to an underlying attribute, use :func:`~sqlalchemy.orm.synonym` with the
168``descriptor`` argument::
169
170    class MyClass(Base):
171        __tablename__ = 'sometable'
172
173        _attr = Column('attr', String)
174
175        def _get_attr(self):
176            return self._some_attr
177        def _set_attr(self, attr):
178            self._some_attr = attr
179        attr = synonym('_attr', descriptor=property(_get_attr, _set_attr))
180
181The above synonym is then usable as an instance attribute as well as a
182class-level expression construct::
183
184    x = MyClass()
185    x.attr = "some value"
186    session.query(MyClass).filter(MyClass.attr == 'some other value').all()
187
188For simple getters, the :func:`synonym_for` decorator can be used in conjunction
189with ``@property``::
190
191    class MyClass(Base):
192        __tablename__ = 'sometable'
193       
194        _attr = Column('attr', String)
195
196        @synonym_for('_attr')
197        @property
198        def attr(self):
199            return self._some_attr
200
201Similarly, :func:`comparable_using` is a front end for the :func:`~sqlalchemy.orm.comparable_property`
202ORM function::
203
204    class MyClass(Base):
205        __tablename__ = 'sometable'
206
207        name = Column('name', String)
208
209        @comparable_using(MyUpperCaseComparator)
210        @property
211        def uc_name(self):
212            return self.name.upper()
213
214Table Configuration
215===================
216
217Table arguments other than the name, metadata, and mapped Column arguments
218are specified using the ``__table_args__`` class attribute.   This attribute
219accommodates both positional as well as keyword arguments that are normally
220sent to the :class:`~sqlalchemy.schema.Table` constructor.   The attribute can be specified
221in one of two forms.  One is as a dictionary::
222
223    class MyClass(Base):
224        __tablename__ = 'sometable'
225        __table_args__ = {'mysql_engine':'InnoDB'}
226
227The other, a tuple of the form ``(arg1, arg2, ..., {kwarg1:value, ...})``, which
228allows positional arguments to be specified as well (usually constraints)::
229
230    class MyClass(Base):
231        __tablename__ = 'sometable'
232        __table_args__ = (
233                ForeignKeyConstraint(['id'], ['remote_table.id']),
234                UniqueConstraint('foo'),
235                {'autoload':True}
236                )
237
238Note that the dictionary is required in the tuple form even if empty.
239
240As an alternative to ``__tablename__``, a direct :class:`~sqlalchemy.schema.Table`
241construct may be used.  The :class:`~sqlalchemy.schema.Column` objects, which
242in this case require their names, will be
243added to the mapping just like a regular mapping to a table::
244
245    class MyClass(Base):
246        __table__ = Table('my_table', Base.metadata,
247            Column('id', Integer, primary_key=True),
248            Column('name', String(50))
249        )
250
251Mapper Configuration
252====================
253
254Mapper arguments are specified using the ``__mapper_args__`` class variable,
255which is a dictionary that accepts the same names as the :class:`~sqlalchemy.orm.mapper`
256function accepts as keywords::
257
258    class Widget(Base):
259        __tablename__ = 'widgets'
260        id = Column(Integer, primary_key=True)
261       
262        __mapper_args__ = {'extension': MyWidgetExtension()}
263
264Inheritance Configuration
265=========================
266
267Declarative supports all three forms of inheritance as intuitively
268as possible.  The ``inherits`` mapper keyword argument is not needed,
269as declarative will determine this from the class itself.   The various
270"polymorphic" keyword arguments are specified using ``__mapper_args__``.
271
272Joined Table Inheritance
273~~~~~~~~~~~~~~~~~~~~~~~~
274
275Joined table inheritance is defined as a subclass that defines its own
276table::
277
278    class Person(Base):
279        __tablename__ = 'people'
280        id = Column(Integer, primary_key=True)
281        discriminator = Column('type', String(50))
282        __mapper_args__ = {'polymorphic_on': discriminator}
283
284    class Engineer(Person):
285        __tablename__ = 'engineers'
286        __mapper_args__ = {'polymorphic_identity': 'engineer'}
287        id = Column(Integer, ForeignKey('people.id'), primary_key=True)
288        primary_language = Column(String(50))
289
290Note that above, the ``Engineer.id`` attribute, since it shares the same
291attribute name as the ``Person.id`` attribute, will in fact represent the ``people.id``
292and ``engineers.id`` columns together, and will render inside a query as ``"people.id"``.
293To provide the ``Engineer`` class with an attribute that represents only the
294``engineers.id`` column, give it a different attribute name::
295
296    class Engineer(Person):
297        __tablename__ = 'engineers'
298        __mapper_args__ = {'polymorphic_identity': 'engineer'}
299        engineer_id = Column('id', Integer, ForeignKey('people.id'), primary_key=True)
300        primary_language = Column(String(50))
301
302Single Table Inheritance
303~~~~~~~~~~~~~~~~~~~~~~~~
304
305Single table inheritance is defined as a subclass that does not have its
306own table; you just leave out the ``__table__`` and ``__tablename__`` attributes::
307
308    class Person(Base):
309        __tablename__ = 'people'
310        id = Column(Integer, primary_key=True)
311        discriminator = Column('type', String(50))
312        __mapper_args__ = {'polymorphic_on': discriminator}
313
314    class Engineer(Person):
315        __mapper_args__ = {'polymorphic_identity': 'engineer'}
316        primary_language = Column(String(50))
317
318When the above mappers are configured, the ``Person`` class is mapped to the ``people``
319table *before* the ``primary_language`` column is defined, and this column will not be included
320in its own mapping.   When ``Engineer`` then defines the ``primary_language``
321column, the column is added to the ``people`` table so that it is included in the mapping
322for ``Engineer`` and is also part of the table's full set of columns.   
323Columns which are not mapped to ``Person`` are also excluded from any other
324single or joined inheriting classes using the ``exclude_properties`` mapper argument.
325Below, ``Manager`` will have all the attributes of ``Person`` and ``Manager`` but *not*
326the ``primary_language`` attribute of ``Engineer``::
327
328    class Manager(Person):
329        __mapper_args__ = {'polymorphic_identity': 'manager'}
330        golf_swing = Column(String(50))
331
332The attribute exclusion logic is provided by the ``exclude_properties`` mapper argument,
333and declarative's default behavior can be disabled by passing an explicit
334``exclude_properties`` collection (empty or otherwise) to the ``__mapper_args__``.
335
336Concrete Table Inheritance
337~~~~~~~~~~~~~~~~~~~~~~~~~~
338
339Concrete is defined as a subclass which has its own table and sets the
340``concrete`` keyword argument to ``True``::
341
342    class Person(Base):
343        __tablename__ = 'people'
344        id = Column(Integer, primary_key=True)
345        name = Column(String(50))
346       
347    class Engineer(Person):
348        __tablename__ = 'engineers'
349        __mapper_args__ = {'concrete':True}
350        id = Column(Integer, primary_key=True)
351        primary_language = Column(String(50))
352        name = Column(String(50))
353
354Usage of an abstract base class is a little less straightforward as it requires
355usage of :func:`~sqlalchemy.orm.util.polymorphic_union`::
356
357    engineers = Table('engineers', Base.metadata,
358                    Column('id', Integer, primary_key=True),
359                    Column('name', String(50)),
360                    Column('primary_language', String(50))
361                )
362    managers = Table('managers', Base.metadata,
363                    Column('id', Integer, primary_key=True),
364                    Column('name', String(50)),
365                    Column('golf_swing', String(50))
366                )
367   
368    punion = polymorphic_union({
369        'engineer':engineers,
370        'manager':managers
371    }, 'type', 'punion')
372   
373    class Person(Base):
374        __table__ = punion
375        __mapper_args__ = {'polymorphic_on':punion.c.type}
376       
377    class Engineer(Person):
378        __table__ = engineers
379        __mapper_args__ = {'polymorphic_identity':'engineer', 'concrete':True}
380
381    class Manager(Person):
382        __table__ = managers
383        __mapper_args__ = {'polymorphic_identity':'manager', 'concrete':True}
384   
385Class Usage
386===========
387
388As a convenience feature, the :func:`declarative_base` sets a default
389constructor on classes which takes keyword arguments, and assigns them to the
390named attributes::
391
392    e = Engineer(primary_language='python')
393
394Note that ``declarative`` has no integration built in with sessions, and is
395only intended as an optional syntax for the regular usage of mappers and Table
396objects.  A typical application setup using :func:`~sqlalchemy.orm.scoped_session` might look
397like::
398
399    engine = create_engine('postgres://scott:tiger@localhost/test')
400    Session = scoped_session(sessionmaker(autocommit=False,
401                                          autoflush=False,
402                                          bind=engine))
403    Base = declarative_base()
404
405Mapped instances then make usage of :class:`~sqlalchemy.orm.session.Session` in the usual way.
406
407"""
408
409from sqlalchemy.schema import Table, Column, MetaData
410from sqlalchemy.orm import synonym as _orm_synonym, mapper, comparable_property, class_mapper
411from sqlalchemy.orm.interfaces import MapperProperty
412from sqlalchemy.orm.properties import PropertyLoader, ColumnProperty
413from sqlalchemy.orm.util import _is_mapped_class
414from sqlalchemy import util, exceptions
415from sqlalchemy.sql import util as sql_util
416
417
418__all__ = 'declarative_base', 'synonym_for', 'comparable_using', 'instrument_declarative'
419
420def instrument_declarative(cls, registry, metadata):
421    """Given a class, configure the class declaratively,
422    using the given registry (any dictionary) and MetaData object.
423    This operation does not assume any kind of class hierarchy.
424   
425    """
426    if '_decl_class_registry' in cls.__dict__:
427        raise exceptions.InvalidRequestError("Class %r already has been instrumented declaratively" % cls)
428    cls._decl_class_registry = registry
429    cls.metadata = metadata
430    _as_declarative(cls, cls.__name__, cls.__dict__)
431   
432def _as_declarative(cls, classname, dict_):
433    cls._decl_class_registry[classname] = cls
434    our_stuff = util.OrderedDict()
435    for k in dict_:
436        value = dict_[k]
437        if (isinstance(value, tuple) and len(value) == 1 and
438            isinstance(value[0], (Column, MapperProperty))):
439            util.warn("Ignoring declarative-like tuple value of attribute "
440                      "%s: possibly a copy-and-paste error with a comma "
441                      "left at the end of the line?" % k)
442            continue
443        if not isinstance(value, (Column, MapperProperty)):
444            continue
445        prop = _deferred_relation(cls, value)
446        our_stuff[k] = prop
447
448    # set up attributes in the order they were created
449    our_stuff.sort(key=lambda key: our_stuff[key]._creation_order)
450
451    # extract columns from the class dict
452    cols = []
453    for key, c in our_stuff.iteritems():
454        if isinstance(c, ColumnProperty):
455            for col in c.columns:
456                if isinstance(col, Column) and col.table is None:
457                    _undefer_column_name(key, col)
458                    cols.append(col)
459        elif isinstance(c, Column):
460            _undefer_column_name(key, c)
461            cols.append(c)
462            # if the column is the same name as the key,
463            # remove it from the explicit properties dict.
464            # the normal rules for assigning column-based properties
465            # will take over, including precedence of columns
466            # in multi-column ColumnProperties.
467            if key == c.key:
468                del our_stuff[key]
469
470    table = None
471    if '__table__' not in cls.__dict__:
472        if '__tablename__' in cls.__dict__:
473            tablename = cls.__tablename__
474           
475            table_args = cls.__dict__.get('__table_args__')
476            if isinstance(table_args, dict):
477                args, table_kw = (), table_args
478            elif isinstance(table_args, tuple):
479                args = table_args[0:-1]
480                table_kw = table_args[-1]
481                if len(table_args) < 2 or not isinstance(table_kw, dict):
482                    raise exceptions.ArgumentError(
483                                "Tuple form of __table_args__ is "
484                                "(arg1, arg2, arg3, ..., {'kw1':val1, 'kw2':val2, ...})"
485                            )
486            else:
487                args, table_kw = (), {}
488
489            autoload = cls.__dict__.get('__autoload__')
490            if autoload:
491                table_kw['autoload'] = True
492
493            cls.__table__ = table = Table(tablename, cls.metadata,
494                                          *(tuple(cols) + tuple(args)), **table_kw)
495    else:
496        table = cls.__table__
497        if cols:
498            for c in cols:
499                if not table.c.contains_column(c):
500                    raise exceptions.ArgumentError("Can't add additional column %r when specifying __table__" % key)
501           
502    mapper_args = getattr(cls, '__mapper_args__', {})
503    if 'inherits' not in mapper_args:
504        for c in cls.__bases__:
505            if _is_mapped_class(c):
506                mapper_args['inherits'] = cls._decl_class_registry.get(c.__name__, None)
507                break
508
509    if hasattr(cls, '__mapper_cls__'):
510        mapper_cls = util.unbound_method_to_callable(cls.__mapper_cls__)
511    else:
512        mapper_cls = mapper
513
514    if not table and 'inherits' not in mapper_args:
515        raise exceptions.InvalidRequestError("Class %r does not have a __table__ or __tablename__ "
516                    "specified and does not inherit from an existing table-mapped class." % cls)
517
518    elif 'inherits' in mapper_args and not mapper_args.get('concrete', False):
519        inherited_mapper = class_mapper(mapper_args['inherits'], compile=False)
520        inherited_table = inherited_mapper.local_table
521        if 'inherit_condition' not in mapper_args and table:
522            # figure out the inherit condition with relaxed rules
523            # about nonexistent tables, to allow for ForeignKeys to
524            # not-yet-defined tables (since we know for sure that our
525            # parent table is defined within the same MetaData)
526            mapper_args['inherit_condition'] = sql_util.join_condition(
527                mapper_args['inherits'].__table__, table,
528                ignore_nonexistent_tables=True)
529
530        if not table:
531            # single table inheritance.
532            # ensure no table args
533            table_args = cls.__dict__.get('__table_args__')
534            if table_args is not None:
535                raise exceptions.ArgumentError("Can't place __table_args__ on an inherited class with no table.")
536       
537            # add any columns declared here to the inherited table.
538            for c in cols:
539                if c.primary_key:
540                    raise exceptions.ArgumentError("Can't place primary key columns on an inherited class with no table.")
541                inherited_table.append_column(c)
542   
543        # single or joined inheritance
544        # exclude any cols on the inherited table which are not mapped on the parent class, to avoid
545        # mapping columns specific to sibling/nephew classes
546        inherited_mapper = class_mapper(mapper_args['inherits'], compile=False)
547        inherited_table = inherited_mapper.local_table
548       
549        if 'exclude_properties' not in mapper_args:
550            mapper_args['exclude_properties'] = exclude_properties = \
551                set([c.key for c in inherited_table.c if c not in inherited_mapper._columntoproperty])
552            exclude_properties.difference_update([c.key for c in cols])
553   
554    cls.__mapper__ = mapper_cls(cls, table, properties=our_stuff, **mapper_args)
555
556class DeclarativeMeta(type):
557    def __init__(cls, classname, bases, dict_):
558        if '_decl_class_registry' in cls.__dict__:
559            return type.__init__(cls, classname, bases, dict_)
560       
561        _as_declarative(cls, classname, dict_)
562        return type.__init__(cls, classname, bases, dict_)
563
564    def __setattr__(cls, key, value):
565        if '__mapper__' in cls.__dict__:
566            if isinstance(value, Column):
567                _undefer_column_name(key, value)
568                cls.__table__.append_column(value)
569                cls.__mapper__.add_property(key, value)
570            elif isinstance(value, ColumnProperty):
571                for col in value.columns:
572                    if isinstance(col, Column) and col.table is None:
573                        _undefer_column_name(key, col)
574                        cls.__table__.append_column(col)
575                cls.__mapper__.add_property(key, value)
576            elif isinstance(value, MapperProperty):
577                cls.__mapper__.add_property(key, _deferred_relation(cls, value))
578            else:
579                type.__setattr__(cls, key, value)
580        else:
581            type.__setattr__(cls, key, value)
582
583
584class _GetColumns(object):
585    def __init__(self, cls):
586        self.cls = cls
587    def __getattr__(self, key):
588        mapper = class_mapper(self.cls, compile=False)
589        if not mapper:
590            return getattr(self.cls, key)
591        else:
592            prop = mapper.get_property(key)
593            if not isinstance(prop, ColumnProperty):
594                raise exceptions.InvalidRequestError("Property %r is not an instance of ColumnProperty (i.e. does not correspnd directly to a Column)." % key)
595            return prop.columns[0]
596
597
598def _deferred_relation(cls, prop):
599    def resolve_arg(arg):
600        import sqlalchemy
601       
602        def access_cls(key):
603            if key in cls._decl_class_registry:
604                return _GetColumns(cls._decl_class_registry[key])
605            elif key in cls.metadata.tables:
606                return cls.metadata.tables[key]
607            else:
608                return sqlalchemy.__dict__[key]
609
610        d = util.PopulateDict(access_cls)
611        def return_cls():
612            try:
613                x = eval(arg, globals(), d)
614               
615                if isinstance(x, _GetColumns):
616                    return x.cls
617                else:
618                    return x
619            except NameError, n:
620                raise exceptions.InvalidRequestError(
621                    "When compiling mapper %s, expression %r failed to locate a name (%r). "
622                    "If this is a class name, consider adding this relation() to the %r "
623                    "class after both dependent classes have been defined." % (
624                    prop.parent, arg, n.args[0], cls))
625        return return_cls
626
627    if isinstance(prop, PropertyLoader):
628        for attr in ('argument', 'order_by', 'primaryjoin', 'secondaryjoin', 'secondary', '_foreign_keys', 'remote_side'):
629            v = getattr(prop, attr)
630            if isinstance(v, basestring):
631                setattr(prop, attr, resolve_arg(v))
632
633        if prop.backref:
634            for attr in ('primaryjoin', 'secondaryjoin', 'secondary', 'foreign_keys', 'remote_side', 'order_by'):
635               if attr in prop.backref.kwargs and isinstance(prop.backref.kwargs[attr], basestring):
636                   prop.backref.kwargs[attr] = resolve_arg(prop.backref.kwargs[attr])
637
638
639    return prop
640
641def synonym_for(name, map_column=False):
642    """Decorator, make a Python @property a query synonym for a column.
643
644    A decorator version of :func:`~sqlalchemy.orm.synonym`.  The function being
645    decorated is the 'descriptor', otherwise passes its arguments through
646    to synonym()::
647
648      @synonym_for('col')
649      @property
650      def prop(self):
651          return 'special sauce'
652
653    The regular ``synonym()`` is also usable directly in a declarative setting
654    and may be convenient for read/write properties::
655
656      prop = synonym('col', descriptor=property(_read_prop, _write_prop))
657
658    """
659    def decorate(fn):
660        return _orm_synonym(name, map_column=map_column, descriptor=fn)
661    return decorate
662
663def comparable_using(comparator_factory):
664    """Decorator, allow a Python @property to be used in query criteria.
665
666    A decorator front end to :func:`~sqlalchemy.orm.comparable_property`, passes
667    through the comparator_factory and the function being decorated::
668
669      @comparable_using(MyComparatorType)
670      @property
671      def prop(self):
672          return 'special sauce'
673
674    The regular ``comparable_property()`` is also usable directly in a
675    declarative setting and may be convenient for read/write properties::
676
677      prop = comparable_property(MyComparatorType)
678
679    """
680    def decorate(fn):
681        return comparable_property(comparator_factory, fn)
682    return decorate
683
684def _declarative_constructor(self, **kwargs):
685    """A simple constructor that allows initialization from kwargs.
686
687    Sets kwargs on the constructed instance.  Only keys that are present as
688    attributes of type(self) are allowed (for example, any mapped column or
689    relation).
690   
691    """
692    for k in kwargs:
693        if not hasattr(type(self), k):
694            raise TypeError(
695                "%r is an invalid keyword argument for %s" %
696                (k, type(self).__name__))
697        setattr(self, k, kwargs[k])
698_declarative_constructor.__name__ = '__init__'
699
700def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
701                     name='Base', constructor=_declarative_constructor,
702                     metaclass=DeclarativeMeta, engine=None):
703    """Construct a base class for declarative class definitions.
704
705    The new base class will be given a metaclass that invokes
706    :func:`instrument_declarative()` upon each subclass definition, and routes
707    later Column- and Mapper-related attribute assignments made on the class
708    into Table and Mapper assignments. 
709
710    :param bind: An optional :class:`~sqlalchemy.engine.base.Connectable`, will be assigned
711      the ``bind`` attribute on the :class:`~sqlalchemy.MetaData` instance.
712      The `engine` keyword argument is a deprecated synonym for `bind`.
713
714    :param metadata:
715      An optional :class:`~sqlalchemy.MetaData` instance.  All :class:`~sqlalchemy.schema.Table`
716      objects implicitly declared by
717      subclasses of the base will share this MetaData.  A MetaData instance
718      will be create if none is provided.  The MetaData instance will be
719      available via the `metadata` attribute of the generated declarative
720      base class.
721
722    :param mapper:
723      An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`.  Will be
724      used to map subclasses to their Tables.
725
726    :param cls:
727      Defaults to :class:`object`.  A type to use as the base for the generated
728      declarative base class.  May be a type or tuple of types.
729
730    :param name:
731      Defaults to ``Base``.  The display name for the generated
732      class.  Customizing this is not required, but can improve clarity in
733      tracebacks and debugging.
734
735    :param constructor:
736      Defaults to declarative._declarative_constructor, an __init__
737      implementation that assigns \**kwargs for declared fields and relations
738      to an instance.  If ``None`` is supplied, no __init__ will be installed
739      and construction will fall back to cls.__init__ with normal Python
740      semantics.
741
742    :param metaclass:
743      Defaults to :class:`DeclarativeMeta`.  A metaclass or __metaclass__
744      compatible callable to use as the meta type of the generated
745      declarative base class.
746
747    """
748    lcl_metadata = metadata or MetaData()
749    if bind or engine:
750        lcl_metadata.bind = bind or engine
751
752    bases = not isinstance(cls, tuple) and (cls,) or cls
753    class_dict = dict(_decl_class_registry=dict(),
754                      metadata=lcl_metadata)
755
756    if constructor:
757        class_dict['__init__'] = constructor
758    if mapper:
759        class_dict['__mapper_cls__'] = mapper
760
761    return metaclass(name, bases, class_dict)
762
763def _undefer_column_name(key, column):
764    if column.key is None:
765        column.key = key
766    if column.name is None:
767        column.name = key
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。