summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2023-02-13 10:08:52 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2023-02-13 10:08:52 -0500
commit037884edaef8638c7128ad13cc51ec0cc7c52bbd (patch)
treeb7c02a4c46e64b1a2bb6c121b159032052dd5d99
parentb95b22d8c0f9f26e33e1912d8ee205319fc20362 (diff)
downloadsqlalchemy-037884edaef8638c7128ad13cc51ec0cc7c52bbd.tar.gz
note column ordering change, indicate recipe to control ordering
Change-Id: I520c18ac8c84923558e2042265943b6340700788 References: #9294
-rw-r--r--doc/build/changelog/whatsnew_20.rst132
-rw-r--r--doc/build/orm/declarative_config.rst2
2 files changed, 134 insertions, 0 deletions
diff --git a/doc/build/changelog/whatsnew_20.rst b/doc/build/changelog/whatsnew_20.rst
index 3378f963e..dc58a6710 100644
--- a/doc/build/changelog/whatsnew_20.rst
+++ b/doc/build/changelog/whatsnew_20.rst
@@ -1822,6 +1822,138 @@ operations.
:ticket:`8925`
+
+ORM Declarative Apples Column Orders Differently; Control behavior using ``__table_cls__``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Declarative has changed the system by which mapped columns that originate from
+mixin or abstract base classes are sorted along with the columns that are on the
+declared class itself to place columns from the declared class first, followed
+by mixin columns. The following mapping::
+
+ class Foo:
+
+ col1 = Column(Integer)
+ col3 = Column(Integer)
+
+
+ class Bar:
+
+ col2 = Column(Integer)
+ col4 = Column(Integer)
+
+
+ class Model(Base, Foo, Bar):
+
+ id = Column(Integer, primary_key=True)
+ __tablename__ = "model"
+
+Produces a CREATE TABLE as follows on 1.4:
+
+.. sourcecode:: sql
+
+ CREATE TABLE model (
+ col1 INTEGER,
+ col3 INTEGER,
+ col2 INTEGER,
+ col4 INTEGER,
+ id INTEGER NOT NULL,
+ PRIMARY KEY (id)
+ )
+
+Whereas on 2.0 it produces:
+
+.. sourcecode:: sql
+
+ CREATE TABLE model (
+ id INTEGER NOT NULL,
+ col1 INTEGER,
+ col3 INTEGER,
+ col2 INTEGER,
+ col4 INTEGER,
+ PRIMARY KEY (id)
+ )
+
+For the specific case above, this can be seen as an improvement, as the primary
+key columns on the ``Model`` are now where one would typically prefer. However,
+this is no comfort for the application that defined models the other way
+around, as::
+
+ class Foo:
+
+ id = Column(Integer, primary_key=True)
+ col1 = Column(Integer)
+ col3 = Column(Integer)
+
+
+ class Model(Foo, Base):
+
+ col2 = Column(Integer)
+ col4 = Column(Integer)
+ __tablename__ = "model"
+
+This now produces CREATE TABLE output as:
+
+.. sourcecode:: sql
+
+ CREATE TABLE model (
+ col2 INTEGER,
+ col4 INTEGER,
+ id INTEGER NOT NULL,
+ col1 INTEGER,
+ col3 INTEGER,
+ PRIMARY KEY (id)
+ )
+
+It seems clear that Declarative may benefit from a simple rule such as
+"place primary key columns first, no matter what", or the availability of a
+public "sort order" attribute on columns. Many users have become familiar with
+a private attribute known as ``_creation_order``, however this attribute was
+never sufficient at controlling ordering in mixin inheritance scenarios, and
+is **no longer used** for column ordering in Declarative.
+
+In the interim, both SQLAlchemy 1.4 and 2.0 have a hook which can be used
+to apply such "sort ordering" right now, which is the
+:ref:`declarative_table_cls` hook. The above model can be given a deterministic
+"primary key first" scheme that is cross-compatible with 1.4 / 2.0 right now,
+using this hook in conjunction with the :paramref:`_schema.Column.info`
+dictionary to apply custom parameters, as in the example below::
+
+ from sqlalchemy import Table
+
+
+ class Foo:
+ @classmethod
+ def __table_cls__(cls, name, metadata_obj, *arg, **kw):
+ arg = sorted(arg, key=lambda obj: obj.info.get("column_order", 0))
+
+ return Table(name, metadata_obj, *arg, **kw)
+
+ id = Column(Integer, primary_key=True, info={"column_order": -10})
+ col1 = Column(Integer, info={"column_order": -1})
+ col3 = Column(Integer)
+
+
+ class Model(Foo, Base):
+
+ col2 = Column(Integer)
+ col4 = Column(Integer)
+ __tablename__ = "model"
+
+The above model places "id" before all others and "col1" after "id"::
+
+ CREATE TABLE model (
+ id INTEGER NOT NULL,
+ col1 INTEGER,
+ col2 INTEGER,
+ col4 INTEGER,
+ col3 INTEGER,
+ PRIMARY KEY (id)
+ )
+
+Future SQLAlchemy releases may opt to provide an explicit ordering hint for the
+:class:`_orm.mapped_column` construct, as this ordering is ORM specific.
+
.. _change_7211:
The ``Sequence`` construct reverts to not having any explicit default "start" value; impacts MS SQL Server
diff --git a/doc/build/orm/declarative_config.rst b/doc/build/orm/declarative_config.rst
index c7464580b..71acefe52 100644
--- a/doc/build/orm/declarative_config.rst
+++ b/doc/build/orm/declarative_config.rst
@@ -455,6 +455,8 @@ created perhaps within distinct databases::
:ref:`orm_inheritance_abstract_poly` - an alternative form of "abstract"
mapped class that is appropriate for inheritance hierarchies.
+.. _declarative_table_cls:
+
``__table_cls__``
~~~~~~~~~~~~~~~~~