Фильтр списка администраторов Django с использованием свойства модели
У меня есть модель, как показано ниже:
class Order(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
number = models.CharField(max_length=36, blank=True, null=True)
external_number = models.CharField(default=None, blank=True, null=True, max_length=250)
@property
def is_external(self) -> bool:
return self.external_number is not None
И я регистрирую свою модель следующим образом:
@admin.register(Order)
class OrderAdmin(ReadOnlyIDAdminClass):
list_filter = ["number", "is_external"]
list_display = ["id", "is_external"]
Но поскольку is_external не является полем базы данных, я получаю следующую ошибку:
(admin.E116) The value of 'list_filter[1]' refers to 'is_external', which does not refer to a Field
Я пробовал что-то вроде создания пользовательского фильтра:
class IsExternal(admin.FieldListFilter):
# Human-readable title which will be displayed in the
# right admin sidebar just above the filter options.
title = 'is external'
# Parameter for the filter that will be used in the URL query.
parameter_name = 'is_external'
def lookups(self, request, model_admin):
return (
('True', True),
('False', False)
)
def queryset(self, request, queryset):
if self.value():
return queryset.filter(external_number__isnull=False)
return queryset.filter(external_number__isnull=True)
и затем обновить моего администратора:
@admin.register(Order)
class OrderAdmin(ReadOnlyIDAdminClass):
list_filter = ["number", ("is_external", IsExternal)]
list_display = ["id", "is_external"]
Но он повышает:
Order has no field named 'is_external' что, как мне кажется, имеет смысл, но есть ли способ сделать это? Мне кажется, что я что-то напутал.
После нескольких часов попыток исправить это, только сейчас я нашел рабочее решение. Мы используем пользовательский фильтр выше, но мы используем его напрямую, например:
@admin.register(Order)
class OrderAdmin(ReadOnlyIDAdminClass):
list_filter = ["number", IsExternal]
list_display = ["id", "is_external"]
Теперь это должно работать; также обратите внимание, что в self.value() нашего фильтра queryset возвращает строку, поэтому мы должны привести/разобрать наши значения соответствующим образом, в моем фильтре выше, я получу значения True or False как строку.
Редактирование:
Я обновил свой пользовательский фильтр, чтобы он работал как фильтр Django, где мы используем значения 1 и 0 для True и False.
class IsExternal(SimpleListFilter):
# Human-readable title which will be displayed in the
# right admin sidebar just above the filter options.
title = 'is external'
# Parameter for the filter that will be used in the URL query.
parameter_name = 'is_external'
def lookups(self, request, model_admin):
return (
(1, 'yes'),
(0, 'no')
)
def queryset(self, request, queryset):
if self.value() == "1":
return queryset.filter(external_number__isnull=False)
elif self.value() == "0":
return queryset.filter(external_number__isnull=True)
return queryset
Обратите внимание, что сейчас используется та же логика, определенная в свойстве модели (но не само свойство), но на уровне базы данных. Если есть способ использовать свойства напрямую, я буду рад, если вы поделитесь!