mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
* Initial work on custom field choice sets * Rename choices to extra_choices (prep for #12194) * Remove CustomField.choices * Add & update tests * Clean up table columns * Add order_alphanetically boolean for choice sets * Introduce ArrayColumn for choice lists * Show dependent custom fields on choice set view * Update custom fields documentation * Introduce ArrayWidget for more convenient editing of choices * Incorporate PR feedback * Misc cleanup * Initial work on predefined choices for custom fields * Misc cleanup * Add IATA airport codes * #13241: Add support for custom field choice labels * Restore ArrayColumn * Misc cleanup * Change extra_choices back to a nested ArrayField to preserve choice ordering * Hack to bypass GraphQL API test utility absent support for nested ArrayFields
This commit is contained in:
@@ -8,11 +8,49 @@ from utilities.forms import widgets
|
||||
from utilities.utils import get_viewname
|
||||
|
||||
__all__ = (
|
||||
'DynamicChoiceField',
|
||||
'DynamicModelChoiceField',
|
||||
'DynamicModelMultipleChoiceField',
|
||||
'DynamicMultipleChoiceField',
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Choice fields
|
||||
#
|
||||
|
||||
class DynamicChoiceField(forms.ChoiceField):
|
||||
|
||||
def get_bound_field(self, form, field_name):
|
||||
bound_field = BoundField(form, self, field_name)
|
||||
data = bound_field.value()
|
||||
|
||||
if data is not None:
|
||||
self.choices = [
|
||||
choice for choice in self.choices if choice[0] == data
|
||||
]
|
||||
|
||||
return bound_field
|
||||
|
||||
|
||||
class DynamicMultipleChoiceField(forms.MultipleChoiceField):
|
||||
|
||||
def get_bound_field(self, form, field_name):
|
||||
bound_field = BoundField(form, self, field_name)
|
||||
data = bound_field.value()
|
||||
|
||||
if data is not None:
|
||||
self.choices = [
|
||||
choice for choice in self.choices if choice[0] in data
|
||||
]
|
||||
|
||||
return bound_field
|
||||
|
||||
|
||||
#
|
||||
# Model choice fields
|
||||
#
|
||||
|
||||
class DynamicModelChoiceMixin:
|
||||
"""
|
||||
Override `get_bound_field()` to avoid pre-populating field choices with a SQL query. The field will be
|
||||
|
@@ -2,6 +2,7 @@ from django import forms
|
||||
|
||||
__all__ = (
|
||||
'ArrayWidget',
|
||||
'ChoicesWidget',
|
||||
'ClearableFileInput',
|
||||
'MarkdownWidget',
|
||||
'NumberWithOptions',
|
||||
@@ -54,3 +55,15 @@ class ArrayWidget(forms.Textarea):
|
||||
if value is None or not len(value):
|
||||
return None
|
||||
return '\n'.join(value)
|
||||
|
||||
|
||||
class ChoicesWidget(forms.Textarea):
|
||||
"""
|
||||
Render each key-value pair of a dictionary on a new line within a textarea for easy editing.
|
||||
"""
|
||||
def format_value(self, value):
|
||||
if not value:
|
||||
return None
|
||||
if type(value) is list:
|
||||
return '\n'.join([f'{k},{v}' for k, v in value])
|
||||
return value
|
||||
|
@@ -462,6 +462,9 @@ class APIViewTestCases:
|
||||
if type(field) is GQLDynamic:
|
||||
# Dynamic fields must specify a subselection
|
||||
fields_string += f'{field_name} {{ id }}\n'
|
||||
# TODO: Improve field detection logic to avoid nested ArrayFields
|
||||
elif field_name == 'extra_choices':
|
||||
continue
|
||||
elif inspect.isclass(field.type) and issubclass(field.type, GQLUnion):
|
||||
# Union types dont' have an id or consistent values
|
||||
continue
|
||||
|
@@ -129,13 +129,18 @@ class ModelTestCase(TestCase):
|
||||
model_dict[key] = str(value)
|
||||
|
||||
else:
|
||||
field = instance._meta.get_field(key)
|
||||
|
||||
# Convert ArrayFields to CSV strings
|
||||
if type(instance._meta.get_field(key)) is ArrayField:
|
||||
model_dict[key] = ','.join([str(v) for v in value])
|
||||
if type(field) is ArrayField:
|
||||
if type(field.base_field) is ArrayField:
|
||||
# Handle nested arrays (e.g. choice sets)
|
||||
model_dict[key] = '\n'.join([f'{k},{v}' for k, v in value])
|
||||
else:
|
||||
model_dict[key] = ','.join([str(v) for v in value])
|
||||
|
||||
# JSON
|
||||
if type(instance._meta.get_field(key)) is JSONField and value is not None:
|
||||
if type(field) is JSONField and value is not None:
|
||||
model_dict[key] = json.dumps(value)
|
||||
|
||||
return model_dict
|
||||
|
Reference in New Issue
Block a user