summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext/declarative.py
diff options
context:
space:
mode:
authorJason Kirtland <jek@discorporate.us>2008-03-18 02:16:15 +0000
committerJason Kirtland <jek@discorporate.us>2008-03-18 02:16:15 +0000
commitc35a7171407568025c49257ea276b32eaf205dba (patch)
treea16208a817f5bf5653d26824d8930096357de978 /lib/sqlalchemy/ext/declarative.py
parentc462e42dfd705ed781c9ae0505c0c65a71c8936e (diff)
downloadsqlalchemy-c35a7171407568025c49257ea276b32eaf205dba.tar.gz
- Start coverage for Class.prop = Column(), promote nameless Columns
Diffstat (limited to 'lib/sqlalchemy/ext/declarative.py')
-rw-r--r--lib/sqlalchemy/ext/declarative.py166
1 files changed, 92 insertions, 74 deletions
diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py
index 333b1eaa4..7f30a7374 100644
--- a/lib/sqlalchemy/ext/declarative.py
+++ b/lib/sqlalchemy/ext/declarative.py
@@ -2,62 +2,73 @@
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
-
+
engine = create_engine('sqlite://')
Base = declarative_base(engine)
-
+
class SomeClass(Base):
__tablename__ = 'some_table'
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.
+
+ 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 ``Table`` and ``mapper()`` definitions as appropriate::
+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. The ``declarative_base`` base class contains a ``MetaData``
-object as well as a dictionary of all classes created against the base.
-So to access the above metadata and create tables we can say::
+Classes which are mapped explicitly using ``mapper()`` can interact freely
+with declarative classes. The ``declarative_base`` base class contains a
+``MetaData`` object as well as a dictionary of all classes created against
+the base. So to access the above metadata and create tables we can say::
Base.metadata.create_all()
-
-The ``declarative_base`` can also receive a pre-created ``MetaData`` object::
+
+The ``declarative_base`` can also receive a pre-created ``MetaData``
+object::
mymetadata = MetaData()
Base = declarative_base(metadata=mymetadata)
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::
+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::
class User(Base):
__tablename__ = 'users'
- id = Column('id', Integer, primary_key=True)
- name = Column('name', String(50))
+ id = Column(Integer, primary_key=True)
+ name = Column(String(50))
addresses = relation("Address", backref="user")
-
+
class Address(Base):
__tablename__ = 'addresses'
- id = Column('id', Integer, primary_key=True)
- email = Column('email', String(50))
- user_id = Column('user_id', Integer, ForeignKey('users.id'))
+ id = Column(Integer, primary_key=True)
+ 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
@@ -66,39 +77,41 @@ using them::
class Address(Base)
__tablename__ = 'addresses'
- id = Column('id', Integer, primary_key=True)
- email = Column('email', String(50))
- user_id = Column('user_id', Integer, ForeignKey('users.id'))
+ 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)
-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 ``instruments`` argument::
+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 ``instruments``
+argument::
class MyClass(Base):
__tablename__ = 'sometable'
-
+
_attr = Column('attr', String)
-
+
def _get_attr(self):
return self._some_attr
def _set_attr(self, attr)
self._some_attr = attr
attr = synonym('_attr', instruments=property(_get_attr, _set_attr))
-
-The above synonym is then usable as an instance attribute as well as a class-level
-expression construct::
+
+The above synonym is then usable as an instance attribute as well as a
+class-level expression construct::
x = MyClass()
x.attr = "some value"
session.query(MyClass).filter(MyClass.attr == 'some other value').all()
-
-As an alternative to ``__tablename__``, a direct ``Table`` construct may be used::
+
+As an alternative to ``__tablename__``, a direct ``Table`` construct may be
+used::
class MyClass(Base):
__table__ = Table('my_table', Base.metadata,
- Column('id', Integer, primary_key=True),
- Column('name', String(50))
+ Column(Integer, primary_key=True),
+ Column(String(50))
)
This is the preferred approach when using reflected tables, as below::
@@ -106,42 +119,44 @@ This is the preferred approach when using reflected tables, as below::
class MyClass(Base):
__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::
+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::
class Person(Base):
__tablename__ = 'people'
- id = Column('id', Integer, primary_key=True)
- discriminator = Column('type', String(50))
+ id = Column(Integer, primary_key=True)
+ discriminator = Column(String(50))
__mapper_args__ = {'polymorphic_on':discriminator}
-
+
class Engineer(Person):
__tablename__ = 'engineers'
__mapper_args__ = {'polymorphic_identity':'engineer'}
- id = Column('id', Integer, ForeignKey('people.id'), primary_key=True)
- primary_language = Column('primary_language', String(50))
-
+ 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::
+As a convenience feature, the ``declarative_base()`` sets a default
+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::
+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::
engine = create_engine('postgres://scott:tiger@localhost/test')
Session = scoped_session(sessionmaker(transactional=True, autoflush=False, bind=engine))
Base = declarative_base()
-
-Mapped instances then make usage of ``Session`` in the usual way.
+Mapped instances then make usage of ``Session`` in the usual way.
"""
+
from sqlalchemy.schema import Table, SchemaItem, Column, MetaData
from sqlalchemy.orm import synonym as _orm_synonym, mapper, comparable_property
from sqlalchemy.orm.interfaces import MapperProperty
@@ -151,11 +166,12 @@ from sqlalchemy import util
__all__ = ['declarative_base', 'synonym_for', 'comparable_using',
'declared_synonym']
+
class DeclarativeMeta(type):
def __init__(cls, classname, bases, dict_):
if '_decl_class_registry' in cls.__dict__:
return type.__init__(cls, classname, bases, dict_)
-
+
cls._decl_class_registry[classname] = cls
our_stuff = util.OrderedDict()
for k in dict_:
@@ -170,7 +186,7 @@ class DeclarativeMeta(type):
continue
prop = _deferred_relation(cls, value)
our_stuff[k] = prop
-
+
table = None
if '__table__' not in cls.__dict__:
if '__tablename__' in cls.__dict__:
@@ -182,28 +198,25 @@ class DeclarativeMeta(type):
table_kw = {}
cols = []
for key, c in our_stuff.iteritems():
- if not isinstance(c, Column):
- continue
- if c.key is None:
- c.key = key
- if c.name is None:
- c.name = key
- cols.append(c)
+ if isinstance(c, Column):
+ _undefer_column_name(key, c)
+ cols.append(c)
cls.__table__ = table = Table(tablename, cls.metadata,
*cols, **table_kw)
else:
table = cls.__table__
-
+
inherits = cls.__mro__[1]
inherits = cls._decl_class_registry.get(inherits.__name__, None)
mapper_args = getattr(cls, '__mapper_args__', {})
-
+
cls.__mapper__ = mapper(cls, table, inherits=inherits, properties=our_stuff, **mapper_args)
return type.__init__(cls, classname, bases, dict_)
-
+
def __setattr__(cls, key, value):
if '__mapper__' in cls.__dict__:
if isinstance(value, Column):
+ _undefer_column_name(key, value)
cls.__table__.append_column(value)
cls.__mapper__.add_property(key, value)
elif isinstance(value, MapperProperty):
@@ -224,13 +237,13 @@ def _deferred_relation(cls, prop):
def declared_synonym(prop, name):
"""deprecated. use synonym(name, instrument=prop)."""
-
+
return _orm_synonym(name, instrument=prop)
declared_synonym = util.deprecated(declared_synonym)
def synonym_for(name, map_column=False):
"""Decorator, make a Python @property a query synonym for a column.
-
+
A decorator version of [sqlalchemy.orm#synonym()]. The function being
decoratred is the 'instrument', otherwise passes its arguments through
to synonym().
@@ -242,7 +255,7 @@ def synonym_for(name, map_column=False):
The regular ``synonym()`` is also usable directly in a declarative
setting and may be convenient for read/write properties::
-
+
prop = synonym('col', instrument=property(_read_prop, _write_prop))
"""
@@ -287,3 +300,8 @@ def declarative_base(engine=None, metadata=None):
setattr(self, k, kwargs[k])
return Base
+def _undefer_column_name(key, column):
+ if column.key is None:
+ column.key = key
+ if column.name is None:
+ column.name = key