Посты с тэгом dirty hack


Грязные технологии: миксины моделей SQLAlchemy

SQLAlchemy позволяет выносить часть определения модели в отдельный базовый класс, который будет потом «подмешиваться» к другим. Очень удобно, когда есть какой-то повторяющийся кусок в большом количестве классов моделей. Но есть один неприятный момент: все поля должны «знать», к какой модели они принадлежат, а для этого нужно копировать объект поля. С первым уровнем SQLAlchemy хорошо справляется, так что с простым Column всё путём, но ведь поля могут содержать ссылки на другие объекты (например, ForeignKey), к которым предъявляются такие же требования. И тут авторы SQLAlchemy пошли самым простым путём: если нельзя сделать автоматически на все случаи жизни, то не пусть делает пользователь вручную. В результате код миксина должен выглядеть примерно так:
class WithParent(object):
    @declared_attr
    def parent_id(cls):
        return Column(ForeignKey(Parent.id))
    @declared_attr
    def parent(cls):
        return relationship(Parent)


Грязные технологии: фабрика классов на основе функции

Так уж получилось, что нам часто требуется определять одинаковые (или почти одинаковые) классы моделей SQLAlchemy для разных MetaData ну и, соответственно, с разными базовыми классами. Декларативно. Повсеместный copy-paste быстро надоел. Были мысли создавать второй класс путём копирования, но уж больно сложно получается: не так просто определить, где на какой глубине остановиться, а где заменить ссылки на что-то уже отзеркалированное. Гораздо проще сделать фабрику и создавать столько классов, сколько нужно. В питоне ж это просто:
def create_C(Base):
    class C(Base):
        id = Column(Integer, primary_key=True)
        # …
    return C

Всё хорошо, только вот отступ лишний появляется. Кому-о, может, и мелочь, а нам он сильно не понравился. Неужели нельзя без него? Ну типа класс задекорировать чем-о так, чтобы он в фабрику превратился. Проблема в том, что тело класса выполняется сразу и только один раз, и никакими декораторами это