Как использовать свой собственный Meta-класс вместе с SQLAlchemy-Model в качестве родительского класса - PullRequest
2 голосов
/ 30 апреля 2019

Я недавно начал использовать Flask в качестве своего внутреннего фреймворка.Однако недавно я столкнулся с проблемой и не мог понять, как ее решить.В крайнем случае я хотел попробовать свои изменения здесь.Если бы вы могли помочь мне с этим, я был бы признателен.

Итак, у меня есть класс, который наследует от db.Model SQLAlchemy:

from flask_sqlalchemy import SQLAlchemy


db = SQLAlchemy()


class User(db.Model):
  ...

И я хотел бы создать свой собственныйМета-класс для автоматической вставки некоторых миксов в этот класс:

class MyMetaClass(type):
  def __new__(meta, name, bases, class_dict):
    bases += (Mixin1, Mixin2, Mixin3)
    return type.__new__(meta, name, bases, class_dict)

Однако, когда я пытаюсь использовать этот Мета-класс с Пользователь класс:

class User(db.Model, metaclass=MyMetaClass):
   ...

У меня следующая ошибка:

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Кто-нибудь знает, как решить эту проблему?Буду признателен за любую помощь.

(Примечание: я не учитель ни классов Flask, ни Meta. Поэтому, если я что-то понял неправильно, прошу прощения за отсутствие понимания)

1 Ответ

1 голос
/ 01 мая 2019

Наконец мне удалось решить мою проблему.Если кто-то еще сталкивается с той же проблемой, я выкладываю решение здесь.

Это отрывок, взятый с сайта SQLAlchemy :


Модельmetaclass отвечает за настройку внутренних компонентов SQLAlchemy при определении подклассов модели.Flask-SQLAlchemy добавляет некоторые дополнительные поведения через миксины;его метакласс по умолчанию DefaultMeta наследует их все.

  • BindMetaMixin: bind_key извлекается из класса и применяется к таблице.См. Несколько баз данных с привязками.
  • NameMetaMixin: если модель не задает имя таблицы , но указывает первичный ключ, автоматически генерируется имя.

Вы можете добавить свое собственное поведение, определив свой собственный метакласс и создав декларативную базу самостоятельно.Не забудьте по-прежнему наследовать нужные миксины (или просто наследовать от метакласса по умолчанию).

Передача декларативного базового класса вместо простого базового класса модели, как показано выше, в base_class вызовет Flask-SQLAlchemyиспользовать эту базу вместо создания базы с метаклассом по умолчанию.

from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import DefaultMeta, Model

class CustomMeta(DefaultMeta):
    def __init__(cls, name, bases, d):
        # custom class setup could go here

        # be sure to call super
        super(CustomMeta, cls).__init__(name, bases, d)

    # custom class-only methods could go here

db = SQLAlchemy(model_class=declarative_base(
    cls=Model, metaclass=CustomMeta, name='Model'))

Вы также можете передать любые другие аргументы, которые вы хотите объявить, чтобывардейный_баз () настроил базовый класс по мере необходимости.


В дополнение к этому, моей первоначальной целью было добавить некоторые дополнительные функции к моим моделям, которые унаследованы от db.Model .Для этого на самом деле вам не нужно использовать мета-классы.Таким образом, для этого случая мы можем расширить класс db.Model , как описано на той же веб-странице.Это пример предоставления каждой модели целочисленного первичного ключа или внешнего ключа для наследования объединенной таблицы:

from flask_sqlalchemy import Model, SQLAlchemy
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declared_attr, has_inherited_table

class IdModel(Model):
    @declared_attr
    def id(cls):
        for base in cls.__mro__[1:-1]:
            if getattr(base, '__table__', None) is not None:
                type = sa.ForeignKey(base.id)
                break
        else:
            type = sa.Integer

        return sa.Column(type, primary_key=True)

db = SQLAlchemy(model_class=IdModel)

class User(db.Model):
    name = db.Column(db.String)

class Employee(User):
    title = db.Column(db.String)
...