From b271fd32bdadd6f9cbd5e2261554f70837cd06ac Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 7 Feb 2020 11:36:22 -0500 Subject: [PATCH] Introduce NaturalOrderingField --- netbox/utilities/fields.py | 33 +++++++++++++++++++++++++++++++++ netbox/utilities/ordering.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 netbox/utilities/ordering.py diff --git a/netbox/utilities/fields.py b/netbox/utilities/fields.py index d2165aea6..6181a7ca1 100644 --- a/netbox/utilities/fields.py +++ b/netbox/utilities/fields.py @@ -1,6 +1,7 @@ from django.core.validators import RegexValidator from django.db import models +from utilities.ordering import naturalize from .forms import ColorSelect ColorValidator = RegexValidator( @@ -35,3 +36,35 @@ class ColorField(models.CharField): def formfield(self, **kwargs): kwargs['widget'] = ColorSelect return super().formfield(**kwargs) + + +class NaturalOrderingField(models.CharField): + """ + A field which stores a naturalized representation of its target field, to be used for ordering its parent model. + + :param target_field: Name of the field of the parent model to be naturalized + :param naturalize_function: The function used to generate a naturalized value (optional) + """ + description = "Stores a representation of its target field suitable for natural ordering" + + def __init__(self, target_field, naturalize_function=naturalize, *args, **kwargs): + self.target_field = target_field + self.naturalize_function = naturalize_function + super().__init__(*args, **kwargs) + + def pre_save(self, model_instance, add): + """ + Generate a naturalized value from the target field + """ + value = getattr(model_instance, self.target_field) + return self.naturalize_function(value, max_length=self.max_length) + + def deconstruct(self): + kwargs = super().deconstruct()[3] # Pass kwargs from CharField + kwargs['naturalize_function'] = self.naturalize_function + return ( + self.name, + 'utilities.fields.NaturalOrderingField', + ['target_field'], + kwargs, + ) diff --git a/netbox/utilities/ordering.py b/netbox/utilities/ordering.py new file mode 100644 index 000000000..2d6c06f63 --- /dev/null +++ b/netbox/utilities/ordering.py @@ -0,0 +1,31 @@ +import re + + +def naturalize(value, max_length=None, integer_places=8): + """ + Take an alphanumeric string and prepend all integers to `integer_places` places to ensure the strings + are ordered naturally. For example: + + site9router21 + site10router4 + site10router19 + + becomes: + + site00000009router00000021 + site00000010router00000004 + site00000010router00000019 + + :param value: The value to be naturalized + :param max_length: The maximum length of the returned string. Characters beyond this length will be stripped. + :param integer_places: The number of places to which each integer will be expanded. (Default: 8) + """ + output = [] + for segment in re.split(r'(\d+)', value): + if segment.isdigit(): + output.append(segment.rjust(integer_places, '0')) + elif segment: + output.append(segment) + ret = ''.join(output) + + return ret[:max_length] if max_length else ret