1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Fixes #5652: Update object data when renaming a custom field

This commit is contained in:
jeremystretch
2021-04-15 12:04:34 -04:00
parent a2203da1c6
commit 92df40a6a0
4 changed files with 56 additions and 1 deletions

View File

@ -162,6 +162,24 @@ class CustomField(models.Model):
def __str__(self):
return self.label or self.name.replace('_', ' ').capitalize()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Cache instance's original name so we can check later whether it has changed
self._name = self.name
def rename_object_data(self, old_name, new_name):
"""
Called when a CustomField has been renamed. Updates all assigned object data.
"""
for ct in self.content_types.all():
model = ct.model_class()
params = {f'custom_field_data__{old_name}__isnull': False}
instances = model.objects.filter(**params)
for instance in instances:
instance.custom_field_data[new_name] = instance.custom_field_data.pop(old_name)
model.objects.bulk_update(instances, ['custom_field_data'], batch_size=100)
def remove_stale_data(self, content_types):
"""
Delete custom field data which is no longer relevant (either because the CustomField is

View File

@ -5,7 +5,7 @@ from cacheops.signals import cache_invalidated, cache_read
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db import DEFAULT_DB_ALIAS
from django.db.models.signals import m2m_changed, pre_delete
from django.db.models.signals import m2m_changed, post_save, pre_delete
from django.utils import timezone
from django_prometheus.models import model_deletes, model_inserts, model_updates
from prometheus_client import Counter
@ -86,6 +86,14 @@ def handle_cf_removed_obj_types(instance, action, pk_set, **kwargs):
instance.remove_stale_data(ContentType.objects.filter(pk__in=pk_set))
def handle_cf_renamed(instance, created, **kwargs):
"""
Handle the renaming of custom field data on objects when a CustomField is renamed.
"""
if not created and instance.name != instance._name:
instance.rename_object_data(old_name=instance._name, new_name=instance.name)
def handle_cf_deleted(instance, **kwargs):
"""
Handle the cleanup of old custom field data when a CustomField is deleted.
@ -94,6 +102,7 @@ def handle_cf_deleted(instance, **kwargs):
m2m_changed.connect(handle_cf_removed_obj_types, sender=CustomField.content_types.through)
post_save.connect(handle_cf_renamed, sender=CustomField)
pre_delete.connect(handle_cf_deleted, sender=CustomField)

View File

@ -91,6 +91,33 @@ class CustomFieldTest(TestCase):
# Delete the custom field
cf.delete()
def test_rename_customfield(self):
obj_type = ContentType.objects.get_for_model(Site)
FIELD_DATA = 'abc'
# Create a custom field
cf = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='field1')
cf.save()
cf.content_types.set([obj_type])
# Assign custom field data to an object
site = Site.objects.create(
name='Site 1',
slug='site-1',
custom_field_data={'field1': FIELD_DATA}
)
site.refresh_from_db()
self.assertEqual(site.custom_field_data['field1'], FIELD_DATA)
# Rename the custom field
cf.name = 'field2'
cf.save()
# Check that custom field data on the object has been updated
site.refresh_from_db()
self.assertNotIn('field1', site.custom_field_data)
self.assertEqual(site.custom_field_data['field2'], FIELD_DATA)
class CustomFieldManagerTest(TestCase):