Introduce a custom form field to represent generic foreign key relations #11348

Open
opened 2025-12-29 21:44:01 +01:00 by adam · 0 comments
Owner

Originally created by @jeremystretch on GitHub (Jul 7, 2025).

NetBox version

v4.3.3

Feature type

New functionality

Proposed functionality

Devise a new type of form field and widget capable of setting a GFK relation on an object. This is currently achieved by using two independent form fields which map to the two concrete model fields supporting the GFK (e.g. content_type and object_id).

Some quick prototyping suggests the new widget would look similar to the following:

from django import forms

class GenericForeignKeyWidget(forms.MultiWidget):
    def __init__(self, content_type_choices=None, attrs=None):
        widgets = [
            forms.Select(choices=content_type_choices),
            forms.TextInput(attrs={'placeholder': 'Object ID'}),
        ]
        super().__init__(widgets, attrs)

    def decompress(self, value):
        if value and hasattr(value, '_meta'):
            ct_id = ContentType.objects.get_for_model(value).pk
            return [ct_id, value.pk]
        return [None, None]

And the corresponding form field might look like this:

from django import forms
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError

class GenericForeignKeyFormField(forms.MultiValueField):
    def __init__(self, content_type_choices=None, **kwargs):
        fields = [
            forms.ChoiceField(choices=content_type_choices),
            forms.CharField()
        ]
        widget = GenericForeignKeyWidget(content_type_choices)
        super().__init__(fields=fields, widget=widget, **kwargs)

    def compress(self, data_list):
        if data_list and all(data_list):
            ct_id, obj_id = data_list
            try:
                content_type = ContentType.objects.get(pk=ct_id)
                model_class = content_type.model_class()
                return model_class.objects.get(pk=obj_id)
            except Exception as e:
                raise ValidationError(f"Invalid GenericForeignKey: {e}")
        return None

These examples will need to be modified to employ NetBox's REST API for object selection.

Use case

If proved viable, this approach will greatly simplify GFK relation handling within model forms by obviating the need to declare two form fields, and by reducing the amount of custom logic needed within each form's __init__() and save() methods.

Database changes

N/A

External dependencies

N/A

Originally created by @jeremystretch on GitHub (Jul 7, 2025). ### NetBox version v4.3.3 ### Feature type New functionality ### Proposed functionality Devise a new type of form field and widget capable of setting a GFK relation on an object. This is currently achieved by using two independent form fields which map to the two concrete model fields supporting the GFK (e.g. `content_type` and `object_id`). Some quick prototyping suggests the new widget would look similar to the following: ```python from django import forms class GenericForeignKeyWidget(forms.MultiWidget): def __init__(self, content_type_choices=None, attrs=None): widgets = [ forms.Select(choices=content_type_choices), forms.TextInput(attrs={'placeholder': 'Object ID'}), ] super().__init__(widgets, attrs) def decompress(self, value): if value and hasattr(value, '_meta'): ct_id = ContentType.objects.get_for_model(value).pk return [ct_id, value.pk] return [None, None] ``` And the corresponding form field might look like this: ```python from django import forms from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError class GenericForeignKeyFormField(forms.MultiValueField): def __init__(self, content_type_choices=None, **kwargs): fields = [ forms.ChoiceField(choices=content_type_choices), forms.CharField() ] widget = GenericForeignKeyWidget(content_type_choices) super().__init__(fields=fields, widget=widget, **kwargs) def compress(self, data_list): if data_list and all(data_list): ct_id, obj_id = data_list try: content_type = ContentType.objects.get(pk=ct_id) model_class = content_type.model_class() return model_class.objects.get(pk=obj_id) except Exception as e: raise ValidationError(f"Invalid GenericForeignKey: {e}") return None ``` These examples will need to be modified to employ NetBox's REST API for object selection. ### Use case If proved viable, this approach will greatly simplify GFK relation handling within model forms by obviating the need to declare two form fields, and by reducing the amount of custom logic needed within each form's `__init__()` and `save()` methods. ### Database changes N/A ### External dependencies N/A
adam added the type: featurenetboxneeds milestonestatus: backlogcomplexity: high labels 2025-12-29 21:44:01 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11348