1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Add PAGINATE_COUNT, MAX_PAGE_SIZE

This commit is contained in:
jeremystretch
2021-10-26 11:39:39 -04:00
parent 94804fecd8
commit 64d8512fc3
9 changed files with 62 additions and 58 deletions

View File

@ -25,9 +25,9 @@ class ConfigRevisionAdmin(admin.ModelAdmin):
# ('Logging', {
# 'fields': ('CHANGELOG_RETENTION',),
# }),
# ('Pagination', {
# 'fields': ('MAX_PAGE_SIZE', 'PAGINATE_COUNT'),
# }),
('Pagination', {
'fields': ('PAGINATE_COUNT', 'MAX_PAGE_SIZE'),
}),
('Miscellaneous', {
'fields': ('MAINTENANCE_MODE', 'MAPS_URL'),
}),

View File

@ -9,6 +9,7 @@ from rest_framework.decorators import action
from rest_framework.response import Response
from ipam.models import *
from netbox.config import Config
from utilities.constants import ADVISORY_LOCK_KEYS
from . import serializers
@ -160,12 +161,15 @@ class AvailableIPsMixin:
# Determine the maximum number of IPs to return
else:
config = Config()
PAGINATE_COUNT = config.PAGINATE_COUNT
MAX_PAGE_SIZE = config.MAX_PAGE_SIZE
try:
limit = int(request.query_params.get('limit', settings.PAGINATE_COUNT))
limit = int(request.query_params.get('limit', PAGINATE_COUNT))
except ValueError:
limit = settings.PAGINATE_COUNT
if settings.MAX_PAGE_SIZE:
limit = min(limit, settings.MAX_PAGE_SIZE)
limit = PAGINATE_COUNT
if MAX_PAGE_SIZE:
limit = min(limit, MAX_PAGE_SIZE)
# Calculate available IPs within the parent
ip_list = []

View File

@ -2,6 +2,8 @@ from django.conf import settings
from django.db.models import QuerySet
from rest_framework.pagination import LimitOffsetPagination
from netbox.config import Config
class OptionalLimitOffsetPagination(LimitOffsetPagination):
"""
@ -9,6 +11,8 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
matching a query, but retains the same format as a paginated request. The limit can only be disabled if
MAX_PAGE_SIZE has been set to 0 or None.
"""
def __init__(self):
self.default_limit = Config().PAGINATE_COUNT
def paginate_queryset(self, queryset, request, view=None):
@ -40,11 +44,9 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
if limit < 0:
raise ValueError()
# Enforce maximum page size, if defined
if settings.MAX_PAGE_SIZE:
if limit == 0:
return settings.MAX_PAGE_SIZE
else:
return min(limit, settings.MAX_PAGE_SIZE)
MAX_PAGE_SIZE = Config().MAX_PAGE_SIZE
if MAX_PAGE_SIZE:
return MAX_PAGE_SIZE if limit == 0 else min(limit, MAX_PAGE_SIZE)
return limit
except (KeyError, ValueError):
pass

View File

@ -82,6 +82,20 @@ PARAMS = (
field_kwargs={'base_field': forms.CharField()}
),
# Pagination
ConfigParam(
name='PAGINATE_COUNT',
label='Default page size',
default=50,
field=forms.IntegerField
),
ConfigParam(
name='MAX_PAGE_SIZE',
label='Maximum page size',
default=1000,
field=forms.IntegerField
),
# Miscellaneous
ConfigParam(
name='MAINTENANCE_MODE',

View File

@ -158,11 +158,6 @@ LOGIN_REQUIRED = False
# re-authenticate. (Default: 1209600 [14 days])
LOGIN_TIMEOUT = None
# An API consumer can request an arbitrary number of objects =by appending the "limit" parameter to the URL (e.g.
# "?limit=1000"). This setting defines the maximum limit. Setting it to 0 or None will allow an API consumer to request
# all objects by specifying "?limit=0".
MAX_PAGE_SIZE = 1000
# The file path where uploaded media such as image attachments are stored. A trailing slash is not needed. Note that
# the default value of this setting is derived from the installed location.
# MEDIA_ROOT = '/opt/netbox/netbox/media'
@ -191,9 +186,6 @@ NAPALM_TIMEOUT = 30
# be provided as a dictionary.
NAPALM_ARGS = {}
# Determine how many objects to display per page within a list. (Default: 50)
PAGINATE_COUNT = 50
# Enable installed plugins. Add the name of each plugin to the list.
PLUGINS = []

View File

@ -75,6 +75,7 @@ ADMINS = getattr(configuration, 'ADMINS', [])
BASE_PATH = getattr(configuration, 'BASE_PATH', '')
if BASE_PATH:
BASE_PATH = BASE_PATH.strip('/') + '/' # Enforce trailing slash only
CHANGELOG_RETENTION = getattr(configuration, 'CHANGELOG_RETENTION', 90)
CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False)
CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', [])
CORS_ORIGIN_WHITELIST = getattr(configuration, 'CORS_ORIGIN_WHITELIST', [])
@ -85,12 +86,19 @@ DEBUG = getattr(configuration, 'DEBUG', False)
DEVELOPER = getattr(configuration, 'DEVELOPER', False)
DOCS_ROOT = getattr(configuration, 'DOCS_ROOT', os.path.join(os.path.dirname(BASE_DIR), 'docs'))
EMAIL = getattr(configuration, 'EMAIL', {})
EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', [])
GRAPHQL_ENABLED = getattr(configuration, 'GRAPHQL_ENABLED', True)
HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', None)
INTERNAL_IPS = getattr(configuration, 'INTERNAL_IPS', ('127.0.0.1', '::1'))
LOGGING = getattr(configuration, 'LOGGING', {})
LOGIN_PERSISTENCE = getattr(configuration, 'LOGIN_PERSISTENCE', False)
LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False)
LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None)
MEDIA_ROOT = getattr(configuration, 'MEDIA_ROOT', os.path.join(BASE_DIR, 'media')).rstrip('/')
METRICS_ENABLED = getattr(configuration, 'METRICS_ENABLED', False)
PLUGINS = getattr(configuration, 'PLUGINS', [])
PLUGINS_CONFIG = getattr(configuration, 'PLUGINS_CONFIG', {})
RELEASE_CHECK_URL = getattr(configuration, 'RELEASE_CHECK_URL', None)
REMOTE_AUTH_AUTO_CREATE_USER = getattr(configuration, 'REMOTE_AUTH_AUTO_CREATE_USER', False)
REMOTE_AUTH_BACKEND = getattr(configuration, 'REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend')
REMOTE_AUTH_DEFAULT_GROUPS = getattr(configuration, 'REMOTE_AUTH_DEFAULT_GROUPS', [])
@ -122,20 +130,10 @@ for param in PARAMS:
if hasattr(configuration, param.name):
globals()[param.name] = getattr(configuration, param.name)
CHANGELOG_RETENTION = getattr(configuration, 'CHANGELOG_RETENTION', 90)
EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', [])
GRAPHQL_ENABLED = getattr(configuration, 'GRAPHQL_ENABLED', True)
LOGIN_PERSISTENCE = getattr(configuration, 'LOGIN_PERSISTENCE', False)
LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False)
LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None)
MAX_PAGE_SIZE = getattr(configuration, 'MAX_PAGE_SIZE', 1000)
METRICS_ENABLED = getattr(configuration, 'METRICS_ENABLED', False)
NAPALM_ARGS = getattr(configuration, 'NAPALM_ARGS', {})
NAPALM_PASSWORD = getattr(configuration, 'NAPALM_PASSWORD', '')
NAPALM_TIMEOUT = getattr(configuration, 'NAPALM_TIMEOUT', 30)
NAPALM_USERNAME = getattr(configuration, 'NAPALM_USERNAME', '')
PAGINATE_COUNT = getattr(configuration, 'PAGINATE_COUNT', 50)
RELEASE_CHECK_URL = getattr(configuration, 'RELEASE_CHECK_URL', None)
# Validate update repo URL and timeout
if RELEASE_CHECK_URL:
@ -462,7 +460,7 @@ REST_FRAMEWORK = {
),
'DEFAULT_VERSION': REST_FRAMEWORK_VERSION,
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning',
'PAGE_SIZE': PAGINATE_COUNT,
# 'PAGE_SIZE': PAGINATE_COUNT,
'SCHEMA_COERCE_METHOD_NAMES': {
# Default mappings
'retrieve': 'read',
@ -561,23 +559,6 @@ RQ_QUEUES = {
}
#
# NetBox internal settings
#
# Pagination
if MAX_PAGE_SIZE and PAGINATE_COUNT > MAX_PAGE_SIZE:
raise ImproperlyConfigured(
f"PAGINATE_COUNT ({PAGINATE_COUNT}) must be less than or equal to MAX_PAGE_SIZE ({MAX_PAGE_SIZE}), if set."
)
PER_PAGE_DEFAULTS = [
25, 50, 100, 250, 500, 1000
]
if PAGINATE_COUNT not in PER_PAGE_DEFAULTS:
PER_PAGE_DEFAULTS.append(PAGINATE_COUNT)
PER_PAGE_DEFAULTS = sorted(PER_PAGE_DEFAULTS)
#
# Plugins
#

View File

@ -36,7 +36,7 @@
{% endfor %}
<div class="input-group input-group-sm">
<select name="per_page" class="form-select per-page">
{% for n in settings.PER_PAGE_DEFAULTS %}
{% for n in page.paginator.get_page_lengths %}
<option value="{{ n }}"{% if page.paginator.per_page == n %} selected="selected"{% endif %}>{{ n }}</option>
{% endfor %}
</select>

View File

@ -1,8 +1,12 @@
from django.conf import settings
from django.core.paginator import Paginator, Page
from netbox.config import Config
class EnhancedPaginator(Paginator):
default_page_lengths = (
25, 50, 100, 250, 500, 1000
)
def __init__(self, object_list, per_page, orphans=None, **kwargs):
@ -10,9 +14,9 @@ class EnhancedPaginator(Paginator):
try:
per_page = int(per_page)
if per_page < 1:
per_page = settings.PAGINATE_COUNT
per_page = Config().PAGINATE_COUNT
except ValueError:
per_page = settings.PAGINATE_COUNT
per_page = Config().PAGINATE_COUNT
# Set orphans count based on page size
if orphans is None and per_page <= 50:
@ -25,6 +29,11 @@ class EnhancedPaginator(Paginator):
def _get_page(self, *args, **kwargs):
return EnhancedPage(*args, **kwargs)
def get_page_lengths(self):
if self.per_page not in self.default_page_lengths:
return sorted([*self.default_page_lengths, self.per_page])
return self.default_page_lengths
class EnhancedPage(Page):
@ -57,17 +66,19 @@ def get_paginate_count(request):
Return the lesser of the calculated value and MAX_PAGE_SIZE.
"""
config = Config()
if 'per_page' in request.GET:
try:
per_page = int(request.GET.get('per_page'))
if request.user.is_authenticated:
request.user.config.set('pagination.per_page', per_page, commit=True)
return min(per_page, settings.MAX_PAGE_SIZE)
return min(per_page, config.MAX_PAGE_SIZE)
except ValueError:
pass
if request.user.is_authenticated:
per_page = request.user.config.get('pagination.per_page', settings.PAGINATE_COUNT)
return min(per_page, settings.MAX_PAGE_SIZE)
per_page = request.user.config.get('pagination.per_page', config.PAGINATE_COUNT)
return min(per_page, config.MAX_PAGE_SIZE)
return min(settings.PAGINATE_COUNT, settings.MAX_PAGE_SIZE)
return min(config.PAGINATE_COUNT, config.MAX_PAGE_SIZE)

View File

@ -1,6 +1,5 @@
import urllib.parse
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase, override_settings
from django.urls import reverse
@ -10,6 +9,7 @@ from dcim.models import Region, Site
from extras.choices import CustomFieldTypeChoices
from extras.models import CustomField
from ipam.models import VLAN
from netbox.config import Config
from utilities.testing import APITestCase, disable_warnings
@ -137,7 +137,7 @@ class APIPaginationTestCase(APITestCase):
def test_default_page_size(self):
response = self.client.get(self.url, format='json', **self.header)
page_size = settings.PAGINATE_COUNT
page_size = Config().PAGINATE_COUNT
self.assertLess(page_size, 100, "Default page size not sufficient for data set")
self.assertHttpStatus(response, status.HTTP_200_OK)