mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Merge branch 'develop' into feature
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import re
|
||||
|
||||
from django import forms
|
||||
from django.contrib.postgres.forms import SimpleArrayField
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
@@ -82,7 +84,10 @@ class CustomFieldChoiceSetImportForm(CSVModelForm):
|
||||
extra_choices = SimpleArrayField(
|
||||
base_field=forms.CharField(),
|
||||
required=False,
|
||||
help_text=_('Comma-separated list of field choices')
|
||||
help_text=_(
|
||||
'Quoted string of comma-separated field choices with optional labels separated by colon: '
|
||||
'"choice1:First Choice,choice2:Second Choice"'
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -91,6 +96,19 @@ class CustomFieldChoiceSetImportForm(CSVModelForm):
|
||||
'name', 'description', 'extra_choices', 'order_alphabetically',
|
||||
)
|
||||
|
||||
def clean_extra_choices(self):
|
||||
if isinstance(self.cleaned_data['extra_choices'], list):
|
||||
data = []
|
||||
for line in self.cleaned_data['extra_choices']:
|
||||
try:
|
||||
value, label = re.split(r'(?<!\\):', line, maxsplit=1)
|
||||
value = value.replace('\\:', ':')
|
||||
label = label.replace('\\:', ':')
|
||||
except ValueError:
|
||||
value, label = line, line
|
||||
data.append((value, label))
|
||||
return data
|
||||
|
||||
|
||||
class CustomLinkImportForm(CSVModelForm):
|
||||
content_types = CSVMultipleContentTypeField(
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from django import forms
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
@@ -88,19 +89,33 @@ class CustomFieldChoiceSetForm(BootstrapMixin, forms.ModelForm):
|
||||
required=False,
|
||||
help_text=mark_safe(_(
|
||||
'Enter one choice per line. An optional label may be specified for each choice by appending it with a '
|
||||
'comma. Example:'
|
||||
) + ' <code>choice1,First Choice</code>')
|
||||
'colon. Example:'
|
||||
) + ' <code>choice1:First Choice</code>')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CustomFieldChoiceSet
|
||||
fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically')
|
||||
|
||||
def __init__(self, *args, initial=None, **kwargs):
|
||||
super().__init__(*args, initial=initial, **kwargs)
|
||||
|
||||
# Escape colons in extra_choices
|
||||
if 'extra_choices' in self.initial and self.initial['extra_choices']:
|
||||
choices = []
|
||||
for choice in self.initial['extra_choices']:
|
||||
choice = (choice[0].replace(':', '\\:'), choice[1].replace(':', '\\:'))
|
||||
choices.append(choice)
|
||||
|
||||
self.initial['extra_choices'] = choices
|
||||
|
||||
def clean_extra_choices(self):
|
||||
data = []
|
||||
for line in self.cleaned_data['extra_choices'].splitlines():
|
||||
try:
|
||||
value, label = line.split(',', maxsplit=1)
|
||||
value, label = re.split(r'(?<!\\):', line, maxsplit=1)
|
||||
value = value.replace('\\:', ':')
|
||||
label = label.replace('\\:', ':')
|
||||
except ValueError:
|
||||
value, label = line, line
|
||||
data.append((value, label))
|
||||
|
@@ -55,6 +55,15 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
||||
content_type = ContentType.objects.get_for_model(model._meta.concrete_model)
|
||||
return self.get_queryset().filter(content_types=content_type)
|
||||
|
||||
def get_defaults_for_model(self, model):
|
||||
"""
|
||||
Return a dictionary of serialized default values for all CustomFields applicable to the given model.
|
||||
"""
|
||||
custom_fields = self.get_for_model(model).filter(default__isnull=False)
|
||||
return {
|
||||
cf.name: cf.default for cf in custom_fields
|
||||
}
|
||||
|
||||
|
||||
class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
||||
content_types = models.ManyToManyField(
|
||||
|
@@ -92,19 +92,24 @@ class CustomFieldChoiceSetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
name='Choice Set 3',
|
||||
extra_choices=(('C1', 'Choice 1'), ('C2', 'Choice 2'), ('C3', 'Choice 3'))
|
||||
),
|
||||
CustomFieldChoiceSet(
|
||||
name='Choice Set 4',
|
||||
extra_choices=(('D1', 'Choice 1'), ('D2', 'Choice 2'), ('D3', 'Choice 3'))
|
||||
),
|
||||
)
|
||||
CustomFieldChoiceSet.objects.bulk_create(choice_sets)
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'Choice Set X',
|
||||
'extra_choices': '\n'.join(['X1,Choice 1', 'X2,Choice 2', 'X3,Choice 3'])
|
||||
'extra_choices': '\n'.join(['X1:Choice 1', 'X2:Choice 2', 'X3:Choice 3'])
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
'name,extra_choices',
|
||||
'Choice Set 4,"D1,D2,D3"',
|
||||
'Choice Set 5,"E1,E2,E3"',
|
||||
'Choice Set 6,"F1,F2,F3"',
|
||||
'Choice Set 5,"D1,D2,D3"',
|
||||
'Choice Set 6,"E1,E2,E3"',
|
||||
'Choice Set 7,"F1,F2,F3"',
|
||||
'Choice Set 8,"F1:L1,F2:L2,F3:L3"',
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
@@ -112,12 +117,20 @@ class CustomFieldChoiceSetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
f'{choice_sets[0].pk},"A,B,C"',
|
||||
f'{choice_sets[1].pk},"A,B,C"',
|
||||
f'{choice_sets[2].pk},"A,B,C"',
|
||||
f'{choice_sets[3].pk},"A:L1,B:L2,C:L3"',
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
# This is here as extra_choices field splits on colon, but is returned
|
||||
# from DB as comma separated.
|
||||
def assertInstanceEqual(self, instance, data, exclude=None, api=False):
|
||||
if 'extra_choices' in data:
|
||||
data['extra_choices'] = data['extra_choices'].replace(':', ',')
|
||||
return super().assertInstanceEqual(instance, data, exclude, api)
|
||||
|
||||
|
||||
class CustomLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = CustomLink
|
||||
|
Reference in New Issue
Block a user