Compare commits

..

27 Commits

Author SHA1 Message Date
Jeremy Stretch
c9a7527f33 Release v2.8.5 2020-05-26 16:17:01 -04:00
Jeremy Stretch
5f9b25453d Merge pull request #4692 from netbox-community/4525-objectvar-initial-data
Fixes #4525: Allow passing initial data to custom script MultiObjectVar
2020-05-26 15:54:25 -04:00
Jeremy Stretch
ccc31b2c7c Fixes #4525: Allow passing initial data to custom script MultiObjectVar 2020-05-26 15:34:29 -04:00
Jeremy Stretch
e54d441433 Remove "disable plugins" from bug report to prevent irrelevant search results 2020-05-26 10:06:46 -04:00
Jeremy Stretch
88cffca270 Closes #4650: Expose INTERNAL_IPS configuration parameter 2020-05-26 10:01:49 -04:00
Jeremy Stretch
92f49b4711 Closes #4672: Set default color for rack and devices roles 2020-05-26 09:36:27 -04:00
Jeremy Stretch
faf3885775 Merge pull request #4689 from kobayashi/4684-devicetype-import-comment
Fixes #4684: Fix ignored comment when importing DeviceType
2020-05-26 09:12:14 -04:00
Jeremy Stretch
f04340679e Merge branch 'develop' into 4684-devicetype-import-comment 2020-05-26 09:11:50 -04:00
Jeremy Stretch
7f5583c7ae Merge pull request #4690 from kobayashi/4676-docs-default-remote-auth
Closes #4676: Set `False` as default value of REMOTE_AUTH_AUTO_CREATE_USER
2020-05-26 09:07:26 -04:00
Jeremy Stretch
a5785552d9 Changelog for #4651, #4652 2020-05-26 09:05:18 -04:00
Jeremy Stretch
abcd26da43 Merge pull request #4682 from netbox-community/4651-csrf-in-plugintemplateextension
4651: Add `csrf_token` to PluginTemplateExtension context
2020-05-26 09:03:07 -04:00
Jeremy Stretch
4545c15173 Merge branch 'develop' into 4651-csrf-in-plugintemplateextension 2020-05-26 09:02:39 -04:00
Jeremy Stretch
b7cf85e8c8 Merge pull request #4681 from netbox-community/4652-perms-in-plugintemplateextension
4652: Add `perms` to PluginTemplateExtension context
2020-05-26 09:02:08 -04:00
kobayashi
9cde377133 Closes #4676: Set default value of REMOTE_AUTH_AUTO_CREATE_USER as False in docs 2020-05-26 01:26:26 -04:00
kobayashi
74c29b0bb7 Fixes #4684: Fix ignored comment when importing DeviceType 2020-05-26 01:17:10 -04:00
Sander Steffann
ff3b348771 Add csrf_token to PluginTemplateExtension context 2020-05-22 22:28:04 +02:00
Sander Steffann
27700d316f Add perms to PluginTemplateExtension context 2020-05-22 22:24:39 +02:00
Jeremy Stretch
1f5d2520c3 Formatting fix 2020-05-20 10:37:26 -04:00
Jeremy Stretch
d2e1428c75 Closes #4665: Add NEMA L14 and L21 power port/outlet types 2020-05-20 09:36:55 -04:00
Jeremy Stretch
cd236aa886 Closes #4645: Update minimum required version of PostgreSQL to 9.6 2020-05-15 10:11:36 -04:00
Jeremy Stretch
3c8e7e739d Fixes #4649: Fix interface assignment for bulk-imported IP addresses 2020-05-15 09:44:00 -04:00
Jeremy Stretch
a64351279d Fixes #4648: Fix bulk CSV import of child devices 2020-05-15 09:36:16 -04:00
Jeremy Stretch
ba91b3aa2e Fixes #4646: Correct UI link for reports with custom name 2020-05-15 09:13:51 -04:00
Jeremy Stretch
8394ff5537 Fixes #4644: Fix ordering of services table by parent 2020-05-15 09:02:56 -04:00
John Anderson
14744da8f6 fixes #4647 - caching invalidation related to assinging new IP addresses to interfaces 2020-05-15 02:45:48 -04:00
John Anderson
2c2d6c6d47 fixes #3304 - primary IP address caching invalidation 2020-05-15 02:31:45 -04:00
Jeremy Stretch
422eeddbef Post-release version bump 2020-05-13 17:32:27 -04:00
24 changed files with 221 additions and 83 deletions

View File

@@ -30,10 +30,9 @@ about: Report a reproducible bug in the current release of NetBox
library such as pynetbox.
-->
### Steps to Reproduce
1. Disable any installed plugins by commenting out the `PLUGINS` setting in
`configuration.py`.
2.
3.
1.
2.
3.
<!-- What did you expect to happen? -->
### Expected Behavior

View File

@@ -86,7 +86,12 @@ CORS_ORIGIN_WHITELIST = [
Default: False
This setting enables debugging. This should be done only during development or troubleshooting. Never enable debugging on a production system, as it can expose sensitive data to unauthenticated users.
This setting enables debugging. This should be done only during development or troubleshooting. Note that only clients
which access NetBox from a recognized [internal IP address](#internal_ips) will see debugging tools in the user
interface.
!!! warning
Never enable debugging on a production system, as it can expose sensitive data to unauthenticated users.
---
@@ -184,6 +189,16 @@ HTTP_PROXIES = {
---
## INTERNAL_IPS
Default: `('127.0.0.1', '::1',)`
A list of IP addresses recognized as internal to the system, used to control the display of debugging output. For
example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP
addresses (and [`DEBUG`](#debug) is true).
---
## LOGGING
By default, all messages of INFO severity or higher will be logged to the console. Additionally, if `DEBUG` is False and email access has been configured, ERROR and CRITICAL messages will be emailed to the users defined in `ADMINS`.
@@ -385,7 +400,7 @@ When remote user authentication is in use, this is the name of the HTTP header w
## REMOTE_AUTH_AUTO_CREATE_USER
Default: `True`
Default: `False`
If true, NetBox will automatically create local accounts for users authenticated via a remote service. (Requires `REMOTE_AUTH_ENABLED`.)

View File

@@ -49,7 +49,7 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and
| HTTP service | nginx or Apache |
| WSGI service | gunicorn or uWSGI |
| Application | Django/Python |
| Database | PostgreSQL 9.4+ |
| Database | PostgreSQL 9.6+ |
| Task queuing | Redis/django-rq |
| Live device access | NAPALM |

View File

@@ -3,7 +3,7 @@
This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md).
!!! warning
NetBox requires PostgreSQL 9.4 or higher. Please note that MySQL and other relational databases are **not** supported.
NetBox requires PostgreSQL 9.6 or higher. Please note that MySQL and other relational databases are **not** supported.
The installation instructions provided here have been tested to work on Ubuntu 18.04 and CentOS 7.5. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors.
@@ -51,7 +51,7 @@ At a minimum, we need to create a database for NetBox and assign it a username a
```no-highlight
# sudo -u postgres psql
psql (9.4.5)
psql (10.10)
Type "help" for help.
postgres=# CREATE DATABASE netbox;

View File

@@ -1,6 +1,32 @@
# NetBox v2.8
v2.8.4 (2020-05-13)
## v2.8.5 (2020-05-26)
**Note:** The minimum required version of PostgreSQL is now 9.6.
### Enhancements
* [#4650](https://github.com/netbox-community/netbox/issues/4650) - Expose `INTERNAL_IPS` configuration parameter
* [#4651](https://github.com/netbox-community/netbox/issues/4651) - Add `csrf_token` context for plugin templates
* [#4652](https://github.com/netbox-community/netbox/issues/4652) - Add permissions context for plugin templates
* [#4665](https://github.com/netbox-community/netbox/issues/4665) - Add NEMA L14 and L21 power port/outlet types
* [#4672](https://github.com/netbox-community/netbox/issues/4672) - Set default color for rack and devices roles
### Bug Fixes
* [#3304](https://github.com/netbox-community/netbox/issues/3304) - Fix caching invalidation issue related to device/virtual machine primary IP addresses
* [#4525](https://github.com/netbox-community/netbox/issues/4525) - Allow passing initial data to custom script MultiObjectVar
* [#4644](https://github.com/netbox-community/netbox/issues/4644) - Fix ordering of services table by parent
* [#4646](https://github.com/netbox-community/netbox/issues/4646) - Correct UI link for reports with custom name
* [#4647](https://github.com/netbox-community/netbox/issues/4647) - Fix caching invalidation issue related to assigning new IP addresses to interfaces
* [#4648](https://github.com/netbox-community/netbox/issues/4648) - Fix bulk CSV import of child devices
* [#4649](https://github.com/netbox-community/netbox/issues/4649) - Fix interface assignment for bulk-imported IP addresses
* [#4676](https://github.com/netbox-community/netbox/issues/4676) - Set default value of `REMOTE_AUTH_AUTO_CREATE_USER` as `False` in docs
* [#4684](https://github.com/netbox-community/netbox/issues/4684) - Respect `comments` field when importing device type in YAML/JSON format
---
## v2.8.4 (2020-05-13)
### Enhancements

View File

@@ -276,6 +276,10 @@ class PowerPortTypeChoices(ChoiceSet):
TYPE_NEMA_L620P = 'nema-l6-20p'
TYPE_NEMA_L630P = 'nema-l6-30p'
TYPE_NEMA_L650P = 'nema-l6-50p'
TYPE_NEMA_L1420P = 'nema-l14-20p'
TYPE_NEMA_L1430P = 'nema-l14-30p'
TYPE_NEMA_L2120P = 'nema-l21-20p'
TYPE_NEMA_L2130P = 'nema-l21-30p'
# California style
TYPE_CS6361C = 'cs6361c'
TYPE_CS6365C = 'cs6365c'
@@ -337,6 +341,10 @@ class PowerPortTypeChoices(ChoiceSet):
(TYPE_NEMA_L620P, 'NEMA L6-20P'),
(TYPE_NEMA_L630P, 'NEMA L6-30P'),
(TYPE_NEMA_L650P, 'NEMA L6-50P'),
(TYPE_NEMA_L1420P, 'NEMA L14-20P'),
(TYPE_NEMA_L1430P, 'NEMA L14-30P'),
(TYPE_NEMA_L2120P, 'NEMA L21-20P'),
(TYPE_NEMA_L2130P, 'NEMA L21-30P'),
)),
('California Style', (
(TYPE_CS6361C, 'CS6361C'),
@@ -405,6 +413,10 @@ class PowerOutletTypeChoices(ChoiceSet):
TYPE_NEMA_L620R = 'nema-l6-20r'
TYPE_NEMA_L630R = 'nema-l6-30r'
TYPE_NEMA_L650R = 'nema-l6-50r'
TYPE_NEMA_L1420R = 'nema-l14-20r'
TYPE_NEMA_L1430R = 'nema-l14-30r'
TYPE_NEMA_L2120R = 'nema-l21-20r'
TYPE_NEMA_L2130R = 'nema-l21-30r'
# California style
TYPE_CS6360C = 'CS6360C'
TYPE_CS6364C = 'CS6364C'
@@ -467,6 +479,10 @@ class PowerOutletTypeChoices(ChoiceSet):
(TYPE_NEMA_L620R, 'NEMA L6-20R'),
(TYPE_NEMA_L630R, 'NEMA L6-30R'),
(TYPE_NEMA_L650R, 'NEMA L6-50R'),
(TYPE_NEMA_L1420R, 'NEMA L14-20R'),
(TYPE_NEMA_L1430R, 'NEMA L14-30R'),
(TYPE_NEMA_L2120R, 'NEMA L21-20R'),
(TYPE_NEMA_L2130R, 'NEMA L21-30R'),
)),
('California Style', (
(TYPE_CS6360C, 'CS6360C'),

View File

@@ -4,7 +4,7 @@ from django.contrib.auth.models import User
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet
from tenancy.filters import TenancyFilterSet
from tenancy.models import Tenant
from utilities.constants import COLOR_CHOICES
from utilities.choices import ColorChoices
from utilities.filters import (
BaseFilterSet, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter,
NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter,
@@ -1084,7 +1084,7 @@ class CableFilterSet(BaseFilterSet):
choices=CableStatusChoices
)
color = django_filters.MultipleChoiceFilter(
choices=COLOR_CHOICES
choices=ColorChoices
)
device_id = MultiValueNumberFilter(
method='filter_device'

View File

@@ -932,6 +932,7 @@ class DeviceTypeImportForm(BootstrapMixin, forms.ModelForm):
model = DeviceType
fields = [
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
'comments',
]
@@ -1956,7 +1957,7 @@ class ChildDeviceCSVForm(BaseDeviceCSVForm):
help_text='Parent device'
)
device_bay = CSVModelChoiceField(
queryset=Device.objects.all(),
queryset=DeviceBay.objects.all(),
to_field_name='name',
help_text='Device bay in which this device is installed'
)
@@ -1976,6 +1977,20 @@ class ChildDeviceCSVForm(BaseDeviceCSVForm):
params = {f"device__{self.fields['parent'].to_field_name}": data.get('parent')}
self.fields['device_bay'].queryset = self.fields['device_bay'].queryset.filter(**params)
def clean(self):
super().clean()
# Set parent_bay reverse relationship
device_bay = self.cleaned_data.get('device_bay')
if device_bay:
self.instance.parent_bay = device_bay
# Inherit site and rack from parent device
parent = self.cleaned_data.get('parent')
if parent:
self.instance.site = parent.site
self.instance.rack = parent.rack
class DeviceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(

View File

@@ -0,0 +1,24 @@
# Generated by Django 3.0.6 on 2020-05-26 13:33
from django.db import migrations
import utilities.fields
class Migration(migrations.Migration):
dependencies = [
('dcim', '0105_interface_name_collation'),
]
operations = [
migrations.AlterField(
model_name='devicerole',
name='color',
field=utilities.fields.ColorField(default='9e9e9e', max_length=6),
),
migrations.AlterField(
model_name='rackrole',
name='color',
field=utilities.fields.ColorField(default='9e9e9e', max_length=6),
),
]

View File

@@ -23,6 +23,7 @@ from dcim.fields import ASNField
from dcim.elevations import RackElevationSVG
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.choices import ColorChoices
from utilities.fields import ColorField, NaturalOrderingField
from utilities.models import ChangeLoggedModel
from utilities.utils import serialize_object, to_meters
@@ -379,7 +380,9 @@ class RackRole(ChangeLoggedModel):
slug = models.SlugField(
unique=True
)
color = ColorField()
color = ColorField(
default=ColorChoices.COLOR_GREY
)
description = models.CharField(
max_length=200,
blank=True,
@@ -1190,7 +1193,9 @@ class DeviceRole(ChangeLoggedModel):
slug = models.SlugField(
unique=True
)
color = ColorField()
color = ColorField(
default=ColorChoices.COLOR_GREY
)
vm_role = models.BooleanField(
default=True,
verbose_name='VM Role',

View File

@@ -366,6 +366,7 @@ manufacturer: Generic
model: TEST-1000
slug: test-1000
u_height: 2
comments: test comment
console-ports:
- name: Console Port 1
type: de-9
@@ -456,6 +457,7 @@ device-bays:
self.assertHttpStatus(response, 200)
dt = DeviceType.objects.get(model='TEST-1000')
self.assertEqual(dt.comments, 'test comment')
# Verify all of the components were created
self.assertEqual(dt.consoleport_templates.count(), 3)

View File

@@ -1105,7 +1105,7 @@ class DeviceView(PermissionRequiredMixin, View):
def get(self, request, pk):
device = get_object_or_404(Device.objects.prefetch_related(
'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform'
'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform', 'primary_ip4', 'primary_ip6'
), pk=pk)
# VirtualChassis members

View File

@@ -432,11 +432,11 @@ class ScriptForm(BootstrapMixin, forms.Form):
def __init__(self, vars, *args, commit_default=True, **kwargs):
super().__init__(*args, **kwargs)
# Dynamically populate fields for variables
for name, var in vars.items():
self.fields[name] = var.as_field()
self.base_fields[name] = var.as_field()
super().__init__(*args, **kwargs)
# Toggle default commit behavior based on Meta option
if not commit_default:

View File

@@ -3,6 +3,7 @@ from django.urls import reverse
from django.utils.text import slugify
from taggit.models import TagBase, GenericTaggedItemBase
from utilities.choices import ColorChoices
from utilities.fields import ColorField
from utilities.models import ChangeLoggedModel
@@ -13,7 +14,7 @@ from utilities.models import ChangeLoggedModel
class Tag(TagBase, ChangeLoggedModel):
color = ColorField(
default='9e9e9e'
default=ColorChoices.COLOR_GREY
)
description = models.CharField(
max_length=200,

View File

@@ -92,7 +92,7 @@ class Report(object):
self.active_test = None
self.failed = False
self.logger = logging.getLogger(f"netbox.reports.{self.module}.{self.name}")
self.logger = logging.getLogger(f"netbox.reports.{self.full_name}")
# Compile test methods and initialize results skeleton
test_methods = []
@@ -120,7 +120,7 @@ class Report(object):
@property
def full_name(self):
return '.'.join([self.module, self.name])
return '.'.join([self.__module__, self.__class__.__name__])
def _log(self, obj, message, level=LOG_DEFAULT):
"""

View File

@@ -18,6 +18,8 @@ def _get_registered_content(obj, method, template_context):
'object': obj,
'request': template_context['request'],
'settings': template_context['settings'],
'csrf_token': template_context['csrf_token'],
'perms': template_context['perms'],
}
model_name = obj._meta.label_lower

View File

@@ -618,7 +618,12 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
if self.instance and self.instance.interface:
self.fields['interface'].queryset = Interface.objects.filter(
device=self.instance.interface.device, virtual_machine=self.instance.interface.virtual_machine
)
).prefetch_related(
'device__primary_ip4',
'device__primary_ip6',
'virtual_machine__primary_ip4',
'virtual_machine__primary_ip6',
) # We prefetch the primary address fields to ensure cache invalidation does not balk on the save()
else:
self.fields['interface'].choices = []
@@ -775,18 +780,6 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
def save(self, *args, **kwargs):
# Set interface
if self.cleaned_data['device'] and self.cleaned_data['interface_name']:
self.instance.interface = Interface.objects.get(
device=self.cleaned_data['device'],
name=self.cleaned_data['interface_name']
)
elif self.cleaned_data['virtual_machine'] and self.cleaned_data['interface_name']:
self.instance.interface = Interface.objects.get(
virtual_machine=self.cleaned_data['virtual_machine'],
name=self.cleaned_data['interface_name']
)
ipaddress = super().save(*args, **kwargs)
# Set as primary for device/VM

View File

@@ -667,6 +667,9 @@ class ServiceTable(BaseTable):
viewname='ipam:service',
args=[Accessor('pk')]
)
parent = tables.LinkColumn(
order_by=('device', 'virtual_machine')
)
tags = TagColumn(
url_name='ipam:service_list'
)

View File

@@ -132,6 +132,10 @@ EXEMPT_VIEW_PERMISSIONS = [
# 'https': 'http://10.10.1.10:1080',
# }
# IP addresses recognized as internal to the system. The debugging toolbar will be available only to clients accessing
# NetBox from an internal IP.
INTERNAL_IPS = ('127.0.0.1', '::1')
# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs:
# https://docs.djangoproject.com/en/stable/topics/logging/
LOGGING = {}

View File

@@ -16,7 +16,7 @@ from django.core.validators import URLValidator
# Environment setup
#
VERSION = '2.8.4'
VERSION = '2.8.5'
# Hostname
HOSTNAME = platform.node()
@@ -78,6 +78,7 @@ EMAIL = getattr(configuration, 'EMAIL', {})
ENFORCE_GLOBAL_UNIQUE = getattr(configuration, 'ENFORCE_GLOBAL_UNIQUE', False)
EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', [])
HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', None)
INTERNAL_IPS = getattr(configuration, 'INTERNAL_IPS', ('127.0.0.1', '::1'))
LOGGING = getattr(configuration, 'LOGGING', {})
LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False)
LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None)
@@ -615,15 +616,6 @@ RQ_QUEUES = {
'check_releases': RQ_PARAMS,
}
#
# Django debug toolbar
#
INTERNAL_IPS = (
'127.0.0.1',
'::1',
)
#
# NetBox internal settings

View File

@@ -10,7 +10,7 @@
<code>python3 manage.py migrate</code> from the command line.
</p>
<p>
<i class="fa fa-warning"></i> <strong>Unsupported PostgreSQL version</strong> - Ensure that PostgreSQL version 9.4 or higher is in use. You
<i class="fa fa-warning"></i> <strong>Unsupported PostgreSQL version</strong> - Ensure that PostgreSQL version 9.6 or higher is in use. You
can check this by connecting to the database using NetBox's credentials and issuing a query for
<code>SELECT VERSION()</code>.
</p>

View File

@@ -80,6 +80,70 @@ def unpack_grouped_choices(choices):
return unpacked_choices
#
# Generic color choices
#
class ColorChoices(ChoiceSet):
COLOR_DARK_RED = 'aa1409'
COLOR_RED = 'f44336'
COLOR_PINK = 'e91e63'
COLOR_ROSE = 'ffe4e1'
COLOR_FUCHSIA = 'ff66ff'
COLOR_PURPLE = '9c27b0'
COLOR_DARK_PURPLE = '673ab7'
COLOR_INDIGO = '3f51b5'
COLOR_BLUE = '2196f3'
COLOR_LIGHT_BLUE = '03a9f4'
COLOR_CYAN = '00bcd4'
COLOR_TEAL = '009688'
COLOR_AQUA = '00ffff'
COLOR_DARK_GREEN = '2f6a31'
COLOR_GREEN = '4caf50'
COLOR_LIGHT_GREEN = '8bc34a'
COLOR_LIME = 'cddc39'
COLOR_YELLOW = 'ffeb3b'
COLOR_AMBER = 'ffc107'
COLOR_ORANGE = 'ff9800'
COLOR_DARK_ORANGE = 'ff5722'
COLOR_BROWN = '795548'
COLOR_LIGHT_GREY = 'c0c0c0'
COLOR_GREY = '9e9e9e'
COLOR_DARK_GREY = '607d8b'
COLOR_BLACK = '111111'
COLOR_WHITE = 'ffffff'
CHOICES = (
(COLOR_DARK_RED, 'Dark red'),
(COLOR_RED, 'Red'),
(COLOR_PINK, 'Pink'),
(COLOR_ROSE, 'Rose'),
(COLOR_FUCHSIA, 'Fuchsia'),
(COLOR_PURPLE, 'Purple'),
(COLOR_DARK_PURPLE, 'Dark purple'),
(COLOR_INDIGO, 'Indigo'),
(COLOR_BLUE, 'Blue'),
(COLOR_LIGHT_BLUE, 'Light blue'),
(COLOR_CYAN, 'Cyan'),
(COLOR_TEAL, 'Teal'),
(COLOR_AQUA, 'Aqua'),
(COLOR_DARK_GREEN, 'Dark green'),
(COLOR_GREEN, 'Green'),
(COLOR_LIGHT_GREEN, 'Light green'),
(COLOR_LIME, 'Lime'),
(COLOR_YELLOW, 'Yellow'),
(COLOR_AMBER, 'Amber'),
(COLOR_ORANGE, 'Orange'),
(COLOR_DARK_ORANGE, 'Dark orange'),
(COLOR_BROWN, 'Brown'),
(COLOR_LIGHT_GREY, 'Light grey'),
(COLOR_GREY, 'Grey'),
(COLOR_DARK_GREY, 'Dark grey'),
(COLOR_BLACK, 'Black'),
(COLOR_WHITE, 'White'),
)
#
# Button color choices
#

View File

@@ -1,34 +1,3 @@
COLOR_CHOICES = (
('aa1409', 'Dark red'),
('f44336', 'Red'),
('e91e63', 'Pink'),
('ffe4e1', 'Rose'),
('ff66ff', 'Fuschia'),
('9c27b0', 'Purple'),
('673ab7', 'Dark purple'),
('3f51b5', 'Indigo'),
('2196f3', 'Blue'),
('03a9f4', 'Light blue'),
('00bcd4', 'Cyan'),
('009688', 'Teal'),
('00ffff', 'Aqua'),
('2f6a31', 'Dark green'),
('4caf50', 'Green'),
('8bc34a', 'Light green'),
('cddc39', 'Lime'),
('ffeb3b', 'Yellow'),
('ffc107', 'Amber'),
('ff9800', 'Orange'),
('ff5722', 'Dark orange'),
('795548', 'Brown'),
('c0c0c0', 'Light grey'),
('9e9e9e', 'Grey'),
('607d8b', 'Dark grey'),
('111111', 'Black'),
('ffffff', 'White'),
)
#
# Filter lookup expressions
#

View File

@@ -14,8 +14,7 @@ from django.forms import BoundField
from django.forms.models import fields_for_model
from django.urls import reverse
from .choices import unpack_grouped_choices
from .constants import *
from .choices import ColorChoices, unpack_grouped_choices
from .validators import EnhancedURLValidator
NUMERIC_EXPANSION_PATTERN = r'\[((?:\d+[?:,-])+\d+)\]'
@@ -163,7 +162,7 @@ class ColorSelect(forms.Select):
option_template_name = 'widgets/colorselect_option.html'
def __init__(self, *args, **kwargs):
kwargs['choices'] = add_blank_choice(COLOR_CHOICES)
kwargs['choices'] = add_blank_choice(ColorChoices)
super().__init__(*args, **kwargs)
self.attrs['class'] = 'netbox-select2-color-picker'
@@ -607,15 +606,18 @@ class DynamicModelChoiceMixin:
filter = django_filters.ModelChoiceFilter
widget = APISelect
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def _get_initial_value(self, initial_data, field_name):
return initial_data.get(field_name)
def get_bound_field(self, form, field_name):
bound_field = BoundField(form, self, field_name)
# Override initial() to allow passing multiple values
bound_field.initial = self._get_initial_value(form.initial, field_name)
# Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options
# will be populated on-demand via the APISelect widget.
data = self.prepare_value(bound_field.data or bound_field.initial)
data = bound_field.value()
if data:
filter = self.filter(field_name=self.to_field_name or 'pk', queryset=self.queryset)
self.queryset = filter.filter(self.queryset, data)
@@ -648,6 +650,12 @@ class DynamicModelMultipleChoiceField(DynamicModelChoiceMixin, forms.ModelMultip
filter = django_filters.ModelMultipleChoiceFilter
widget = APISelectMultiple
def _get_initial_value(self, initial_data, field_name):
# If a QueryDict has been passed as initial form data, get *all* listed values
if hasattr(initial_data, 'getlist'):
return initial_data.getlist(field_name)
return initial_data.get(field_name)
class LaxURLField(forms.URLField):
"""