Можно ли программно добавить большое количество фильтров

У меня есть таблица с примерно 30 столбцами, и я хотел бы прикрепить пять фильтров к большинству столбцов в очень повторяющейся манере. Поэтому я надеялся, что смогу использовать декоратор класса, чтобы определить их в соответствии с этим ответом SO. Никакой радости. TypeError: 'NoneType' object is not callable (во время выполнения, когда я вызываю представление)

Потом я прочитал о "правильном" метаклассировании и попробовал

class SettingsMeta(type):
    def __new__(cls, clsname, bases, attrs):

        for name in ('fx_t', 'status'): # just two for now but target ~60 with 5 different lookup_expr
            attr_name = name + '_start'
            attrs[attr_name] =  FD.CharFilter( 
                field_name = name,
                lookup_expr = 'istartswith' ,
                label =  name.replace('_t','').capitalize() + ' starts with',
            )  
        return super(SettingsMeta, cls).__new__(cls, clsname, bases, uppercase_attrs)

class SelectMaskdataFilters( FD.FilterSet, metaclass=SettingsMeta): 
    class Meta:
        model = Maskdata
        fields = {
            'notes':        [ 'icontains',],
        }
    #status_sw = FD.CharFilter( field_name='status', lookup_expr='startswith')
    ...

Опять ничего хорошего: TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases при запуске сервера.

SelectMaskDataFilters работает как ожидалось в представлении, если я просто удалю metaclass=SettingsMeta

Я не в себе, но мне очень не нравится повторяться с сотней или около того определений фильтров, все из которых будут почти одинаковыми. Итак, знает ли кто-нибудь, как определить большое количество фильтров в FilterSet, кроме копирования, вставки и ошибочных небольших изменений? Обратите внимание, большинство фильтров будут использовать method=, а не lookup_expr=, поэтому атрибут fields во внутреннем классе Meta не пригодится

Вы можете определить их в локальной области видимости SelectMaskdataFilters:

import django_filters

class SelectMaskdataFilters(django_filters.FilterSet):
    for name in ('fx_t', 'status'):
        label = name.replace('_t','').capitalize()
        locals()[f'{name}_start'] = django_filters.CharFilter( 
            field_name = name,
            lookup_expr = 'istartswith' ,
            label = f'{label} starts with'
        )
    
    class Meta:
        model = Maskdata
        fields = {
            'notes': [ 'icontains',],
        }

Ошибка конфликта метаклассов связана с тем, что класс, от которого вы наследуете (FD.FilterSet), уже использует пользовательский метакласс. Поэтому, если вы наследуете свой от type, Python имеет конфликтующие метаклассы, которые он не может заставить хорошо работать вместе: какой метод metaclass.__new__ должен быть вызван? Тот, который находится в вашем метаклассе или в метаклассе FD.FilterSet.

Способ заставить это работать правильно - заставить ваши метаклассы работать совместно, и иметь один метакласс, который разрешает конфликты. В этом случае все, что вам нужно сделать, это наследоваться от метакласса самого FD.FieldSet вместо type. (и, конечно же, вызывать методы суперкласса, если это необходимо)

Вы можете использовать однопараметрическую форму типа для получения соответствующего метакласса, вам даже не нужно импортировать его явно.

В вашем листинге просто измените эту строку:

class SettingsMeta(type):

for

class SettingsMeta(type(FD.FilterSet)):

и все должно работать (без необходимости повторять себя в теле класса для каждой из ваших моделей)

Вернуться на верх