Как перейти к «следующему» объекту в админке Django? - PullRequest
0 голосов
/ 19 сентября 2019

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

Это то, что я должен реализовать:

from django.shortcuts import redirect

class MyModelAdmin(admin.ModelAdmin):
    change_form_template = ['mymodel-savenext.html']

    def response_change(self, request, obj):
        """Determines the HttpResponse for the change_view stage."""
        if '_save_next' in request.POST:
            # -> How do I get the queryset in the original order and find `obj` it it?
            nextobj = ?
            return redirect('admin:app_mymodel_admin', nextobj.id)
        return super().response_change(request, obj)

Обратите внимание, что набор запросов может быть большим.Если пользователь сразу перейдет к URL-адресу формы изменения (например, вставит URL-адрес), имеет смысл перенаправить его обратно в список или принять порядок «по умолчанию», например, получить следующий идентификатор.

1 Ответ

0 голосов
/ 19 сентября 2019

В этом есть две трудности:

  1. Чтобы собрать тот же набор запросов , который использовался для перечисления объектов, и
  2. Возможно изменение этого набора запросов для получениятолько объект, следующий за текущим (без необходимости проходить через все это).

Что касается первого: Django сохраняет параметры, использованные для генерации списка, в GETпараметр называется _changelist_filters, но этот аргумент не используется changeform_view.Он только «восстанавливается» как параметры GET при возврате в список.Чтобы использовать этот аргумент, мы должны изменить HttpRequest (что звучит плохо), чтобы мы могли создать экземпляр django.contrib.admin.views.main.ChangeList - объект, который будет обрабатывать эти фильтры и генерировать набор запросов.

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

В результате получается этот миксин, добавляемый в ModelAdmin класс:

from django.http import QueryDict


class GotoNextAdminMixin(object):

    def get_next_instance_pk(self, request, current):
        """Returns the primary key of the next object in the query (considering filters and ordering).
        Returns None if the object is not in the queryset.
        """
        querystring = request.GET.get('_changelist_filters')
        if querystring:
            # Alters the HttpRequest object to make it function as a list request
            original_get = request.GET
            try:
                request.GET = QueryDict(querystring)
                # from django.contrib.admin.options: ModelAdmin.changelist_view
                ChangeList = self.get_changelist(request)
                list_display = self.get_list_display(request)
                changelist = ChangeList(
                    request, self.model, list_display,
                    self.get_list_display_links(request, list_display),
                    self.get_list_filter(request),
                    self.date_hierarchy,
                    self.get_search_fields(request),
                    self.get_list_select_related(request),
                    self.list_per_page,
                    self.list_max_show_all,
                    self.list_editable,
                    self)
                queryset = changelist.get_queryset(request)
            finally:
                request.GET = original_get
        else:
            queryset = self.get_queryset(request)

        # Try to find pk in this list:
        iterator = queryset.values_list('pk', flat=True).iterator()
        try:
            while next(iterator) != current.pk:
                continue
            return next(iterator)
        except StopIteration:
            pass # Not found or it was the last item

Затем в моем ModelAdminкласс:

class MyModelAdmin(admin.ModelAdmin, GotoNextAdminMixin):

    def response_change(self, request, obj):
        """Determines the HttpResponse for the change_view stage."""
        if '_save_next' in request.POST:
            next_pk = self.get_next_instance_pk(request, obj)
            if next_pk:
                response = redirect('admin:app_mymodel_change', next_pk)
                qs = request.GET.urlencode()  # keeps _changelist_filters
            else:
                # Last item (or no longer in list) - go back to list in the same position
                response = redirect('admin:app_mymodel_changelist')
                qs = request.GET.get('_changelist_filters')

            if qs:
                response['Location'] += '?' + qs
            return response

        return super().response_change(request, obj)

Недостатки этого подхода:

  • Необходимо изменить переменную HttpRequest.GET.В текущей версии Django это не свойство, поэтому оно может изменяться, но в будущем это может быть не так.
  • Должен выполнять итерацию по всему набору запросов (хотя мы получаем только PK) - плохо для больших таблиц или дорогозапросы.
  • Работает только для моделей с первичным ключом с одним полем (как и большинство вещей в Django, поэтому это не может быть проблемой).
  • Если объект больше не находится в списке(например, был фильтр и текущий объект был отфильтрован), усилия были потрачены впустую.

Плюсы:

  • Работает!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...