Setting all custom field values to None via Custom Scripts

214 views
Skip to first unread message

Doyoup Kim

unread,
Apr 29, 2020, 6:57:32 PM4/29/20
to NetBox
I'm trying to make a simple custom script. It searches a device using ObjectVar and once the script runs its suppose to perform certain tasks. The task I'm trying to perform is setting all of the device's custom field to None. I've tried the following:

class TestScript(Script):

    device = ObjectVar(
        description = "Get Device",
        queryset = Device.objects.filter(status=DeviceStatusChoices.STATUS_ACTIVE, device_role__in=[6, 23, 11, 12])
    )


    def run(self, data, commit):
        for device in Device.objects.filter(status=DeviceStatusChoices.STATUS_ACTIVE, device_role__in=[6, 23, 11, 12]):
            if str(device) == str(data['device']):

                 device.custom_fields = None  #Attempt 1
                 device.CustomField = None    #Attempt 2
                 device.custom_fields['OS'] = None  #Attempt 1
                 device.save()


All our devices has 4 fields in the custom field section. After running this script I would like all of them to have the value None or whatever default value. The script seems to run however the changes aren't being saved. Any advice is appreciated.

Brian Candler

unread,
Apr 30, 2020, 3:44:11 AM4/30/20
to NetBox
"custom_fields" are the definitions of which fields have been defined for this particular type of object, while "custom_field_values" are actual user-entered values for a given custom_field.  Hence you'll need to manipulate the CustomFieldValue model - in your case, you would *delete* items to reset them to unset (default).

On the Device end, the relation you want is called "custom_field_values":

    custom_field_values = GenericRelation(
        to='extras.CustomFieldValue',
        content_type_field='obj_type',
        object_id_field='obj_id'
    )

Also look at class CustomFieldValue(models.Model) in netbox/extras/models.py.  It's a GenericForeignKey relation to object.

If you're only reading custom field values, there's a convenience helper method "cf" which collects all the values into a dict.  But this code may also give you some clues as to how to access them.

    def cache_custom_fields(self):
        """
        Cache all custom field values for this instance
        """
        self._cf = {
            field.name: value for field, value in self.get_custom_fields().items()
        }

    @property
    def cf(self):
        """
        Name-based CustomFieldValue accessor for use in templates
        """
        if self._cf is None:
            self.cache_custom_fields()
        return self._cf

    def get_custom_fields(self):
        """
        Return a dictionary of custom fields for a single object in the form {<field>: value}.
        """

        # Find all custom fields applicable to this type of object
        content_type = ContentType.objects.get_for_model(self)
        fields = CustomField.objects.filter(obj_type=content_type)

        # If the object exists, populate its custom fields with values
        if hasattr(self, 'pk'):
            values = self.custom_field_values.all()
            values_dict = {cfv.field_id: cfv.value for cfv in values}
            return OrderedDict([(field, values_dict.get(field.pk)) for field in fields])
        else:
            return OrderedDict([(field, None) for field in fields])

Doyoup Kim

unread,
May 6, 2020, 4:51:23 PM5/6/20
to NetBox
Apologies for the late response and thank you for your answer. I wasn't aware there were 2 variables in play here the custom fields and the custom field values. So knowing that I can elaborate on my previously stated task and say that what I'm trying to do is to set the custom_field_values of certain custom fields on a device to their default values or None.

Here is what the customer_fields look like on one of our devices:
Ideally I would like all the values of these custom fields to be nothing or it's default value after I run this custom script.

Here is what I've tried after your response

** I'm trying to set the custom_field: OS to None
ct = ContentType.objects.get_for_model(Device)
cf = CustomField.objects.get(obj_type=ct, name="OS")
device = Device.objects.get(name=device.name)
ch = CustomFieldChoice.objects.get(field=cf, value=None)  <----- I've also tried adding: "" and it results in a error saying:
CustomFieldChoice matching query does not exist.
CustomFieldValue.objects.update_or_create(field=cf, obj_type=ct, obj_id=device.id, serialized_value=ch.id)

I've also tried
device.cf['OS'].delete()
However that just resulted in deleting the value option entirely.

So am I on the right track to achieving this? Also some custom field values have choices whereas other values have no constraints on what it can be. Does that make a different in regards to setting them to None or their default values?

Thank you in advanced.

Brian Candler

unread,
May 6, 2020, 5:16:12 PM5/6/20
to NetBox
I don't have any custom fields to test with, but if you want to delete all the custom field values on a device, I'm guessing it would be something along the lines of:

device.custom_field_values.delete() 

or:

CustomFieldValue.objects.filter(device=device).delete()

or:

content_type = ContentType.objects.get_for_model(id)
CustomFieldValue.objects.filter(obj_type=content_type, obj_id=device.id).delete()


Obviously, test in a development environment where it doesn't matter if the wrong things get deleted.

Doyoup Kim

unread,
May 6, 2020, 6:28:33 PM5/6/20
to NetBox
Your third suggestion:

content_type = ContentType.objects.get_for_model(id)
CustomFieldValue.objects.filter(obj_type=content_type, obj_id=device.id).delete()

Did exactly what I was trying to do. However, I just had to change id to device. Thank you very much for your responsive answer!
Reply all
Reply to author
Forward
0 new messages