mirror of https://github.com/django/django.git
Fixed #33316 -- Added pagination to admin history view.
This commit is contained in:
parent
ff0b81b56b
commit
ac5cc6cf01
|
@ -1927,6 +1927,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||
def history_view(self, request, object_id, extra_context=None):
|
||||
"The 'history' admin view for this model."
|
||||
from django.contrib.admin.models import LogEntry
|
||||
from django.contrib.admin.views.main import PAGE_VAR
|
||||
|
||||
# First check if the user can see this history.
|
||||
model = self.model
|
||||
|
@ -1945,11 +1946,19 @@ class ModelAdmin(BaseModelAdmin):
|
|||
content_type=get_content_type_for_model(model)
|
||||
).select_related().order_by('action_time')
|
||||
|
||||
paginator = self.get_paginator(request, action_list, 100)
|
||||
page_number = request.GET.get(PAGE_VAR, 1)
|
||||
page_obj = paginator.get_page(page_number)
|
||||
page_range = paginator.get_elided_page_range(page_obj.number)
|
||||
|
||||
context = {
|
||||
**self.admin_site.each_context(request),
|
||||
'title': _('Change history: %s') % obj,
|
||||
'subtitle': None,
|
||||
'action_list': action_list,
|
||||
'action_list': page_obj,
|
||||
'page_range': page_range,
|
||||
'page_var': PAGE_VAR,
|
||||
'pagination_required': paginator.count > 100,
|
||||
'module_name': str(capfirst(opts.verbose_name_plural)),
|
||||
'object': obj,
|
||||
'opts': opts,
|
||||
|
|
|
@ -774,14 +774,21 @@ a.deletelink:focus, a.deletelink:hover {
|
|||
|
||||
/* OBJECT HISTORY */
|
||||
|
||||
table#change-history {
|
||||
#change-history table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table#change-history tbody th {
|
||||
#change-history table tbody th {
|
||||
width: 16em;
|
||||
}
|
||||
|
||||
#change-history .paginator {
|
||||
color: var(--body-quiet-color);
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
background: var(--body-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* PAGE STRUCTURE */
|
||||
|
||||
#container {
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
<div class="module">
|
||||
<div id="change-history" class="module">
|
||||
|
||||
{% if action_list %}
|
||||
<table id="change-history">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% translate 'Date/time' %}</th>
|
||||
|
@ -34,6 +34,20 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="paginator">
|
||||
{% if pagination_required %}
|
||||
{% for i in page_range %}
|
||||
{% if i == action_list.paginator.ELLIPSIS %}
|
||||
{{ action_list.paginator.ELLIPSIS }}
|
||||
{% elif i == action_list.number %}
|
||||
<span class="this-page">{{ i }}</span>
|
||||
{% else %}
|
||||
<a href="?{{ page_var }}={{ i }}" {% if i == action_list.paginator.num_pages %} class="end" {% endif %}>{{ i }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{{ action_list.paginator.count }} {% if action_list.paginator.count == 1 %}{% translate "entry" %}{% else %}{% translate "entries" %}{% endif %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p>{% translate 'This object doesn’t have a change history. It probably wasn’t added via this admin site.' %}</p>
|
||||
{% endif %}
|
||||
|
|
|
@ -2016,6 +2016,10 @@ Other methods
|
|||
Django view for the page that shows the modification history for a given
|
||||
model instance.
|
||||
|
||||
.. versionchanged:: 4.1
|
||||
|
||||
Pagination was added.
|
||||
|
||||
Unlike the hook-type ``ModelAdmin`` methods detailed in the previous section,
|
||||
these five methods are in reality designed to be invoked as Django views from
|
||||
the admin application URL dispatching handler to render the pages that deal
|
||||
|
|
|
@ -58,6 +58,9 @@ Minor features
|
|||
subclasses can now control the query string value separator when filtering
|
||||
for multiple values using the ``__in`` lookup.
|
||||
|
||||
* The admin :meth:`history view <django.contrib.admin.ModelAdmin.history_view>`
|
||||
is now paginated.
|
||||
|
||||
:mod:`django.contrib.admindocs`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
from django.contrib.admin.models import LogEntry
|
||||
from django.contrib.admin.models import CHANGE, LogEntry
|
||||
from django.contrib.admin.tests import AdminSeleniumTestCase
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.paginator import Paginator
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
|
@ -43,3 +46,52 @@ class AdminHistoryViewTests(TestCase):
|
|||
'nolabel_form_field and not_a_form_field. '
|
||||
'Changed City verbose_name for city “%s”.' % city
|
||||
)
|
||||
|
||||
|
||||
@override_settings(ROOT_URLCONF='admin_views.urls')
|
||||
class SeleniumTests(AdminSeleniumTestCase):
|
||||
available_apps = ['admin_views'] + AdminSeleniumTestCase.available_apps
|
||||
|
||||
def setUp(self):
|
||||
self.superuser = User.objects.create_superuser(
|
||||
username='super', password='secret', email='super@example.com',
|
||||
)
|
||||
content_type_pk = ContentType.objects.get_for_model(User).pk
|
||||
for i in range(1, 1101):
|
||||
LogEntry.objects.log_action(
|
||||
self.superuser.pk,
|
||||
content_type_pk,
|
||||
self.superuser.pk,
|
||||
repr(self.superuser),
|
||||
CHANGE,
|
||||
change_message=f'Changed something {i}',
|
||||
)
|
||||
self.admin_login(
|
||||
username='super', password='secret', login_url=reverse('admin:index'),
|
||||
)
|
||||
|
||||
def test_pagination(self):
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
user_history_url = reverse('admin:auth_user_history', args=(self.superuser.pk,))
|
||||
self.selenium.get(self.live_server_url + user_history_url)
|
||||
|
||||
paginator = self.selenium.find_element(By.CSS_SELECTOR, '.paginator')
|
||||
self.assertTrue(paginator.is_displayed())
|
||||
self.assertIn('%s entries' % LogEntry.objects.count(), paginator.text)
|
||||
self.assertIn(str(Paginator.ELLIPSIS), paginator.text)
|
||||
# The current page.
|
||||
current_page_link = self.selenium.find_element(By.CSS_SELECTOR, 'span.this-page')
|
||||
self.assertEqual(current_page_link.text, '1')
|
||||
# The last page.
|
||||
last_page_link = self.selenium.find_element(By.CSS_SELECTOR, '.end')
|
||||
self.assertTrue(last_page_link.text, '20')
|
||||
# Select the second page.
|
||||
pages = paginator.find_elements(By.TAG_NAME, 'a')
|
||||
second_page_link = pages[0]
|
||||
self.assertEqual(second_page_link.text, '2')
|
||||
second_page_link.click()
|
||||
self.assertIn('?p=2', self.selenium.current_url)
|
||||
rows = self.selenium.find_elements(By.CSS_SELECTOR, '#change-history tbody tr')
|
||||
self.assertIn('Changed something 101', rows[0].text)
|
||||
self.assertIn('Changed something 200', rows[-1].text)
|
||||
|
|
Loading…
Reference in New Issue