mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-26 17:26:10 -06:00
Add datetime custom field type
This commit is contained in:
parent
7994073687
commit
16daeabccf
@ -16,6 +16,7 @@ Custom fields may be created by navigating to Customization > Custom Fields. Net
|
|||||||
* Decimal: A fixed-precision decimal number (4 decimal places)
|
* Decimal: A fixed-precision decimal number (4 decimal places)
|
||||||
* Boolean: True or false
|
* Boolean: True or false
|
||||||
* Date: A date in ISO 8601 format (YYYY-MM-DD)
|
* Date: A date in ISO 8601 format (YYYY-MM-DD)
|
||||||
|
* Date & time: A date and time in ISO 8601 format (YYYY-MM-DD HH:MM:SS)
|
||||||
* URL: This will be presented as a link in the web UI
|
* URL: This will be presented as a link in the web UI
|
||||||
* JSON: Arbitrary data stored in JSON format
|
* JSON: Arbitrary data stored in JSON format
|
||||||
* Selection: A selection of one of several pre-defined custom choices
|
* Selection: A selection of one of several pre-defined custom choices
|
||||||
|
@ -13,6 +13,7 @@ class CustomFieldTypeChoices(ChoiceSet):
|
|||||||
TYPE_DECIMAL = 'decimal'
|
TYPE_DECIMAL = 'decimal'
|
||||||
TYPE_BOOLEAN = 'boolean'
|
TYPE_BOOLEAN = 'boolean'
|
||||||
TYPE_DATE = 'date'
|
TYPE_DATE = 'date'
|
||||||
|
TYPE_DATETIME = 'datetime'
|
||||||
TYPE_URL = 'url'
|
TYPE_URL = 'url'
|
||||||
TYPE_JSON = 'json'
|
TYPE_JSON = 'json'
|
||||||
TYPE_SELECT = 'select'
|
TYPE_SELECT = 'select'
|
||||||
@ -27,6 +28,7 @@ class CustomFieldTypeChoices(ChoiceSet):
|
|||||||
(TYPE_DECIMAL, 'Decimal'),
|
(TYPE_DECIMAL, 'Decimal'),
|
||||||
(TYPE_BOOLEAN, 'Boolean (true/false)'),
|
(TYPE_BOOLEAN, 'Boolean (true/false)'),
|
||||||
(TYPE_DATE, 'Date'),
|
(TYPE_DATE, 'Date'),
|
||||||
|
(TYPE_DATETIME, 'Date & time'),
|
||||||
(TYPE_URL, 'URL'),
|
(TYPE_URL, 'URL'),
|
||||||
(TYPE_JSON, 'JSON'),
|
(TYPE_JSON, 'JSON'),
|
||||||
(TYPE_SELECT, 'Selection'),
|
(TYPE_SELECT, 'Selection'),
|
||||||
|
@ -25,7 +25,7 @@ from utilities.forms.fields import (
|
|||||||
DynamicModelMultipleChoiceField, JSONField, LaxURLField,
|
DynamicModelMultipleChoiceField, JSONField, LaxURLField,
|
||||||
)
|
)
|
||||||
from utilities.forms.utils import add_blank_choice
|
from utilities.forms.utils import add_blank_choice
|
||||||
from utilities.forms.widgets import DatePicker
|
from utilities.forms.widgets import DatePicker, DateTimePicker
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
from utilities.validators import validate_regex
|
from utilities.validators import validate_regex
|
||||||
|
|
||||||
@ -306,8 +306,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
"""
|
"""
|
||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
if self.type == CustomFieldTypeChoices.TYPE_DATE and type(value) is date:
|
if self.type in (CustomFieldTypeChoices.TYPE_DATE, CustomFieldTypeChoices.TYPE_DATETIME):
|
||||||
return value.isoformat()
|
if type(value) in (date, datetime):
|
||||||
|
return value.isoformat()
|
||||||
if self.type == CustomFieldTypeChoices.TYPE_OBJECT:
|
if self.type == CustomFieldTypeChoices.TYPE_OBJECT:
|
||||||
return value.pk
|
return value.pk
|
||||||
if self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
|
if self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
|
||||||
@ -325,6 +326,11 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
return date.fromisoformat(value)
|
return date.fromisoformat(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return value
|
return value
|
||||||
|
if self.type == CustomFieldTypeChoices.TYPE_DATETIME:
|
||||||
|
try:
|
||||||
|
return datetime.fromisoformat(value)
|
||||||
|
except ValueError:
|
||||||
|
return value
|
||||||
if self.type == CustomFieldTypeChoices.TYPE_OBJECT:
|
if self.type == CustomFieldTypeChoices.TYPE_OBJECT:
|
||||||
model = self.object_type.model_class()
|
model = self.object_type.model_class()
|
||||||
return model.objects.filter(pk=value).first()
|
return model.objects.filter(pk=value).first()
|
||||||
@ -380,6 +386,10 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
elif self.type == CustomFieldTypeChoices.TYPE_DATE:
|
elif self.type == CustomFieldTypeChoices.TYPE_DATE:
|
||||||
field = forms.DateField(required=required, initial=initial, widget=DatePicker())
|
field = forms.DateField(required=required, initial=initial, widget=DatePicker())
|
||||||
|
|
||||||
|
# Date & time
|
||||||
|
elif self.type == CustomFieldTypeChoices.TYPE_DATETIME:
|
||||||
|
field = forms.DateTimeField(required=required, initial=initial, widget=DateTimePicker())
|
||||||
|
|
||||||
# Select
|
# Select
|
||||||
elif self.type in (CustomFieldTypeChoices.TYPE_SELECT, CustomFieldTypeChoices.TYPE_MULTISELECT):
|
elif self.type in (CustomFieldTypeChoices.TYPE_SELECT, CustomFieldTypeChoices.TYPE_MULTISELECT):
|
||||||
choices = [(c, c) for c in self.choices]
|
choices = [(c, c) for c in self.choices]
|
||||||
@ -490,6 +500,10 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
elif self.type == CustomFieldTypeChoices.TYPE_DATE:
|
elif self.type == CustomFieldTypeChoices.TYPE_DATE:
|
||||||
filter_class = filters.MultiValueDateFilter
|
filter_class = filters.MultiValueDateFilter
|
||||||
|
|
||||||
|
# Date & time
|
||||||
|
elif self.type == CustomFieldTypeChoices.TYPE_DATETIME:
|
||||||
|
filter_class = filters.MultiValueDateTimeFilter
|
||||||
|
|
||||||
# Select
|
# Select
|
||||||
elif self.type == CustomFieldTypeChoices.TYPE_SELECT:
|
elif self.type == CustomFieldTypeChoices.TYPE_SELECT:
|
||||||
filter_class = filters.MultiValueCharFilter
|
filter_class = filters.MultiValueCharFilter
|
||||||
@ -558,9 +572,17 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
|
|||||||
elif self.type == CustomFieldTypeChoices.TYPE_DATE:
|
elif self.type == CustomFieldTypeChoices.TYPE_DATE:
|
||||||
if type(value) is not date:
|
if type(value) is not date:
|
||||||
try:
|
try:
|
||||||
datetime.strptime(value, '%Y-%m-%d')
|
date.fromisoformat(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValidationError("Date values must be in the format YYYY-MM-DD.")
|
raise ValidationError("Date values must be in ISO 8601 format (YYYY-MM-DD).")
|
||||||
|
|
||||||
|
# Validate date & time
|
||||||
|
elif self.type == CustomFieldTypeChoices.TYPE_DATETIME:
|
||||||
|
if type(value) is not datetime:
|
||||||
|
try:
|
||||||
|
datetime.fromisoformat(value)
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError("Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS).")
|
||||||
|
|
||||||
# Validate selected choice
|
# Validate selected choice
|
||||||
elif self.type == CustomFieldTypeChoices.TYPE_SELECT:
|
elif self.type == CustomFieldTypeChoices.TYPE_SELECT:
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
{% checkmark value false="False" %}
|
{% checkmark value false="False" %}
|
||||||
{% elif customfield.type == 'date' and value %}
|
{% elif customfield.type == 'date' and value %}
|
||||||
{{ value|annotated_date }}
|
{{ value|annotated_date }}
|
||||||
|
{% elif customfield.type == 'datetime' and value %}
|
||||||
|
{{ value|annotated_date }}
|
||||||
{% elif customfield.type == 'url' and value %}
|
{% elif customfield.type == 'url' and value %}
|
||||||
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
||||||
{% elif customfield.type == 'json' and value %}
|
{% elif customfield.type == 'json' and value %}
|
||||||
|
Loading…
Reference in New Issue
Block a user