SQLAlchemy: избегать повторения в определении класса декларативного стиля
Я использую SQLAlchemy, и многие классы в моей объектной модели имеют одинаковые два атрибута: id и (целочисленный и первичный ключ) и имя (строка). Я пытаюсь избежать объявления их в каждом классе, например:
class C1(declarative_base()):
id = Column(Integer, primary_key = True)
name = Column(String)
#...
class C2(declarative_base()):
id = Column(Integer, primary_key = True)
name = Column(String)
#...
Какой хороший способ сделать это? Я попытался использовать метаклассы, но он пока не работает.
Ответы
Ответ 1
Вы можете отчислить свои общие атрибуты в класс mixin и умножить наследование вместе с declarative_base()
:
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
class IdNameMixin(object):
id = Column(Integer, primary_key=True)
name = Column(String)
class C1(declarative_base(), IdNameMixin):
__tablename__ = 'C1'
class C2(declarative_base(), IdNameMixin):
__tablename__ = 'C2'
print C1.__dict__['id'] is C2.__dict__['id']
print C1.__dict__['name'] is C2.__dict__['name']
EDIT. Возможно, вы думаете, что это приведет к тому, что C1
и C2
будут использовать одни и те же объекты Column
, но, как отмечено в SQLAlchemy docs, объекты столбца копируются при создании из класса mixin. Я обновил образец кода, чтобы продемонстрировать это поведение.
Ответ 2
Вы также можете использовать метод копирования столбцов? Таким образом, поля могут быть определены независимо от таблиц, а те поля, которые используются повторно, - это просто field.copy() - ed.
id = Column(Integer, primary_key = True)
name = Column(String)
class C1(declarative_base()):
id = id.copy()
name = name.copy()
#...
class C2(declarative_base()):
id = id.copy()
name = name.copy()
#...
Ответ 3
Я думаю, что у меня это получилось.
Я создал метакласс, который происходит от DeclarativeMeta, и сделал это метаклассом C1 и C2. В этом новом метаклассе я просто сказал
def __new__(mcs, name, base, attr):
attr['__tablename__'] = name.lower()
attr['id'] = Column(Integer, primary_key = True)
attr['name'] = Column(String)
return super().__new__(mcs, name, base, attr)
И это работает нормально.