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:
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
||||
|
Reference in New Issue
Block a user