фляга администрирования пользовательских QueryAjaxModelLoader

из того, что я понимаю, администратор Flask поддерживает использование AJAX для загрузки модели внешнего ключа. The Администратор Колбы-Документация По Модели охватывает основы под заголовком form_ajax_refs. Мне удалось успешно использовать это во многих случаях, однако у меня возникают проблемы с уровнем кастомизации, что я надеюсь достичь. Позвольте мне уточнить.

у меня есть Product модель Organisation модель и таблица соединений для их связи, определенные как Итак:

class Product(Base):
    __tablename__ = "products"

    product_uuid = Column(UUID(as_uuid=True), primary_key=True)
    title = Column(String, nullable=False)
    description = Column(String, nullable=False)
    last_seen = Column(DateTime(timezone=True), nullable=False, index=True)
    price = Column(Numeric(precision=7, scale=2), nullable=False, index=True)

class Organisation(Base):
    __tablename__ = "organisations"
    org_id = Column(String, primary_key=True)
    org_name = Column(String, nullable=False)
    products = relationship(
        Product,
        secondary="organisation_products",
        backref="organisations"
    )

organisation_products_table = Table(
    "organisation_products",
    Base.metadata,
    Column("org_id", String, ForeignKey("organisations.org_id"), nullable=False),
    Column("product_uuid", UUID(as_uuid=True), ForeignKey("products.product_uuid"), nullable=False),
    UniqueConstraint("org_id", "product_uuid"),
    )

в представлении модели администратора колбы модели под названием CuratedList это имеет ограничение внешнего ключа для Product модель, я использую form_ajax_refs в форме создать представление, чтобы разрешить выбор динамически загруженного Product предметы.

form_ajax_refs = {"products": {"fields": (Product.title,)}}

это хорошо работает, чтобы показать мне все строки Product модель.

мое текущее требование, однако, является используйте только загрузчик модели AJAX для отображения продуктов с определенным org_id, например "Погуглить."

Попытка № 1

переопределить о ModelView класс присоединиться на organisation_products_table и фильтра org_id. Это выглядит примерно так:

def get_query(self):
    return (
        self.session.query(CuratedList)
        .join(
            curated_list_items_table,
            curated_list_items_table.c.list_uuid == CuratedList.list_uuid
        )
        .join(
            Product,
            Product.product_uuid == curated_list_items_table.c.product_uuid
        )
        .join(
            organisation_products_table,
            organisation_products_table.c.product_uuid == Product.product_uuid
        )
        .filter(CuratedList.org_id == "Google")
        .filter(organisation_products_table.c.org_id == "Google")
    )

к сожалению, это не решит проблему, и возвращает такое же поведение, как:

def get_query(self):
    return (
        self.session.query(CuratedList)
        .filter(CuratedList.org_id == self._org_id)
    )

это не влияет на поведение form_ajax_refs.

Попытка Номер 2

на Администратор Колбы-Документация По Модели упоминает другой способ использования form_ajax_refs, который включает в себя использование QueryAjaxModelLoader класса.

во второй попытке я подкласс QueryAjaxModelLoader class и попробуйте переопределить значения it's model, session или fields переменные. Что-то вроде этого:--33-->

class ProductAjaxModelLoader(QueryAjaxModelLoader):
    def __init__(self, name, session, model, **options):
        super(ProductAjaxModelLoader, self).__init__(name, session, model, **options)

        fields = (
            session.query(model.title)
            .join(organisation_products_table)
            .filter(organisation_products_table.c.org_id == "Google")
        ).all()

        self.fields = fields
        self.model = model
        self.session = session

а потом вместо прежних form_ajax_refs подход, я использую мой новый AjaxModelLoader вот так:

form_ajax_refs = {
    "products": ProductAjaxModelLoader(
        "products", db.session, Product, fields=['title']
    )
}

к сожалению, ли переопределение значений session или model С моим запросом возвращает no продукты из загрузчика AJAX и переопределение fields по-прежнему возвращает все продукты; не только продукты org_id "Google".

к чему я надеюсь не прибегать

я хотел бы иметь возможность достичь этого без создание новой модели для каждой организации, поскольку это окажется не масштабируемым и плохим дизайном.

любые предложения приветствуются. Спасибо.

2 ответов


благодаря комментарию Joes к моему первоначальному вопросу, я сформулировал рабочее решение:

переопределить AjaxModelLoader функции get_list вот так:

def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
    filters = list(
        field.ilike(u'%%%s%%' % term) for field in self._cached_fields
    )
    filters.append(Organisation.org_id == "Google")
    return (
        db.session.query(Product)
        .join(organisation_products_table)
        .join(Organisation)
        .filter(*filters)
        .all()
    )

после многих проб и ошибок, и благодаря сообщениям выше, я представляю общий способ передачи фильтров загрузчику модели Ajax.

вот общий класс, который включает фильтрацию по таблице внешнего ключа.

from flask_admin.contrib.sqla.ajax import QueryAjaxModelLoader, DEFAULT_PAGE_SIZE

class FilteredAjaxModelLoader(QueryAjaxModelLoader):
    additional_filters = []

    def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
        filters = list(
            field.ilike(u'%%%s%%' % term) for field in self._cached_fields
        )
        for f in self.additional_filters:
            filters.append(f)
        # filters.append(User.list_id == 2) # Now this is passed in the constructor
        # filters.append(User.is_active == 'Y')
        return (
            db.session.query(self.model)
                .filter(*filters)
                .all()
        )

    def __init__(self, name, session, model, **options):
        super(FilteredAjaxModelLoader, self).__init__(name, session, model, **options)
        self.additional_filters = options.get('filters')

использование:

передать его в form_ajax_refs Как вы бы QueryAjaxModelLoader

FilteredAjaxModelLoader('component', db.session, User, fields=['list_value'],
                                filters=[User.list_id == 2, User.is_active == 'Y'])

надеюсь, что это поможет.