Sqlalchemy Перемещение столбцов mixin до конца
У меня есть модель sqlalchemy, где все большинство таблиц/объектов имеют поле примечаний. Поэтому, чтобы попробовать следовать принципу DRY, я переместил поле в класс mixin.
class NotesMixin(object):
notes = sa.Column(sa.String(4000) , nullable=False, default='')
class Service(Base, NotesMixin):
__tablename__ = "service"
service_id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String(255), nullable=False, index=True, unique=True)
class Datacenter(Base, NotesMixin):
__tablename__ = "datacenter"
datacenter_id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String(255), nullable=False, index=True, unique=True)
class Network(Base, NotesMixin, StatusMixin):
__tablename__ = "network"
network_id = sa.Column(sa.Integer, primary_key=True)
etc...
Теперь столбец примечаний - это первый столбец в модели /db. Я знаю, что это не влияет на функциональность моего приложения, но это немного раздражает меня, чтобы видеть заметки перед id и т.д. Любой способ переместить его в конец?
Ответы
Ответ 1
Обнаружено более чистое решение:
Используйте декоратор sqlalchemy.ext.declarative.declared_attr
в sqlalchemy 0.6.5 (sqlalchemy.util.classproperty
в sqlalchemy <= 0.6.4)
class NotesMixin(object):
@declared_attr
def notes(cls):
return sa.Column(sa.String(4000) , nullable=False, default='')
В соответствии с документами это "для столбцов с внешними ключами, а также для множества конструкций уровня отображения, которые требуют контекста-явного контекста". Хотя это, строго говоря, здесь не так, он делает это, вызывая метод (и создавая столбец) при построении подкласса, тем самым избегая необходимости делать копию. Это означает, что столбец mixin появится в конце. Вероятно, лучшее решение, чем взлом _creation_order
...
Ответ 2
Легкий ответ: просто создайте таблицы базы данных самостоятельно, вместо того, чтобы sqlalchemy сделать это с помощью metadata.create_all()
.
Если вы не считаете это приемлемым, я боюсь, что для этого потребуется небольшое изменение в sqlalchemy.ext.declarative
, или вам придется создать свой собственный метакласс и передать его на declarative_base()
с помощью metaclass
аргумент ключевого слова. Затем этот класс будет использоваться вместо стандартного DeclarativeMeta
.
Объяснение: sqlalchemy использует порядок создания свойств столбца, который он хранит в атрибуте "private" ._creation_order
(сгенерируется при вызове Column()). Декларативное расширение смешивает столбцы, создавая копию объекта столбца из вашего класса mixin и добавляя его в класс. ._creation_order
этой копии устанавливается на то же значение, что и исходное свойство класса mixin. Поскольку класс mixin, конечно, сначала создан, свойства столбца будут иметь более низкий порядок создания, чем подкласс.
Итак, чтобы сделать ваш запрос возможным, при создании копии следует назначить новый заказ на создание, а не брать оригинал. Вы можете попробовать создать свой метакласс на основе этого объяснения и использовать его. Но вы также можете попробовать и спросить разработчиков sqlalchemy. Может быть, они готовы принять это как запрос об ошибке/функции? По крайней мере, это похоже на незначительную (одну строку) изменение, которая не имела бы никакого эффекта, кроме изменения, которое вы просите (что, возможно, тоже лучше).
Ответ 3
Можно также изменить порядок столбцов при компиляции CREATE TABLE (здесь показано для диалекта postgresql
):
from sqlalchemy.schema import CreateTable
from sqlalchemy.ext.compiler import compiles
@compiles(CreateTable, 'postgresql')
def _compile_create_table(element, compiler, **kwargs):
element.columns = element.columns[::-1] # reverse order of columns
return compiler.visit_create_table(element)
Затем это работает с metadata.create_all()
.