Создание представления списка с группировкой по имени поля и ссылкой на детали - django admin

Итак, у меня есть следующая таблица:

class IncomeStatementQuarterly(models.Model):
    date = models.DateField()
    statement = models.CharField(max_length=1000, blank=True, null=True)
    ticker = models.CharField(max_length=1000, blank=True, null=True)
    security = models.ForeignKey(SecuritiesTable, models.DO_NOTHING)
    line_item = models.CharField(max_length=1000, blank=True, null=True)
    amount = models.DecimalField(max_digits=65535, decimal_places=4, blank=True, null=True)

    class Meta:
        ordering=('ticker',)
        verbose_name = "Income statement / Quarterly"
        verbose_name_plural = "Income statements / Quarterly"
        managed = False
        db_table = 'income_statement_quarterly'
        unique_together = (('date', 'ticker', 'line_item'),)

и следующее в моем классе admin.py:

@admin.register(IncomeStatementQuarterly)
class IncomeStatementQuarterlyAdmin(admin.ModelAdmin):
    
    date_hierarchy = 'date'
    list_filter = ('line_item',)
    list_display = ("ticker", "date", "statement", "line_item", 'amount')
    search_fields = ['ticker']
    list_display_links = ('ticker',)


    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

    def has_change_permission(self, request, obj=None) -> bool:
        return False

Моя цель - создать представление, сгруппированное по полю 'ticker' и 'date'. На данный момент, мое представление администратора отображает каждую строку моей модели следующим образом:

enter image description here

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

Возможно ли это?

Я искал везде последние 5 дней и готовился создать новую модель statements_list, состоящую из всех уникальных комбинаций полей (тикер, дата) моей текущей модели, которая будет иметь первичный ключ, связывающий ее с уникальной комбинацией полей текущей модели (дата, тикер)

Надеюсь, это не слишком запутанно... В основном конечный результат будет выглядеть примерно так (исходя из того, что доступно на картинке):

  • A | Jan 31, 2005 ---> ссылка на все заявления с этими параметрами
  • A | Apr 30, 2005 ---> ссылка на все утверждения с этими параметрами

Вы можете реализовать это, переопределив метод get_queryset() класса ModelAdmin и используя group_by запрос.

queryset = IncomeStatementQuarterly.objects.values('date', 'ticker').annotate(max_id=Max('id'))

In mysql raw query like,

SELECT date, ticker, MAX(id) AS "max_id" 
FROM incomestatementquarterly 
GROUP BY date, ticker

Но если вы используете admin default change_list template, то этот запрос выдает error. Потому что django admin при выводе значений в шаблоне ожидает список объектов в наборе запросов, а group_by запрос возвращает в наборе запросов список dict.

Если вы хотите использовать приведенный выше запрос, то вы переопределяете шаблон change_list и выводите данные самостоятельно.

Другое решение:

Еще один способ получить те же данные,

Override get_queryset() метод класса ModelAdmin

Ваш класс администратора,

@admin.register(IncomeStatementQuarterly)
class IncomeStatementQuarterlyAdmin(admin.ModelAdmin):
    
    list_display = ("ticker", "date", "view_details")
    ....
    ....
    def get_queryset(self, request):
        max_ids_subquery = IncomeStatementQuarterly.objects.values('date', 'ticker').annotate(max_id=Max('id')).values('max_id')

        queryset = IncomeStatementQuarterly.objects.filter(id__in=max_ids_subquery)

        return queryset

    def view_details(self, obj):
        url = ("%s?ticker=%s&date=%s") % (reverse('admin:your_app_incomestatementquarterlyproxy_changelist'),obj.ticker, obj.date)
        return format_html('<a class="button" href="{}">View</a>',url)

В mysql запрос simiral to,

SELECT * FROM incomestatementquarterly 
WHERE id IN ( SELECT MAX(id) AS "max_id" FROM incomestatementquarterly t1 GROUP BY t1.date, t1.ticker)

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

class IncomeStatementQuarterlyProxy(IncomeStatementQuarterly):

    class Meta:
        proxy = True

класс администратора прокси-модели

@admin.register(IncomeStatementQuarterlyProxy)
class IncomeStatementQuarterlyProxyAdmin(admin.ModelAdmin):
    
    date_hierarchy = 'date'
    list_filter = ('line_item',)
    list_display = ("ticker", "date", "statement", "line_item", 'amount')
    search_fields = ['ticker']
    list_display_links = ('ticker',)


    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

    def has_change_permission(self, request, obj=None) -> bool:
        return False
Вернуться на верх