Cannot filter on Tags in Custom Scripts #10820

Closed
opened 2025-12-29 21:36:17 +01:00 by adam · 4 comments
Owner

Originally created by @justinharris1986 on GitHub (Feb 27, 2025).

Deployment Type

Self-hosted

NetBox Version

v4.2.4

Python Version

3.10

Steps to Reproduce

Build this example script in Customization -> Scripts:

from extras.scripts import Script, ObjectVar
from dcim.models import Interface, Device
from extras.models import Tag

class Test(Script):
    class Meta:
        name = "test"
        description = "test"
        scheduling_enabled = False

    partner = ObjectVar(
        description="Partner Tags",
        model=Tag,
        query_params={
            'description': 'Partner'
        }
    )

    #NNI_device = ObjectVar(
    #    description="NNI Device",
    #    model=Device,
    #    query_params={
    #        'region_id': '$region',
    #        'role_id': 2
    #    }
    #)

    NNI_interface = ObjectVar(
        description="NNI Interface",
        model=Interface,
        query_params={
            'tag': '$partner', # However if I replace this with 'tag': 'partnera', then it filters properly. note: PartnerA does NOT work, so it feels like it is looking for a slug, but $partner is something other than a slug. 
            #'device_id': '$NNI_device'
        }
    )


    def run(self, data, commit):
        print(data)

Expected Behavior

The dropdown for NNI interface should populate ONLY the interfaces that are tagged with the selection from the Partner dropdown, above.

Observed Behavior

The dropdown for NNI interface is blank

Few other observed behaviors:

  • This affects more than just device Interfaces. I tried on Circuits as well, same issue.
  • If I change the line 'tag': '$partner', to 'tag': 'partnera', (valid tag slug) it works as expected, for ONLY that one tag (as expected).
  • If I change the line 'tag': '$partner', to 'tag': 'PartnerA', (the valid Name for the above slug) it does NOT work as expected, leading to the same behavior as the dropdown.
  • If I comment the Partner tags section, uncomment the NNI device section, and do the same with the query_params in the NNI interface, it works as expected.

My assumption is that $partner is returning something other than the slug as a value.

To validate this, look at the developer tools, you will see this request XHR/fetch after you select a partner in the dropdown:

Summary
URL: https://10.X.X.X/api/dcim/interfaces/?brief=true&limit=100&tag=8
Status: 400 Bad Request
Source: Network
Address: 10.X.X.X:443
Initiator: netbox.js:3:17395

(IP masked for security)

Taking that url in to the browser does indeed return:

{
    "tag": [
        "Select a valid choice. 8 is not one of the available choices."
    ]
}

Changing the URL to: https://10.X.X.X/api/dcim/interfaces/?brief=true&limit=100&tag=partnera does return a valid result:

HTTP 200 OK
Allow: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE
Content-Type: application/json
Vary: Accept

{
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 10506,
<snip>

According to the documentation at: https://netboxlabs.com/docs/netbox/en/stable/reference/filtering/#filtering-objects we should be using slugs rather than ids with the API, however, it appears that all the other filters use IDs, so they are at odds with each other.

Changing the query_params from tag to tag_id did not have any effect.

Originally created by @justinharris1986 on GitHub (Feb 27, 2025). ### Deployment Type Self-hosted ### NetBox Version v4.2.4 ### Python Version 3.10 ### Steps to Reproduce Build this example script in Customization -> Scripts: ``` from extras.scripts import Script, ObjectVar from dcim.models import Interface, Device from extras.models import Tag class Test(Script): class Meta: name = "test" description = "test" scheduling_enabled = False partner = ObjectVar( description="Partner Tags", model=Tag, query_params={ 'description': 'Partner' } ) #NNI_device = ObjectVar( # description="NNI Device", # model=Device, # query_params={ # 'region_id': '$region', # 'role_id': 2 # } #) NNI_interface = ObjectVar( description="NNI Interface", model=Interface, query_params={ 'tag': '$partner', # However if I replace this with 'tag': 'partnera', then it filters properly. note: PartnerA does NOT work, so it feels like it is looking for a slug, but $partner is something other than a slug. #'device_id': '$NNI_device' } ) def run(self, data, commit): print(data) ``` ### Expected Behavior The dropdown for NNI interface should populate ONLY the interfaces that are tagged with the selection from the Partner dropdown, above. ### Observed Behavior The dropdown for NNI interface is blank Few other observed behaviors: - This affects more than just device Interfaces. I tried on Circuits as well, same issue. - If I change the line 'tag': '$partner', to 'tag': 'partnera', (valid tag slug) it works as expected, for ONLY that one tag (as expected). - If I change the line 'tag': '$partner', to 'tag': 'PartnerA', (the valid Name for the above slug) it does NOT work as expected, leading to the same behavior as the dropdown. - If I comment the Partner tags section, uncomment the NNI device section, and do the same with the query_params in the NNI interface, it works as expected. My assumption is that $partner is returning something other than the slug as a value. To validate this, look at the developer tools, you will see this request XHR/fetch after you select a partner in the dropdown: ``` Summary URL: https://10.X.X.X/api/dcim/interfaces/?brief=true&limit=100&tag=8 Status: 400 Bad Request Source: Network Address: 10.X.X.X:443 Initiator: netbox.js:3:17395 ``` (IP masked for security) Taking that url in to the browser does indeed return: ``` { "tag": [ "Select a valid choice. 8 is not one of the available choices." ] } ``` Changing the URL to: https://10.X.X.X/api/dcim/interfaces/?brief=true&limit=100&tag=partnera does return a valid result: ``` HTTP 200 OK Allow: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE Content-Type: application/json Vary: Accept { "count": 2, "next": null, "previous": null, "results": [ { "id": 10506, <snip> ``` According to the documentation at: https://netboxlabs.com/docs/netbox/en/stable/reference/filtering/#filtering-objects we should be using slugs rather than ids with the API, however, it appears that all the other filters use IDs, so they are at odds with each other. Changing the query_params from tag to tag_id did not have any effect.
adam added the type: bugstatus: acceptedseverity: low labels 2025-12-29 21:36:18 +01:00
adam closed this issue 2025-12-29 21:36:18 +01:00
Author
Owner

@jeremystretch commented on GitHub (Feb 28, 2025):

I've created a simplified example for reference:

class TestScript(Script):

    assigned_tag = ObjectVar(
        model=Tag
    )
    site = ObjectVar(
        model=Site,
        query_params={
            'tag': '$assigned_tag',
        }
    )

The problem is that the assigned_tag field, being an ObjectVar, returns the numeric ID of the selected tag, but as you've surmised the tag filter for the site REST API endpoint expects a slug. Unfortunately, there is no tag_id filter, which would have allowed us to work around this limitation.

IMO the cleanest approach would be to add a tag_id filter for every model which supports tagging. (This can be achieved by extending NetBoxModelFilterSet.)

The alternative approach would require really digging into the logic for assigning field values from API responses. Even if a feasible solution was identified, it would have little value beyond this specific scenario as these fields are typically intended to capture the primary keys of related objects.

@jeremystretch commented on GitHub (Feb 28, 2025): I've created a simplified example for reference: ```python class TestScript(Script): assigned_tag = ObjectVar( model=Tag ) site = ObjectVar( model=Site, query_params={ 'tag': '$assigned_tag', } ) ``` The problem is that the `assigned_tag` field, being an ObjectVar, returns the numeric ID of the selected tag, but as you've surmised the `tag` filter for the `site` REST API endpoint expects a slug. Unfortunately, there is no `tag_id` filter, which would have allowed us to work around this limitation. IMO the cleanest approach would be to add a `tag_id` filter for every model which supports tagging. (This can be achieved by extending NetBoxModelFilterSet.) The alternative approach would require really digging into the logic for assigning field values from API responses. Even if a feasible solution was identified, it would have little value beyond this specific scenario as these fields are typically intended to capture the primary keys of related objects.
Author
Owner

@jeremystretch commented on GitHub (Feb 28, 2025):

IMO the cleanest approach would be to add a tag_id filter for every model which supports tagging. (This can be achieved by extending NetBoxModelFilterSet.)

I've proposed this functionality in FR #18783.

@jeremystretch commented on GitHub (Feb 28, 2025): IMO the cleanest approach would be to add a `tag_id` filter for every model which supports tagging. (This can be achieved by extending NetBoxModelFilterSet.) I've proposed this functionality in FR #18783.
Author
Owner

@jeremystretch commented on GitHub (Mar 12, 2025):

Blocked by #18783

@jeremystretch commented on GitHub (Mar 12, 2025): Blocked by #18783
Author
Owner

@jeremystretch commented on GitHub (Apr 22, 2025):

A tag_id filter has been introduced for all applicable models in NetBox v4.3 under #18783, so this should now be possible.

@jeremystretch commented on GitHub (Apr 22, 2025): A `tag_id` filter has been introduced for all applicable models in NetBox v4.3 under #18783, so this should now be possible.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#10820