Custom validator for tags works for single edits but breaks bulk edits #10142

Closed
opened 2025-12-29 21:27:26 +01:00 by adam · 6 comments
Owner

Originally created by @Azmodeszer on GitHub (Aug 27, 2024).

Deployment Type

Self-hosted

NetBox Version

v.4.0.9

Python Version

3.11

Steps to Reproduce

  1. Create a custom validator as described here https://github.com/netbox-community/netbox/discussions/16198#discussioncomment-10463695
  2. Bulk edit affected objects. (VMs or devices, in my case)

Expected Behavior

Edit works as it does for single edits.

Observed Behavior

<class 'KeyError'>

'tags'
Originally created by @Azmodeszer on GitHub (Aug 27, 2024). ### Deployment Type Self-hosted ### NetBox Version v.4.0.9 ### Python Version 3.11 ### Steps to Reproduce 1. Create a custom validator as described here https://github.com/netbox-community/netbox/discussions/16198#discussioncomment-10463695 2. Bulk edit affected objects. (VMs or devices, in my case) ### Expected Behavior Edit works as it does for single edits. ### Observed Behavior ``` <class 'KeyError'> 'tags' ```
adam added the type: bugpending closurenetbox labels 2025-12-29 21:27:26 +01:00
adam closed this issue 2025-12-29 21:27:26 +01:00
Author
Owner

@jeremystretch commented on GitHub (Aug 27, 2024):

  1. Create a custom validator as described here

Please replicate the exact reproduction steps in your initial post above.

@jeremystretch commented on GitHub (Aug 27, 2024): > 1. Create a custom validator as described here Please replicate the exact reproduction steps in your initial post above.
Author
Owner

@Azmodeszer commented on GitHub (Aug 28, 2024):

@jeremystretch

  1. Create a script for the validator checking for the presence of either of two tags and the lack of a third. self.fail if the condition is met:
from extras.validators import CustomValidator

class webmintagging(CustomValidator):

    def validate(self, instance, request):
        if ("webmin-autoupdates".upper() in [tag.slug.upper() for tag in instance._m2m_values['tags']] or "webmin-autosecupdates".upper() in [tag.slug.upper() for tag in instance._m2m_values['tags']]) and not "webmin".upper() in [tag.slug.upper() for tag in instance._m2m_values['tags']]:
            self.fail("Can't add specific Webmin tag without basic Webmin tag!", field='tags')
  1. Add a custom validator pointing to the script to configuration.py, applying it to devices and VMs:
CUSTOM_VALIDATORS = {
    'dcim.device': (
        'scripts.custom_validators.webmintagging.webmintagging',
    ),
    'virtualization.virtualmachine': (
        'scripts.custom_validators.webmintagging.webmintagging',
    ),
}
  1. sudo systemctl restart netbox
  2. Try to edit a single device or VM. (validation works)
  3. Try to bulk edit two or more devices or VMs. (does not work and results in the error above)

Maybe something to do with the fact that for bulk edits the field splits into "Add tags" and "Remove tags"?

@Azmodeszer commented on GitHub (Aug 28, 2024): @jeremystretch 1. Create a script for the validator checking for the presence of either of two tags and the lack of a third. self.fail if the condition is met: ``` from extras.validators import CustomValidator class webmintagging(CustomValidator): def validate(self, instance, request): if ("webmin-autoupdates".upper() in [tag.slug.upper() for tag in instance._m2m_values['tags']] or "webmin-autosecupdates".upper() in [tag.slug.upper() for tag in instance._m2m_values['tags']]) and not "webmin".upper() in [tag.slug.upper() for tag in instance._m2m_values['tags']]: self.fail("Can't add specific Webmin tag without basic Webmin tag!", field='tags') ``` 2. Add a custom validator pointing to the script to configuration.py, applying it to devices and VMs: ``` CUSTOM_VALIDATORS = { 'dcim.device': ( 'scripts.custom_validators.webmintagging.webmintagging', ), 'virtualization.virtualmachine': ( 'scripts.custom_validators.webmintagging.webmintagging', ), } ``` 3. `sudo systemctl restart netbox` 4. Try to edit a single device or VM. (validation works) 5. Try to bulk edit two or more devices or VMs. (does not work and results in the error above) Maybe something to do with the fact that for bulk edits the field splits into "Add tags" and "Remove tags"?
Author
Owner

@atownson commented on GitHub (Aug 30, 2024):

Just an FYI, I can replicate this issue and confirm the custom validator breaks bulk editing regardless of whether or not tags are added or removed in the bulk edit request (i.e. just changing the description field).
Here's the validator we have in place:

class GroupValidator(CustomValidator):
    def validate(self, instance):
        # Group is required unless the instance is tagged with 'Exception'
        if not instance.group and 'exception' not in [tag.name.lower() for tag in instance._m2m_values['tags']]:
            self.fail('Custom validation: Group is required.', field='group')

image

The issue appears to be that the _m2m_values dictionary is empty.

@atownson commented on GitHub (Aug 30, 2024): Just an FYI, I can replicate this issue and confirm the custom validator breaks bulk editing regardless of whether or not tags are added or removed in the bulk edit request (i.e. just changing the `description` field). Here's the validator we have in place: ``` class GroupValidator(CustomValidator): def validate(self, instance): # Group is required unless the instance is tagged with 'Exception' if not instance.group and 'exception' not in [tag.name.lower() for tag in instance._m2m_values['tags']]: self.fail('Custom validation: Group is required.', field='group') ``` ![image](https://github.com/user-attachments/assets/f67ca422-c302-41aa-8c3c-4656e6c9ba3b) The issue appears to be that the `_m2m_values` dictionary is empty.
Author
Owner

@kuhball commented on GitHub (Feb 21, 2025):

Hi, just ran into this as well. I'm wondering whether using the _m2m_values is the suggested way of accessing m2m fields. If yes it would maybe be nice to add this to the docs.

@kuhball commented on GitHub (Feb 21, 2025): Hi, just ran into this as well. I'm wondering whether using the `_m2m_values` is the suggested way of accessing m2m fields. If yes it would maybe be nice to add this to the docs.
Author
Owner

@github-actions[bot] commented on GitHub (May 23, 2025):

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. NetBox is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. Do not attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see our contributing guide.

@github-actions[bot] commented on GitHub (May 23, 2025): This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. NetBox is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. **Do not** attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see our [contributing guide](https://github.com/netbox-community/netbox/blob/main/CONTRIBUTING.md).
Author
Owner

@arthanson commented on GitHub (May 30, 2025):

Closing this, in general methods / vars with underscores are considered internal functions. This would also not be considered a bug and shoul be a discussion.

That said, if you have that working just not with bulk_edit what you are probably wanting to do is first have a check with hasattr(instance, '_m2m_values') and only check for the tag being there after that. Being aware that _m2m_values is an internal function and could change or be removed in the future without notice.

@arthanson commented on GitHub (May 30, 2025): Closing this, in general methods / vars with underscores are considered internal functions. This would also not be considered a bug and shoul be a discussion. That said, if you have that working just not with bulk_edit what you are probably wanting to do is first have a check with hasattr(instance, '_m2m_values') and only check for the tag being there after that. Being aware that _m2m_values is an internal function and could change or be removed in the future without notice.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#10142