Compare commits

..

4 Commits

Author SHA1 Message Date
Arthur
24642be351 cleanup 2026-01-08 17:08:10 -08:00
Arthur
89af9efd85 cleanup 2026-01-08 17:04:00 -08:00
Arthur
99d678502f cleanup 2026-01-08 16:23:47 -08:00
Arthur
e6300ee06d Fix TomSelect dropdown ordering 2026-01-08 16:17:40 -08:00
8 changed files with 39 additions and 36 deletions

3
.gitignore vendored
View File

@@ -9,8 +9,7 @@ yarn-error.log*
/netbox/netbox/configuration.py
/netbox/netbox/ldap_config.py
/netbox/local/*
/netbox/media/*
!/netbox/media/.gitkeep
/netbox/media
/netbox/reports/*
!/netbox/reports/__init__.py
/netbox/scripts/*

View File

@@ -733,9 +733,10 @@ class ModuleForm(ModuleCommonForm, PrimaryModelForm):
)
module_bay = DynamicModelChoiceField(
label=_('Module bay'),
queryset=ModuleBay.objects.all(),
queryset=ModuleBay.objects.order_by('name'),
query_params={
'device_id': '$device'
'device_id': '$device',
'ordering': 'name',
},
context={
'disabled': 'installed_module',

View File

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -75,11 +75,15 @@ export class DynamicTomSelect extends TomSelect {
load(value: string) {
const self = this;
const currentValue = self.getValue();
// Automatically clear any cached options. (Only options included
// in the API response should be present.)
self.clearOptions();
// Populate the null option (if any) if not searching
// Clear user_options to prevent the pre-selected option from being treated specially
(self as any).user_options = {};
if (self.nullOption && !value) {
self.addOption(self.nullOption);
}
@@ -93,21 +97,33 @@ export class DynamicTomSelect extends TomSelect {
addClasses(self.wrapper, self.settings.loadingClass);
self.loading++;
// Make the API request
fetch(url)
.then(response => response.json())
.then(apiData => {
const results: Dict[] = apiData.results;
const options: Dict[] = [];
for (const result of results) {
// Add options and set $order to preserve API response order
results.forEach((result, index) => {
const option = self.getOptionFromData(result);
options.push(option);
self.addOption(option);
const key = option[self.settings.valueField as string] as string;
if (self.options[key]) {
(self.options[key] as any).$order = index;
}
});
if (self.loading > 0) {
self.loading--;
if (self.loading === 0) {
self.wrapper.classList.remove(self.settings.loadingClass as string);
}
}
return options;
})
// Pass the options to the callback function
.then(options => {
self.loadCallback(options, []);
if (currentValue && !self.items.includes(currentValue as string)) {
self.items.push(currentValue as string);
}
self.refreshOptions(false);
})
.catch(() => {
self.loadCallback([], []);

View File

@@ -213,9 +213,6 @@ class Token(models.Model):
def clean(self):
super().clean()
if self.version == TokenVersionChoices.V2 and not settings.API_TOKEN_PEPPERS:
raise ValidationError(_("Unable to save v2 tokens: API_TOKEN_PEPPERS is not defined."))
if self._state.adding:
if self.pepper_id is not None and self.pepper_id not in settings.API_TOKEN_PEPPERS:
raise ValidationError(_(

View File

@@ -1,10 +1,9 @@
from datetime import timedelta
from django.core.exceptions import ValidationError
from django.test import TestCase, override_settings
from django.test import TestCase
from django.utils import timezone
from users.choices import TokenVersionChoices
from users.models import User, Token
from utilities.testing import create_test_user
@@ -95,15 +94,6 @@ class TokenTest(TestCase):
token.refresh_from_db()
self.assertEqual(token.description, 'New Description')
@override_settings(API_TOKEN_PEPPERS={})
def test_v2_without_peppers_configured(self):
"""
Attempting to save a v2 token without API_TOKEN_PEPPERS defined should raise a ValidationError.
"""
token = Token(version=TokenVersionChoices.V2)
with self.assertRaises(ValidationError):
token.clean()
class UserConfigTest(TestCase):