Можно ли программно добавить большое количество фильтров
У меня есть таблица с примерно 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)):
и все должно работать (без необходимости повторять себя в теле класса для каждой из ваших моделей)