Фильтр списков администратора Django и порядок в аннотированных полях

#django #django-models #django-admin

Вопрос:

Я пытаюсь отфильтровать администратора Django по аннотированному полю, но получаю FieldDoesNotExist ошибку.

 class Event(models.Model):
    name = models.CharField(max_length=50, blank=True)

class EventSession(models.Model):
    event = models.ForeignKey(Event, on_delete=models.CASCADE)
    start_date = models.DateTimeField()
    end_date = models.DateTimeField()


@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
    ordering = ["event_start_date"]
    list_filter = ["event_start_date", "event_end_date"]

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        qs = qs.annotate(
            event_start_date=Min("eventsession_set__start_date"), # start of first day
            event_end_date=Max("eventsession_set__start_date"), # start of last day
        )
        return qs
 

В результате в Django Admin возникает ошибка:

 FieldDoesNotExist at /admin/events/event/
Event has no field named 'event_start_date'
 

Мне нужно фильтровать event_start_date , а не eventsession_set__start_date потому, что порядок фильтрации (редактирования) в последнем случае приводит к появлению в представлении списка нескольких строк на событие (по одной для каждого сеанса).

Ошибка возникает из-за метода get_field django/db/models/options.py :

     try:
        # Retrieve field instance by name from cached or just-computed
        # field map.
        return self.fields_map[field_name]
    except KeyError:
        raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name))
 

Я на Django 3.2. Есть идеи?

Комментарии:

1. Как бы странно это ни звучало, к сожалению, это полностью не поддерживается Django

2. Хлоп. Не то, на что я надеялся, но спасибо. облом.

Ответ №1:

Это пример того, как добавить порядок и отфильтровать по аннотированным полям на странице списка администраторов django

 class EventStartDateListFilter(admin.SimpleListFilter):
    title = "start_date"
    parameter_name = "start_date"

    def lookups(self, request, model_admin):
        return (
            ("week", "week"),
            # add other filters
        )

    def queryset(self, request, queryset):
        value = self.value()
        if value == "week":
            return queryset.filter(_event_start_date__gt=now() - timedelta(weeks=1))
    
        return queryset

@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
    list_display = ["__str__", "event_start_date", "event_end_date"]

    ordering = ["event_start_date", "event_end_date"]
    list_filter = [EventStartDateListFilter]


    def event_start_date(self, obj):
       return obj._event_start_date

    event_start_date.admin_order_field = '_event_start_date'

    def event_end_date(self, obj):
        return obj._event_end_date

    event_end_date.admin_order_field = '_event_end_date'

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        qs = qs.annotate(
            _event_start_date=Min("eventsession_set__start_date"), # start of first day
            _event_end_date=Max("eventsession_set__start_date"), # start of last day
        )
        return qs
 

Ответ №2:

Я считаю, что нашел решение для дублирования записей — переопределение, get_changelist как описано здесь.

 def get_changelist(self, request, **kwargs):
    from django.contrib.admin.views.main import ChangeList

    class SortedChangeList(ChangeList):
        def get_queryset(self, request):
            qs = super().get_queryset(request)
            return qs.order_by("event_start_date")

    if request.GET.get("o"):
        return ChangeList

    return SortedChangeList