From 5d375cd730fa0a89cb6684df47b2ae10cc217f57 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 9 Jul 2008 20:38:35 +0000 Subject: [PATCH] - Declarative supports a __table_args__ class variable, which is either a dictionary, or tuple of the form (arg1, arg2, ..., {kwarg1:value, ...}) which contains positional + kw arguments to be passed to the Table constructor. [ticket:1096] --- CHANGES | 7 +++++++ doc/build/content/plugins.txt | 16 +++++++++++++--- lib/sqlalchemy/ext/declarative.py | 32 ++++++++++++++++++++++++------- test/ext/declarative.py | 18 ++++++++++++++++- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 8d83e79168..8142601203 100644 --- a/CHANGES +++ b/CHANGES @@ -83,6 +83,13 @@ CHANGES If you'd like them to return datetime objects anyway despite their accepting strings as input, make a TypeDecorator around String - SQLA doesn't encourage this pattern. + +- extensions + - Declarative supports a __table_args__ class variable, which + is either a dictionary, or tuple of the form + (arg1, arg2, ..., {kwarg1:value, ...}) which contains positional + + kw arguments to be passed to the Table constructor. + [ticket:1096] 0.5beta1 ======== diff --git a/doc/build/content/plugins.txt b/doc/build/content/plugins.txt index 6e941cb9d4..5f795d4974 100644 --- a/doc/build/content/plugins.txt +++ b/doc/build/content/plugins.txt @@ -171,11 +171,21 @@ As an alternative to `__tablename__`, a direct `Table` construct may be used. T Column('name', String(50)) ) -This is the preferred approach when using reflected tables, as below: +Other table-based attributes include `__table_args__`, which is +either a dictionary as in: {python} - class MyClass(Base): - __table__ = Table('my_table', Base.metadata, autoload=True) + class MyClass(Base) + __tablename__ = 'sometable' + __table_args__ = {'mysql_engine':'InnoDB'} + +or a dictionary-containing tuple in the form +`(arg1, arg2, ..., {kwarg1:value, ...})`, as in: + + {python} + class MyClass(Base) + __tablename__ = 'sometable' + __table_args__ = (ForeignKeyConstraint(['id'], ['remote_table.id']), {'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: diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index e69c5f2322..cd7efbc177 100644 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -146,10 +146,19 @@ added to the mapping just like a regular mapping to a table:: Column('name', String(50)) ) -This is the preferred approach when using reflected tables, as below:: +Other table-based attributes include ``__table_args__``, which is +either a dictionary as in:: - class MyClass(Base): - __table__ = Table('my_table', Base.metadata, autoload=True) + class MyClass(Base) + __tablename__ = 'sometable' + __table_args__ = {'mysql_engine':'InnoDB'} + +or a dictionary-containing tuple in the form +``(arg1, arg2, ..., {kwarg1:value, ...})``, as in:: + + class MyClass(Base) + __tablename__ = 'sometable' + __table_args__ = (ForeignKeyConstraint(['id'], ['remote_table.id']), {'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 @@ -237,11 +246,20 @@ def _as_declarative(cls, classname, dict_): if '__table__' not in cls.__dict__: if '__tablename__' in cls.__dict__: tablename = cls.__tablename__ + + table_args = cls.__dict__.get('__table_args__') + if isinstance(table_args, dict): + args, table_kw = (), table_args + elif isinstance(table_args, tuple): + args = table_args[0:-1] + table_kw = table_args[-1] + else: + args, table_kw = (), {} + autoload = cls.__dict__.get('__autoload__') if autoload: - table_kw = {'autoload': True} - else: - table_kw = {} + table_kw['autoload'] = True + cols = [] for key, c in our_stuff.iteritems(): if isinstance(c, ColumnProperty): @@ -253,7 +271,7 @@ def _as_declarative(cls, classname, dict_): _undefer_column_name(key, c) cols.append(c) cls.__table__ = table = Table(tablename, cls.metadata, - *cols, **table_kw) + *(tuple(cols) + tuple(args)), **table_kw) else: table = cls.__table__ diff --git a/test/ext/declarative.py b/test/ext/declarative.py index ebe608c321..21947b1e65 100644 --- a/test/ext/declarative.py +++ b/test/ext/declarative.py @@ -2,7 +2,7 @@ import testenv; testenv.configure_for_tests() from sqlalchemy.ext import declarative as decl from testlib import sa, testing -from testlib.sa import MetaData, Table, Column, Integer, String, ForeignKey +from testlib.sa import MetaData, Table, Column, Integer, String, ForeignKey, ForeignKeyConstraint from testlib.sa.orm import relation, create_session from testlib.testing import eq_ from testlib.compat import set @@ -255,7 +255,23 @@ class DeclarativeTest(testing.TestBase, testing.AssertsExecutionResults): sa.exc.ArgumentError, "Mapper Mapper|User|users could not assemble any primary key", define) + + def test_table_args(self): + class Foo(Base): + __tablename__ = 'foo' + __table_args__ = {'mysql_engine':'InnoDB'} + id = Column('id', Integer, primary_key=True) + + assert Foo.__table__.kwargs['mysql_engine'] == 'InnoDB' + class Bar(Base): + __tablename__ = 'bar' + __table_args__ = (ForeignKeyConstraint(['id'], ['foo.id']), {'mysql_engine':'InnoDB'}) + id = Column('id', Integer, primary_key=True) + + assert Bar.__table__.c.id.references(Foo.__table__.c.id) + assert Bar.__table__.kwargs['mysql_engine'] == 'InnoDB' + def test_expression(self): class User(Base, ComparableEntity): __tablename__ = 'users' -- 2.47.3