mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
* Add datetime custom field type * Update custom field tests
This commit is contained in:
parent
7994073687
commit
5517963b24
@ -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
|
||||||
|
@ -28,6 +28,7 @@ A new ASN range model has been introduced to facilitate the provisioning of new
|
|||||||
|
|
||||||
* [#9073](https://github.com/netbox-community/netbox/issues/9073) - Enable syncing config context data from remote sources
|
* [#9073](https://github.com/netbox-community/netbox/issues/9073) - Enable syncing config context data from remote sources
|
||||||
* [#9653](https://github.com/netbox-community/netbox/issues/9653) - Enable setting a default platform for device types
|
* [#9653](https://github.com/netbox-community/netbox/issues/9653) - Enable setting a default platform for device types
|
||||||
|
* [#10729](https://github.com/netbox-community/netbox/issues/10729) - Add date & time custom field type
|
||||||
* [#11254](https://github.com/netbox-community/netbox/issues/11254) - Introduce the `X-Request-ID` HTTP header to annotate the unique ID of each request for change logging
|
* [#11254](https://github.com/netbox-community/netbox/issues/11254) - Introduce the `X-Request-ID` HTTP header to annotate the unique ID of each request for change logging
|
||||||
* [#11440](https://github.com/netbox-community/netbox/issues/11440) - Add an `enabled` field for device type interfaces
|
* [#11440](https://github.com/netbox-community/netbox/issues/11440) - Add an `enabled` field for device type interfaces
|
||||||
* [#11517](https://github.com/netbox-community/netbox/issues/11517) - Standardize the inclusion of related objects across the entire UI
|
* [#11517](https://github.com/netbox-community/netbox/issues/11517) - Standardize the inclusion of related objects across the entire UI
|
||||||
|
@ -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:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
@ -157,12 +158,12 @@ class CustomFieldTest(TestCase):
|
|||||||
self.assertIsNone(instance.custom_field_data.get(cf.name))
|
self.assertIsNone(instance.custom_field_data.get(cf.name))
|
||||||
|
|
||||||
def test_date_field(self):
|
def test_date_field(self):
|
||||||
value = '2016-06-23'
|
value = datetime.date(2016, 6, 23)
|
||||||
|
|
||||||
# Create a custom field & check that initial value is null
|
# Create a custom field & check that initial value is null
|
||||||
cf = CustomField.objects.create(
|
cf = CustomField.objects.create(
|
||||||
name='date_field',
|
name='date_field',
|
||||||
type=CustomFieldTypeChoices.TYPE_TEXT,
|
type=CustomFieldTypeChoices.TYPE_DATE,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
cf.content_types.set([self.object_type])
|
cf.content_types.set([self.object_type])
|
||||||
@ -170,10 +171,35 @@ class CustomFieldTest(TestCase):
|
|||||||
self.assertIsNone(instance.custom_field_data[cf.name])
|
self.assertIsNone(instance.custom_field_data[cf.name])
|
||||||
|
|
||||||
# Assign a value and check that it is saved
|
# Assign a value and check that it is saved
|
||||||
instance.custom_field_data[cf.name] = value
|
instance.custom_field_data[cf.name] = cf.serialize(value)
|
||||||
instance.save()
|
instance.save()
|
||||||
instance.refresh_from_db()
|
instance.refresh_from_db()
|
||||||
self.assertEqual(instance.custom_field_data[cf.name], value)
|
self.assertEqual(instance.cf[cf.name], value)
|
||||||
|
|
||||||
|
# Delete the stored value and check that it is now null
|
||||||
|
instance.custom_field_data.pop(cf.name)
|
||||||
|
instance.save()
|
||||||
|
instance.refresh_from_db()
|
||||||
|
self.assertIsNone(instance.custom_field_data.get(cf.name))
|
||||||
|
|
||||||
|
def test_datetime_field(self):
|
||||||
|
value = datetime.datetime(2016, 6, 23, 9, 45, 0)
|
||||||
|
|
||||||
|
# Create a custom field & check that initial value is null
|
||||||
|
cf = CustomField.objects.create(
|
||||||
|
name='date_field',
|
||||||
|
type=CustomFieldTypeChoices.TYPE_DATETIME,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
cf.content_types.set([self.object_type])
|
||||||
|
instance = Site.objects.first()
|
||||||
|
self.assertIsNone(instance.custom_field_data[cf.name])
|
||||||
|
|
||||||
|
# Assign a value and check that it is saved
|
||||||
|
instance.custom_field_data[cf.name] = cf.serialize(value)
|
||||||
|
instance.save()
|
||||||
|
instance.refresh_from_db()
|
||||||
|
self.assertEqual(instance.cf[cf.name], value)
|
||||||
|
|
||||||
# Delete the stored value and check that it is now null
|
# Delete the stored value and check that it is now null
|
||||||
instance.custom_field_data.pop(cf.name)
|
instance.custom_field_data.pop(cf.name)
|
||||||
@ -408,6 +434,7 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
CustomField(type=CustomFieldTypeChoices.TYPE_DECIMAL, name='decimal_field', default=123.45),
|
CustomField(type=CustomFieldTypeChoices.TYPE_DECIMAL, name='decimal_field', default=123.45),
|
||||||
CustomField(type=CustomFieldTypeChoices.TYPE_BOOLEAN, name='boolean_field', default=False),
|
CustomField(type=CustomFieldTypeChoices.TYPE_BOOLEAN, name='boolean_field', default=False),
|
||||||
CustomField(type=CustomFieldTypeChoices.TYPE_DATE, name='date_field', default='2020-01-01'),
|
CustomField(type=CustomFieldTypeChoices.TYPE_DATE, name='date_field', default='2020-01-01'),
|
||||||
|
CustomField(type=CustomFieldTypeChoices.TYPE_DATETIME, name='datetime_field', default='2020-01-01T01:23:45'),
|
||||||
CustomField(type=CustomFieldTypeChoices.TYPE_URL, name='url_field', default='http://example.com/1'),
|
CustomField(type=CustomFieldTypeChoices.TYPE_URL, name='url_field', default='http://example.com/1'),
|
||||||
CustomField(type=CustomFieldTypeChoices.TYPE_JSON, name='json_field', default='{"x": "y"}'),
|
CustomField(type=CustomFieldTypeChoices.TYPE_JSON, name='json_field', default='{"x": "y"}'),
|
||||||
CustomField(
|
CustomField(
|
||||||
@ -459,12 +486,13 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
custom_fields[3].name: Decimal('456.78'),
|
custom_fields[3].name: Decimal('456.78'),
|
||||||
custom_fields[4].name: True,
|
custom_fields[4].name: True,
|
||||||
custom_fields[5].name: '2020-01-02',
|
custom_fields[5].name: '2020-01-02',
|
||||||
custom_fields[6].name: 'http://example.com/2',
|
custom_fields[6].name: '2020-01-02 12:00:00',
|
||||||
custom_fields[7].name: '{"foo": 1, "bar": 2}',
|
custom_fields[7].name: 'http://example.com/2',
|
||||||
custom_fields[8].name: 'Bar',
|
custom_fields[8].name: '{"foo": 1, "bar": 2}',
|
||||||
custom_fields[9].name: ['Bar', 'Baz'],
|
custom_fields[9].name: 'Bar',
|
||||||
custom_fields[10].name: vlans[1].pk,
|
custom_fields[10].name: ['Bar', 'Baz'],
|
||||||
custom_fields[11].name: [vlans[2].pk, vlans[3].pk],
|
custom_fields[11].name: vlans[1].pk,
|
||||||
|
custom_fields[12].name: [vlans[2].pk, vlans[3].pk],
|
||||||
}
|
}
|
||||||
sites[1].save()
|
sites[1].save()
|
||||||
|
|
||||||
@ -476,6 +504,7 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
CustomFieldTypeChoices.TYPE_DECIMAL: 'decimal',
|
CustomFieldTypeChoices.TYPE_DECIMAL: 'decimal',
|
||||||
CustomFieldTypeChoices.TYPE_BOOLEAN: 'boolean',
|
CustomFieldTypeChoices.TYPE_BOOLEAN: 'boolean',
|
||||||
CustomFieldTypeChoices.TYPE_DATE: 'string',
|
CustomFieldTypeChoices.TYPE_DATE: 'string',
|
||||||
|
CustomFieldTypeChoices.TYPE_DATETIME: 'string',
|
||||||
CustomFieldTypeChoices.TYPE_URL: 'string',
|
CustomFieldTypeChoices.TYPE_URL: 'string',
|
||||||
CustomFieldTypeChoices.TYPE_JSON: 'object',
|
CustomFieldTypeChoices.TYPE_JSON: 'object',
|
||||||
CustomFieldTypeChoices.TYPE_SELECT: 'string',
|
CustomFieldTypeChoices.TYPE_SELECT: 'string',
|
||||||
@ -511,6 +540,7 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
'decimal_field': None,
|
'decimal_field': None,
|
||||||
'boolean_field': None,
|
'boolean_field': None,
|
||||||
'date_field': None,
|
'date_field': None,
|
||||||
|
'datetime_field': None,
|
||||||
'url_field': None,
|
'url_field': None,
|
||||||
'json_field': None,
|
'json_field': None,
|
||||||
'select_field': None,
|
'select_field': None,
|
||||||
@ -536,6 +566,7 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(response.data['custom_fields']['decimal_field'], site2_cfvs['decimal_field'])
|
self.assertEqual(response.data['custom_fields']['decimal_field'], site2_cfvs['decimal_field'])
|
||||||
self.assertEqual(response.data['custom_fields']['boolean_field'], site2_cfvs['boolean_field'])
|
self.assertEqual(response.data['custom_fields']['boolean_field'], site2_cfvs['boolean_field'])
|
||||||
self.assertEqual(response.data['custom_fields']['date_field'], site2_cfvs['date_field'])
|
self.assertEqual(response.data['custom_fields']['date_field'], site2_cfvs['date_field'])
|
||||||
|
self.assertEqual(response.data['custom_fields']['datetime_field'], site2_cfvs['datetime_field'])
|
||||||
self.assertEqual(response.data['custom_fields']['url_field'], site2_cfvs['url_field'])
|
self.assertEqual(response.data['custom_fields']['url_field'], site2_cfvs['url_field'])
|
||||||
self.assertEqual(response.data['custom_fields']['json_field'], site2_cfvs['json_field'])
|
self.assertEqual(response.data['custom_fields']['json_field'], site2_cfvs['json_field'])
|
||||||
self.assertEqual(response.data['custom_fields']['select_field'], site2_cfvs['select_field'])
|
self.assertEqual(response.data['custom_fields']['select_field'], site2_cfvs['select_field'])
|
||||||
@ -571,6 +602,7 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(response_cf['decimal_field'], cf_defaults['decimal_field'])
|
self.assertEqual(response_cf['decimal_field'], cf_defaults['decimal_field'])
|
||||||
self.assertEqual(response_cf['boolean_field'], cf_defaults['boolean_field'])
|
self.assertEqual(response_cf['boolean_field'], cf_defaults['boolean_field'])
|
||||||
self.assertEqual(response_cf['date_field'].isoformat(), cf_defaults['date_field'])
|
self.assertEqual(response_cf['date_field'].isoformat(), cf_defaults['date_field'])
|
||||||
|
self.assertEqual(response_cf['datetime_field'].isoformat(), cf_defaults['datetime_field'])
|
||||||
self.assertEqual(response_cf['url_field'], cf_defaults['url_field'])
|
self.assertEqual(response_cf['url_field'], cf_defaults['url_field'])
|
||||||
self.assertEqual(response_cf['json_field'], cf_defaults['json_field'])
|
self.assertEqual(response_cf['json_field'], cf_defaults['json_field'])
|
||||||
self.assertEqual(response_cf['select_field'], cf_defaults['select_field'])
|
self.assertEqual(response_cf['select_field'], cf_defaults['select_field'])
|
||||||
@ -588,7 +620,8 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(site.custom_field_data['integer_field'], cf_defaults['integer_field'])
|
self.assertEqual(site.custom_field_data['integer_field'], cf_defaults['integer_field'])
|
||||||
self.assertEqual(site.custom_field_data['decimal_field'], cf_defaults['decimal_field'])
|
self.assertEqual(site.custom_field_data['decimal_field'], cf_defaults['decimal_field'])
|
||||||
self.assertEqual(site.custom_field_data['boolean_field'], cf_defaults['boolean_field'])
|
self.assertEqual(site.custom_field_data['boolean_field'], cf_defaults['boolean_field'])
|
||||||
self.assertEqual(str(site.custom_field_data['date_field']), cf_defaults['date_field'])
|
self.assertEqual(site.custom_field_data['date_field'], cf_defaults['date_field'])
|
||||||
|
self.assertEqual(site.custom_field_data['datetime_field'], cf_defaults['datetime_field'])
|
||||||
self.assertEqual(site.custom_field_data['url_field'], cf_defaults['url_field'])
|
self.assertEqual(site.custom_field_data['url_field'], cf_defaults['url_field'])
|
||||||
self.assertEqual(site.custom_field_data['json_field'], cf_defaults['json_field'])
|
self.assertEqual(site.custom_field_data['json_field'], cf_defaults['json_field'])
|
||||||
self.assertEqual(site.custom_field_data['select_field'], cf_defaults['select_field'])
|
self.assertEqual(site.custom_field_data['select_field'], cf_defaults['select_field'])
|
||||||
@ -609,7 +642,8 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
'integer_field': 456,
|
'integer_field': 456,
|
||||||
'decimal_field': 456.78,
|
'decimal_field': 456.78,
|
||||||
'boolean_field': True,
|
'boolean_field': True,
|
||||||
'date_field': '2020-01-02',
|
'date_field': datetime.date(2020, 1, 2),
|
||||||
|
'datetime_field': datetime.datetime(2020, 1, 2, 12, 0, 0),
|
||||||
'url_field': 'http://example.com/2',
|
'url_field': 'http://example.com/2',
|
||||||
'json_field': '{"foo": 1, "bar": 2}',
|
'json_field': '{"foo": 1, "bar": 2}',
|
||||||
'select_field': 'Bar',
|
'select_field': 'Bar',
|
||||||
@ -632,7 +666,8 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(response_cf['integer_field'], data_cf['integer_field'])
|
self.assertEqual(response_cf['integer_field'], data_cf['integer_field'])
|
||||||
self.assertEqual(response_cf['decimal_field'], data_cf['decimal_field'])
|
self.assertEqual(response_cf['decimal_field'], data_cf['decimal_field'])
|
||||||
self.assertEqual(response_cf['boolean_field'], data_cf['boolean_field'])
|
self.assertEqual(response_cf['boolean_field'], data_cf['boolean_field'])
|
||||||
self.assertEqual(response_cf['date_field'].isoformat(), data_cf['date_field'])
|
self.assertEqual(response_cf['date_field'], data_cf['date_field'])
|
||||||
|
self.assertEqual(response_cf['datetime_field'], data_cf['datetime_field'])
|
||||||
self.assertEqual(response_cf['url_field'], data_cf['url_field'])
|
self.assertEqual(response_cf['url_field'], data_cf['url_field'])
|
||||||
self.assertEqual(response_cf['json_field'], data_cf['json_field'])
|
self.assertEqual(response_cf['json_field'], data_cf['json_field'])
|
||||||
self.assertEqual(response_cf['select_field'], data_cf['select_field'])
|
self.assertEqual(response_cf['select_field'], data_cf['select_field'])
|
||||||
@ -650,7 +685,8 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(site.custom_field_data['integer_field'], data_cf['integer_field'])
|
self.assertEqual(site.custom_field_data['integer_field'], data_cf['integer_field'])
|
||||||
self.assertEqual(site.custom_field_data['decimal_field'], data_cf['decimal_field'])
|
self.assertEqual(site.custom_field_data['decimal_field'], data_cf['decimal_field'])
|
||||||
self.assertEqual(site.custom_field_data['boolean_field'], data_cf['boolean_field'])
|
self.assertEqual(site.custom_field_data['boolean_field'], data_cf['boolean_field'])
|
||||||
self.assertEqual(str(site.custom_field_data['date_field']), data_cf['date_field'])
|
self.assertEqual(site.cf['date_field'], data_cf['date_field'])
|
||||||
|
self.assertEqual(site.cf['datetime_field'], data_cf['datetime_field'])
|
||||||
self.assertEqual(site.custom_field_data['url_field'], data_cf['url_field'])
|
self.assertEqual(site.custom_field_data['url_field'], data_cf['url_field'])
|
||||||
self.assertEqual(site.custom_field_data['json_field'], data_cf['json_field'])
|
self.assertEqual(site.custom_field_data['json_field'], data_cf['json_field'])
|
||||||
self.assertEqual(site.custom_field_data['select_field'], data_cf['select_field'])
|
self.assertEqual(site.custom_field_data['select_field'], data_cf['select_field'])
|
||||||
@ -697,6 +733,7 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(response_cf['decimal_field'], cf_defaults['decimal_field'])
|
self.assertEqual(response_cf['decimal_field'], cf_defaults['decimal_field'])
|
||||||
self.assertEqual(response_cf['boolean_field'], cf_defaults['boolean_field'])
|
self.assertEqual(response_cf['boolean_field'], cf_defaults['boolean_field'])
|
||||||
self.assertEqual(response_cf['date_field'].isoformat(), cf_defaults['date_field'])
|
self.assertEqual(response_cf['date_field'].isoformat(), cf_defaults['date_field'])
|
||||||
|
self.assertEqual(response_cf['datetime_field'].isoformat(), cf_defaults['datetime_field'])
|
||||||
self.assertEqual(response_cf['url_field'], cf_defaults['url_field'])
|
self.assertEqual(response_cf['url_field'], cf_defaults['url_field'])
|
||||||
self.assertEqual(response_cf['json_field'], cf_defaults['json_field'])
|
self.assertEqual(response_cf['json_field'], cf_defaults['json_field'])
|
||||||
self.assertEqual(response_cf['select_field'], cf_defaults['select_field'])
|
self.assertEqual(response_cf['select_field'], cf_defaults['select_field'])
|
||||||
@ -714,7 +751,8 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(site.custom_field_data['integer_field'], cf_defaults['integer_field'])
|
self.assertEqual(site.custom_field_data['integer_field'], cf_defaults['integer_field'])
|
||||||
self.assertEqual(site.custom_field_data['decimal_field'], cf_defaults['decimal_field'])
|
self.assertEqual(site.custom_field_data['decimal_field'], cf_defaults['decimal_field'])
|
||||||
self.assertEqual(site.custom_field_data['boolean_field'], cf_defaults['boolean_field'])
|
self.assertEqual(site.custom_field_data['boolean_field'], cf_defaults['boolean_field'])
|
||||||
self.assertEqual(str(site.custom_field_data['date_field']), cf_defaults['date_field'])
|
self.assertEqual(site.custom_field_data['date_field'], cf_defaults['date_field'])
|
||||||
|
self.assertEqual(site.custom_field_data['datetime_field'], cf_defaults['datetime_field'])
|
||||||
self.assertEqual(site.custom_field_data['url_field'], cf_defaults['url_field'])
|
self.assertEqual(site.custom_field_data['url_field'], cf_defaults['url_field'])
|
||||||
self.assertEqual(site.custom_field_data['json_field'], cf_defaults['json_field'])
|
self.assertEqual(site.custom_field_data['json_field'], cf_defaults['json_field'])
|
||||||
self.assertEqual(site.custom_field_data['select_field'], cf_defaults['select_field'])
|
self.assertEqual(site.custom_field_data['select_field'], cf_defaults['select_field'])
|
||||||
@ -732,7 +770,8 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
'integer_field': 456,
|
'integer_field': 456,
|
||||||
'decimal_field': 456.78,
|
'decimal_field': 456.78,
|
||||||
'boolean_field': True,
|
'boolean_field': True,
|
||||||
'date_field': '2020-01-02',
|
'date_field': datetime.date(2020, 1, 2),
|
||||||
|
'datetime_field': datetime.datetime(2020, 1, 2, 12, 0, 0),
|
||||||
'url_field': 'http://example.com/2',
|
'url_field': 'http://example.com/2',
|
||||||
'json_field': '{"foo": 1, "bar": 2}',
|
'json_field': '{"foo": 1, "bar": 2}',
|
||||||
'select_field': 'Bar',
|
'select_field': 'Bar',
|
||||||
@ -773,7 +812,8 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(response_cf['integer_field'], custom_field_data['integer_field'])
|
self.assertEqual(response_cf['integer_field'], custom_field_data['integer_field'])
|
||||||
self.assertEqual(response_cf['decimal_field'], custom_field_data['decimal_field'])
|
self.assertEqual(response_cf['decimal_field'], custom_field_data['decimal_field'])
|
||||||
self.assertEqual(response_cf['boolean_field'], custom_field_data['boolean_field'])
|
self.assertEqual(response_cf['boolean_field'], custom_field_data['boolean_field'])
|
||||||
self.assertEqual(response_cf['date_field'].isoformat(), custom_field_data['date_field'])
|
self.assertEqual(response_cf['date_field'], custom_field_data['date_field'])
|
||||||
|
self.assertEqual(response_cf['datetime_field'], custom_field_data['datetime_field'])
|
||||||
self.assertEqual(response_cf['url_field'], custom_field_data['url_field'])
|
self.assertEqual(response_cf['url_field'], custom_field_data['url_field'])
|
||||||
self.assertEqual(response_cf['json_field'], custom_field_data['json_field'])
|
self.assertEqual(response_cf['json_field'], custom_field_data['json_field'])
|
||||||
self.assertEqual(response_cf['select_field'], custom_field_data['select_field'])
|
self.assertEqual(response_cf['select_field'], custom_field_data['select_field'])
|
||||||
@ -791,7 +831,8 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(site.custom_field_data['integer_field'], custom_field_data['integer_field'])
|
self.assertEqual(site.custom_field_data['integer_field'], custom_field_data['integer_field'])
|
||||||
self.assertEqual(site.custom_field_data['decimal_field'], custom_field_data['decimal_field'])
|
self.assertEqual(site.custom_field_data['decimal_field'], custom_field_data['decimal_field'])
|
||||||
self.assertEqual(site.custom_field_data['boolean_field'], custom_field_data['boolean_field'])
|
self.assertEqual(site.custom_field_data['boolean_field'], custom_field_data['boolean_field'])
|
||||||
self.assertEqual(str(site.custom_field_data['date_field']), custom_field_data['date_field'])
|
self.assertEqual(site.cf['date_field'], custom_field_data['date_field'])
|
||||||
|
self.assertEqual(site.cf['datetime_field'], custom_field_data['datetime_field'])
|
||||||
self.assertEqual(site.custom_field_data['url_field'], custom_field_data['url_field'])
|
self.assertEqual(site.custom_field_data['url_field'], custom_field_data['url_field'])
|
||||||
self.assertEqual(site.custom_field_data['json_field'], custom_field_data['json_field'])
|
self.assertEqual(site.custom_field_data['json_field'], custom_field_data['json_field'])
|
||||||
self.assertEqual(site.custom_field_data['select_field'], custom_field_data['select_field'])
|
self.assertEqual(site.custom_field_data['select_field'], custom_field_data['select_field'])
|
||||||
@ -826,6 +867,7 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(response_cf['decimal_field'], original_cfvs['decimal_field'])
|
self.assertEqual(response_cf['decimal_field'], original_cfvs['decimal_field'])
|
||||||
self.assertEqual(response_cf['boolean_field'], original_cfvs['boolean_field'])
|
self.assertEqual(response_cf['boolean_field'], original_cfvs['boolean_field'])
|
||||||
self.assertEqual(response_cf['date_field'], original_cfvs['date_field'])
|
self.assertEqual(response_cf['date_field'], original_cfvs['date_field'])
|
||||||
|
self.assertEqual(response_cf['datetime_field'], original_cfvs['datetime_field'])
|
||||||
self.assertEqual(response_cf['url_field'], original_cfvs['url_field'])
|
self.assertEqual(response_cf['url_field'], original_cfvs['url_field'])
|
||||||
self.assertEqual(response_cf['json_field'], original_cfvs['json_field'])
|
self.assertEqual(response_cf['json_field'], original_cfvs['json_field'])
|
||||||
self.assertEqual(response_cf['select_field'], original_cfvs['select_field'])
|
self.assertEqual(response_cf['select_field'], original_cfvs['select_field'])
|
||||||
@ -844,6 +886,7 @@ class CustomFieldAPITest(APITestCase):
|
|||||||
self.assertEqual(site2.cf['decimal_field'], original_cfvs['decimal_field'])
|
self.assertEqual(site2.cf['decimal_field'], original_cfvs['decimal_field'])
|
||||||
self.assertEqual(site2.cf['boolean_field'], original_cfvs['boolean_field'])
|
self.assertEqual(site2.cf['boolean_field'], original_cfvs['boolean_field'])
|
||||||
self.assertEqual(site2.cf['date_field'], original_cfvs['date_field'])
|
self.assertEqual(site2.cf['date_field'], original_cfvs['date_field'])
|
||||||
|
self.assertEqual(site2.cf['datetime_field'], original_cfvs['datetime_field'])
|
||||||
self.assertEqual(site2.cf['url_field'], original_cfvs['url_field'])
|
self.assertEqual(site2.cf['url_field'], original_cfvs['url_field'])
|
||||||
self.assertEqual(site2.cf['json_field'], original_cfvs['json_field'])
|
self.assertEqual(site2.cf['json_field'], original_cfvs['json_field'])
|
||||||
self.assertEqual(site2.cf['select_field'], original_cfvs['select_field'])
|
self.assertEqual(site2.cf['select_field'], original_cfvs['select_field'])
|
||||||
@ -977,6 +1020,7 @@ class CustomFieldImportTest(TestCase):
|
|||||||
CustomField(name='decimal', type=CustomFieldTypeChoices.TYPE_DECIMAL),
|
CustomField(name='decimal', type=CustomFieldTypeChoices.TYPE_DECIMAL),
|
||||||
CustomField(name='boolean', type=CustomFieldTypeChoices.TYPE_BOOLEAN),
|
CustomField(name='boolean', type=CustomFieldTypeChoices.TYPE_BOOLEAN),
|
||||||
CustomField(name='date', type=CustomFieldTypeChoices.TYPE_DATE),
|
CustomField(name='date', type=CustomFieldTypeChoices.TYPE_DATE),
|
||||||
|
CustomField(name='datetime', type=CustomFieldTypeChoices.TYPE_DATETIME),
|
||||||
CustomField(name='url', type=CustomFieldTypeChoices.TYPE_URL),
|
CustomField(name='url', type=CustomFieldTypeChoices.TYPE_URL),
|
||||||
CustomField(name='json', type=CustomFieldTypeChoices.TYPE_JSON),
|
CustomField(name='json', type=CustomFieldTypeChoices.TYPE_JSON),
|
||||||
CustomField(name='select', type=CustomFieldTypeChoices.TYPE_SELECT, choices=[
|
CustomField(name='select', type=CustomFieldTypeChoices.TYPE_SELECT, choices=[
|
||||||
@ -995,10 +1039,10 @@ class CustomFieldImportTest(TestCase):
|
|||||||
Import a Site in CSV format, including a value for each CustomField.
|
Import a Site in CSV format, including a value for each CustomField.
|
||||||
"""
|
"""
|
||||||
data = (
|
data = (
|
||||||
('name', 'slug', 'status', 'cf_text', 'cf_longtext', 'cf_integer', 'cf_decimal', 'cf_boolean', 'cf_date', 'cf_url', 'cf_json', 'cf_select', 'cf_multiselect'),
|
('name', 'slug', 'status', 'cf_text', 'cf_longtext', 'cf_integer', 'cf_decimal', 'cf_boolean', 'cf_date', 'cf_datetime', 'cf_url', 'cf_json', 'cf_select', 'cf_multiselect'),
|
||||||
('Site 1', 'site-1', 'active', 'ABC', 'Foo', '123', '123.45', 'True', '2020-01-01', 'http://example.com/1', '{"foo": 123}', 'Choice A', '"Choice A,Choice B"'),
|
('Site 1', 'site-1', 'active', 'ABC', 'Foo', '123', '123.45', 'True', '2020-01-01', '2020-01-01 12:00:00', 'http://example.com/1', '{"foo": 123}', 'Choice A', '"Choice A,Choice B"'),
|
||||||
('Site 2', 'site-2', 'active', 'DEF', 'Bar', '456', '456.78', 'False', '2020-01-02', 'http://example.com/2', '{"bar": 456}', 'Choice B', '"Choice B,Choice C"'),
|
('Site 2', 'site-2', 'active', 'DEF', 'Bar', '456', '456.78', 'False', '2020-01-02', '2020-01-02 12:00:00', 'http://example.com/2', '{"bar": 456}', 'Choice B', '"Choice B,Choice C"'),
|
||||||
('Site 3', 'site-3', 'active', '', '', '', '', '', '', '', '', '', ''),
|
('Site 3', 'site-3', 'active', '', '', '', '', '', '', '', '', '', '', ''),
|
||||||
)
|
)
|
||||||
csv_data = '\n'.join(','.join(row) for row in data)
|
csv_data = '\n'.join(','.join(row) for row in data)
|
||||||
|
|
||||||
@ -1008,13 +1052,14 @@ class CustomFieldImportTest(TestCase):
|
|||||||
|
|
||||||
# Validate data for site 1
|
# Validate data for site 1
|
||||||
site1 = Site.objects.get(name='Site 1')
|
site1 = Site.objects.get(name='Site 1')
|
||||||
self.assertEqual(len(site1.custom_field_data), 10)
|
self.assertEqual(len(site1.custom_field_data), 11)
|
||||||
self.assertEqual(site1.custom_field_data['text'], 'ABC')
|
self.assertEqual(site1.custom_field_data['text'], 'ABC')
|
||||||
self.assertEqual(site1.custom_field_data['longtext'], 'Foo')
|
self.assertEqual(site1.custom_field_data['longtext'], 'Foo')
|
||||||
self.assertEqual(site1.custom_field_data['integer'], 123)
|
self.assertEqual(site1.custom_field_data['integer'], 123)
|
||||||
self.assertEqual(site1.custom_field_data['decimal'], 123.45)
|
self.assertEqual(site1.custom_field_data['decimal'], 123.45)
|
||||||
self.assertEqual(site1.custom_field_data['boolean'], True)
|
self.assertEqual(site1.custom_field_data['boolean'], True)
|
||||||
self.assertEqual(site1.custom_field_data['date'], '2020-01-01')
|
self.assertEqual(site1.cf['date'].isoformat(), '2020-01-01')
|
||||||
|
self.assertEqual(site1.cf['datetime'].isoformat(), '2020-01-01T12:00:00+00:00')
|
||||||
self.assertEqual(site1.custom_field_data['url'], 'http://example.com/1')
|
self.assertEqual(site1.custom_field_data['url'], 'http://example.com/1')
|
||||||
self.assertEqual(site1.custom_field_data['json'], {"foo": 123})
|
self.assertEqual(site1.custom_field_data['json'], {"foo": 123})
|
||||||
self.assertEqual(site1.custom_field_data['select'], 'Choice A')
|
self.assertEqual(site1.custom_field_data['select'], 'Choice A')
|
||||||
@ -1022,13 +1067,14 @@ class CustomFieldImportTest(TestCase):
|
|||||||
|
|
||||||
# Validate data for site 2
|
# Validate data for site 2
|
||||||
site2 = Site.objects.get(name='Site 2')
|
site2 = Site.objects.get(name='Site 2')
|
||||||
self.assertEqual(len(site2.custom_field_data), 10)
|
self.assertEqual(len(site2.custom_field_data), 11)
|
||||||
self.assertEqual(site2.custom_field_data['text'], 'DEF')
|
self.assertEqual(site2.custom_field_data['text'], 'DEF')
|
||||||
self.assertEqual(site2.custom_field_data['longtext'], 'Bar')
|
self.assertEqual(site2.custom_field_data['longtext'], 'Bar')
|
||||||
self.assertEqual(site2.custom_field_data['integer'], 456)
|
self.assertEqual(site2.custom_field_data['integer'], 456)
|
||||||
self.assertEqual(site2.custom_field_data['decimal'], 456.78)
|
self.assertEqual(site2.custom_field_data['decimal'], 456.78)
|
||||||
self.assertEqual(site2.custom_field_data['boolean'], False)
|
self.assertEqual(site2.custom_field_data['boolean'], False)
|
||||||
self.assertEqual(site2.custom_field_data['date'], '2020-01-02')
|
self.assertEqual(site2.cf['date'].isoformat(), '2020-01-02')
|
||||||
|
self.assertEqual(site2.cf['datetime'].isoformat(), '2020-01-02T12:00:00+00:00')
|
||||||
self.assertEqual(site2.custom_field_data['url'], 'http://example.com/2')
|
self.assertEqual(site2.custom_field_data['url'], 'http://example.com/2')
|
||||||
self.assertEqual(site2.custom_field_data['json'], {"bar": 456})
|
self.assertEqual(site2.custom_field_data['json'], {"bar": 456})
|
||||||
self.assertEqual(site2.custom_field_data['select'], 'Choice B')
|
self.assertEqual(site2.custom_field_data['select'], 'Choice B')
|
||||||
|
@ -32,6 +32,9 @@ class CustomFieldModelFormTest(TestCase):
|
|||||||
cf_date = CustomField.objects.create(name='date', type=CustomFieldTypeChoices.TYPE_DATE)
|
cf_date = CustomField.objects.create(name='date', type=CustomFieldTypeChoices.TYPE_DATE)
|
||||||
cf_date.content_types.set([obj_type])
|
cf_date.content_types.set([obj_type])
|
||||||
|
|
||||||
|
cf_datetime = CustomField.objects.create(name='datetime', type=CustomFieldTypeChoices.TYPE_DATETIME)
|
||||||
|
cf_datetime.content_types.set([obj_type])
|
||||||
|
|
||||||
cf_url = CustomField.objects.create(name='url', type=CustomFieldTypeChoices.TYPE_URL)
|
cf_url = CustomField.objects.create(name='url', type=CustomFieldTypeChoices.TYPE_URL)
|
||||||
cf_url.content_types.set([obj_type])
|
cf_url.content_types.set([obj_type])
|
||||||
|
|
||||||
|
@ -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