~ubuntu-branches/debian/jessie/sqlalchemy/jessie

« back to all changes in this revision

Viewing changes to lib/sqlalchemy/ext/declarative.py

  • Committer: Bazaar Package Importer
  • Author(s): Piotr Ożarowski
  • Date: 2010-07-18 10:16:17 UTC
  • mfrom: (1.5.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20100718101617-63m6de864oav9fsw
Tags: 0.6.3-1
* New upstream release
* Add ${python:Breaks} in debian/control

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
        id = Column(Integer, primary_key=True)
22
22
        name =  Column(String(50))
23
23
 
24
 
Above, the :func:`declarative_base` callable returns a new base class from which
25
 
all mapped classes should inherit.  When the class definition is completed, a
26
 
new :class:`~sqlalchemy.schema.Table` and
27
 
:class:`~sqlalchemy.orm.mapper` will have been generated, accessible
28
 
via the ``__table__`` and ``__mapper__`` attributes on the ``SomeClass`` class.
 
24
Above, the :func:`declarative_base` callable returns a new base class from
 
25
which all mapped classes should inherit. When the class definition is
 
26
completed, a new :class:`~sqlalchemy.schema.Table` and
 
27
:class:`~sqlalchemy.orm.mapper` will have been generated, accessible via the
 
28
``__table__`` and ``__mapper__`` attributes on the ``SomeClass`` class.
29
29
 
30
30
Defining Attributes
31
31
===================
327
327
    class Engineer(Person):
328
328
        __tablename__ = 'engineers'
329
329
        __mapper_args__ = {'polymorphic_identity': 'engineer'}
330
 
        engineer_id = Column('id', Integer, ForeignKey('people.id'), primary_key=True)
 
330
        engineer_id = Column('id', Integer, ForeignKey('people.id'),
 
331
                                                    primary_key=True)
331
332
        primary_language = Column(String(50))
332
333
 
333
334
Single Table Inheritance
580
581
Mixing in deferred(), column_property(), etc.
581
582
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
582
583
 
583
 
Like :func:`~sqlalchemy.orm.relationship`, all :class:`~sqlalchemy.orm.interfaces.MapperProperty`
584
 
subclasses such as :func:`~sqlalchemy.orm.deferred`, 
585
 
:func:`~sqlalchemy.orm.column_property`, etc. ultimately involve references
586
 
to columns, and therefore have the :func:`~sqlalchemy.util.classproperty` requirement so that no reliance on copying is needed::
 
584
Like :func:`~sqlalchemy.orm.relationship`, all
 
585
:class:`~sqlalchemy.orm.interfaces.MapperProperty` subclasses such as
 
586
:func:`~sqlalchemy.orm.deferred`, :func:`~sqlalchemy.orm.column_property`,
 
587
etc. ultimately involve references to columns, and therefore have the
 
588
:func:`~sqlalchemy.util.classproperty` requirement so that no reliance on
 
589
copying is needed::
587
590
 
588
591
    class SomethingMixin(object):
589
592
 
657
660
        __mapper_args__ = {'polymorphic_on': discriminator}
658
661
 
659
662
    class Engineer(Person):
660
 
        __tablename__ = None
 
663
        primary_language = Column(String(50))
661
664
        __mapper_args__ = {'polymorphic_identity': 'engineer'}
662
 
        primary_language = Column(String(50))
663
665
 
664
666
If you want to use a similar pattern with a mix of single and joined
665
667
table inheritance, you would need a slightly different mixin and use
672
674
    class Tablename:
673
675
        @classproperty
674
676
        def __tablename__(cls):
675
 
            if (decl.has_inherited_table(cls) and
676
 
                TableNameMixin not in cls.__bases__):
 
677
            if (has_inherited_table(cls) and
 
678
                Tablename not in cls.__bases__):
677
679
                return None
678
680
            return cls.__name__.lower()
679
681
 
682
684
        discriminator = Column('type', String(50))
683
685
        __mapper_args__ = {'polymorphic_on': discriminator}
684
686
 
 
687
    # This is single table inheritance
685
688
    class Engineer(Person):
686
 
        # This is single table inheritance
687
 
        __tablename__ = None
 
689
        primary_language = Column(String(50))
688
690
        __mapper_args__ = {'polymorphic_identity': 'engineer'}
689
 
        primary_language = Column(String(50))
690
691
 
 
692
    # This is joined table inheritance
691
693
    class Manager(Person,Tablename):
692
 
        # This is joinded table inheritance
693
 
        __tablename__ = None
694
 
        __mapper_args__ = {'polymorphic_identity': 'engineer'}
 
694
        id = Column(Integer, ForeignKey('person.id'), primary_key=True)
695
695
        preferred_recreation = Column(String(50))
 
696
        __mapper_args__ = {'polymorphic_identity': 'engineer'}
696
697
 
697
698
Combining Table/Mapper Arguments from Multiple Mixins
698
699
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
753
754
"""
754
755
 
755
756
from sqlalchemy.schema import Table, Column, MetaData
756
 
from sqlalchemy.orm import synonym as _orm_synonym, mapper, comparable_property, class_mapper
 
757
from sqlalchemy.orm import synonym as _orm_synonym, mapper,\
 
758
                                comparable_property, class_mapper
757
759
from sqlalchemy.orm.interfaces import MapperProperty
758
760
from sqlalchemy.orm.properties import RelationshipProperty, ColumnProperty
759
761
from sqlalchemy.orm.util import _is_mapped_class
761
763
from sqlalchemy.sql import util as sql_util
762
764
 
763
765
 
764
 
__all__ = 'declarative_base', 'synonym_for', 'comparable_using', 'instrument_declarative'
 
766
__all__ = 'declarative_base', 'synonym_for', \
 
767
            'comparable_using', 'instrument_declarative'
765
768
 
766
769
def instrument_declarative(cls, registry, metadata):
767
770
    """Given a class, configure the class declaratively,
778
781
    _as_declarative(cls, cls.__name__, cls.__dict__)
779
782
 
780
783
def has_inherited_table(cls):
781
 
    """Given a class, return True if any of the classes it inherits from has a mapped
782
 
    table, otherwise return False.
 
784
    """Given a class, return True if any of the classes it inherits from has a
 
785
    mapped table, otherwise return False.
783
786
    """
784
787
    for class_ in cls.__mro__:
785
788
        if getattr(class_,'__table__',None) is not None:
907
910
                table_kw = table_args[-1]
908
911
                if len(table_args) < 2 or not isinstance(table_kw, dict):
909
912
                    raise exceptions.ArgumentError(
910
 
                                "Tuple form of __table_args__ is "
911
 
                                "(arg1, arg2, arg3, ..., {'kw1':val1, 'kw2':val2, ...})"
912
 
                            )
 
913
                        "Tuple form of __table_args__ is "
 
914
                        "(arg1, arg2, arg3, ..., {'kw1':val1, "
 
915
                        "'kw2':val2, ...})"
 
916
                    )
913
917
            else:
914
918
                args, table_kw = (), {}
915
919
 
918
922
                table_kw['autoload'] = True
919
923
 
920
924
            cls.__table__ = table = Table(tablename, cls.metadata,
921
 
                                          *(tuple(cols) + tuple(args)), **table_kw)
 
925
                                          *(tuple(cols) + tuple(args)),
 
926
                                           **table_kw)
922
927
    else:
923
928
        table = cls.__table__
924
929
        if cols:
925
930
            for c in cols:
926
931
                if not table.c.contains_column(c):
927
932
                    raise exceptions.ArgumentError(
928
 
                        "Can't add additional column %r when specifying __table__" % key
929
 
                        )
 
933
                        "Can't add additional column %r when "
 
934
                        "specifying __table__" % key
 
935
                    )
930
936
    
931
937
    if 'inherits' not in mapper_args:
932
938
        for c in cls.__bases__:
933
939
            if _is_mapped_class(c):
934
 
                mapper_args['inherits'] = cls._decl_class_registry.get(c.__name__, None)
 
940
                mapper_args['inherits'] = cls._decl_class_registry.get(
 
941
                                                            c.__name__, None)
935
942
                break
936
943
 
937
944
    if hasattr(cls, '__mapper_cls__'):
942
949
    if table is None and 'inherits' not in mapper_args:
943
950
        raise exceptions.InvalidRequestError(
944
951
            "Class %r does not have a __table__ or __tablename__ "
945
 
            "specified and does not inherit from an existing table-mapped class." % cls
 
952
            "specified and does not inherit from an existing "
 
953
            "table-mapped class." % cls
946
954
            )
947
955
 
948
956
    elif 'inherits' in mapper_args and not mapper_args.get('concrete', False):
949
 
        inherited_mapper = class_mapper(mapper_args['inherits'], compile=False)
 
957
        inherited_mapper = class_mapper(mapper_args['inherits'],
 
958
                                            compile=False)
950
959
        inherited_table = inherited_mapper.local_table
951
960
        if 'inherit_condition' not in mapper_args and table is not None:
952
961
            # figure out the inherit condition with relaxed rules
962
971
            # ensure no table args
963
972
            if table_args:
964
973
                raise exceptions.ArgumentError(
965
 
                    "Can't place __table_args__ on an inherited class with no table."
 
974
                    "Can't place __table_args__ on an inherited class "
 
975
                    "with no table."
966
976
                    )
967
977
        
968
978
            # add any columns declared here to the inherited table.
969
979
            for c in cols:
970
980
                if c.primary_key:
971
981
                    raise exceptions.ArgumentError(
972
 
                        "Can't place primary key columns on an inherited class with no table."
 
982
                        "Can't place primary key columns on an inherited "
 
983
                        "class with no table."
973
984
                        )
974
985
                if c.name in inherited_table.c:
975
986
                    raise exceptions.ArgumentError(
976
 
                                "Column '%s' on class %s conflicts with existing column '%s'" % 
977
 
                                (c, cls, inherited_table.c[c.name])
978
 
                            )
 
987
                        "Column '%s' on class %s conflicts with "
 
988
                        "existing column '%s'" % 
 
989
                        (c, cls, inherited_table.c[c.name])
 
990
                    )
979
991
                inherited_table.append_column(c)
980
992
    
981
993
        # single or joined inheritance
982
994
        # exclude any cols on the inherited table which are not mapped on the
983
995
        # parent class, to avoid
984
996
        # mapping columns specific to sibling/nephew classes
985
 
        inherited_mapper = class_mapper(mapper_args['inherits'], compile=False)
 
997
        inherited_mapper = class_mapper(mapper_args['inherits'],
 
998
                                            compile=False)
986
999
        inherited_table = inherited_mapper.local_table
987
1000
        
988
1001
        if 'exclude_properties' not in mapper_args:
991
1004
                     if c not in inherited_mapper._columntoproperty])
992
1005
            exclude_properties.difference_update([c.key for c in cols])
993
1006
    
994
 
    cls.__mapper__ = mapper_cls(cls, table, properties=our_stuff, **mapper_args)
 
1007
    cls.__mapper__ = mapper_cls(cls, 
 
1008
                                table, 
 
1009
                                properties=our_stuff, 
 
1010
                                **mapper_args)
995
1011
 
996
1012
class DeclarativeMeta(type):
997
1013
    def __init__(cls, classname, bases, dict_):
1014
1030
                        cls.__table__.append_column(col)
1015
1031
                cls.__mapper__.add_property(key, value)
1016
1032
            elif isinstance(value, MapperProperty):
1017
 
                cls.__mapper__.add_property(key, _deferred_relationship(cls, value))
 
1033
                cls.__mapper__.add_property(
 
1034
                                        key, 
 
1035
                                        _deferred_relationship(cls, value)
 
1036
                                )
1018
1037
            else:
1019
1038
                type.__setattr__(cls, key, value)
1020
1039
        else:
1031
1050
            prop = mapper.get_property(key, raiseerr=False)
1032
1051
            if prop is None:
1033
1052
                raise exceptions.InvalidRequestError(
1034
 
                                        "Class %r does not have a mapped column named %r"
1035
 
                                        % (self.cls, key))
 
1053
                            "Class %r does not have a mapped column named %r"
 
1054
                            % (self.cls, key))
1036
1055
            elif not isinstance(prop, ColumnProperty):
1037
1056
                raise exceptions.InvalidRequestError(
1038
 
                                        "Property %r is not an instance of"
1039
 
                                        " ColumnProperty (i.e. does not correspond"
1040
 
                                        " directly to a Column)." % key)
 
1057
                            "Property %r is not an instance of"
 
1058
                            " ColumnProperty (i.e. does not correspond"
 
1059
                            " directly to a Column)." % key)
1041
1060
        return getattr(self.cls, key)
1042
1061
 
1043
1062
 
1064
1083
                    return x
1065
1084
            except NameError, n:
1066
1085
                raise exceptions.InvalidRequestError(
1067
 
                    "When compiling mapper %s, expression %r failed to locate a name (%r). "
1068
 
                    "If this is a class name, consider adding this relationship() to the %r "
1069
 
                    "class after both dependent classes have been defined." % (
1070
 
                    prop.parent, arg, n.args[0], cls))
 
1086
                    "When compiling mapper %s, expression %r failed to "
 
1087
                    "locate a name (%r). If this is a class name, consider "
 
1088
                    "adding this relationship() to the %r class after "
 
1089
                    "both dependent classes have been defined." % 
 
1090
                    (prop.parent, arg, n.args[0], cls)
 
1091
                )
1071
1092
        return return_cls
1072
1093
 
1073
1094
    if isinstance(prop, RelationshipProperty):
1090
1111
def synonym_for(name, map_column=False):
1091
1112
    """Decorator, make a Python @property a query synonym for a column.
1092
1113
 
1093
 
    A decorator version of :func:`~sqlalchemy.orm.synonym`.  The function being
1094
 
    decorated is the 'descriptor', otherwise passes its arguments through
1095
 
    to synonym()::
 
1114
    A decorator version of :func:`~sqlalchemy.orm.synonym`. The function being
 
1115
    decorated is the 'descriptor', otherwise passes its arguments through to
 
1116
    synonym()::
1096
1117
 
1097
1118
      @synonym_for('col')
1098
1119
      @property
1141
1162
    attributes of the instance's class are allowed. These could be,
1142
1163
    for example, any mapped columns or relationships.
1143
1164
    """
 
1165
    cls_ = type(self)
1144
1166
    for k in kwargs:
1145
 
        if not hasattr(type(self), k):
 
1167
        if not hasattr(cls_, k):
1146
1168
            raise TypeError(
1147
1169
                "%r is an invalid keyword argument for %s" %
1148
 
                (k, type(self).__name__))
 
1170
                (k, cls_.__name__))
1149
1171
        setattr(self, k, kwargs[k])
1150
1172
_declarative_constructor.__name__ = '__init__'
1151
1173
 
1164
1186
      :class:`~sqlalchemy.engine.base.Connectable`, will be assigned
1165
1187
      the ``bind`` attribute on the :class:`~sqlalchemy.MetaData` 
1166
1188
      instance. 
1167
 
      
1168
1189
 
1169
1190
    :param metadata:
1170
1191
      An optional :class:`~sqlalchemy.MetaData` instance.  All
1175
1196
      `metadata` attribute of the generated declarative base class.
1176
1197
 
1177
1198
    :param mapper:
1178
 
      An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`.  Will be
1179
 
      used to map subclasses to their Tables.
 
1199
      An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`. Will
 
1200
      be used to map subclasses to their Tables.
1180
1201
 
1181
1202
    :param cls:
1182
 
      Defaults to :class:`object`.  A type to use as the base for the generated
1183
 
      declarative base class.  May be a class or tuple of classes.
 
1203
      Defaults to :class:`object`. A type to use as the base for the generated
 
1204
      declarative base class. May be a class or tuple of classes.
1184
1205
 
1185
1206
    :param name:
1186
1207
      Defaults to ``Base``.  The display name for the generated