diff --git a/netbox_custom_objects/templates/netbox_custom_objects/customobject.html b/netbox_custom_objects/templates/netbox_custom_objects/customobject.html
index b69d828..b796961 100644
--- a/netbox_custom_objects/templates/netbox_custom_objects/customobject.html
+++ b/netbox_custom_objects/templates/netbox_custom_objects/customobject.html
@@ -102,19 +102,26 @@
{% endif %}
- {% for field in fields %}
- {% with is_visible_in_ui=object|get_field_is_ui_visible:field %}
- {% if field.is_single_value and is_visible_in_ui %}
-
- {{ field }} |
-
- {% with customfield=field value=object|get_field_value:field %}
- {% include "builtins/customfield_value.html" %}
- {% endwith %}
- |
-
- {% endif %}
- {% endwith %}
+ {% for group_name, group_fields in field_groups.items %}
+ {% if group_name %}
+
+ {% endif %}
+ {% for field in group_fields %}
+ {% with is_visible_in_ui=object|get_field_is_ui_visible:field %}
+ {% if field.is_single_value and is_visible_in_ui %}
+
+ {{ field }} |
+
+ {% with customfield=field value=object|get_field_value:field %}
+ {% include "builtins/customfield_value.html" %}
+ {% endwith %}
+ |
+
+ {% endif %}
+ {% endwith %}
+ {% endfor %}
{% endfor %}
@@ -123,12 +130,15 @@
{% include 'inc/panels/tags.html' %}
{% plugin_right_page object %}
- {% for field in fields %}
+ {% for group_name, group_fields in field_groups.items %}
+ {% for field in group_fields %}
{% if field.many %}
{% with field_values=object|get_child_relations:field is_visible_in_ui=object|get_field_is_ui_visible:field %}
{% if is_visible_in_ui %}
-
+
{% for relation in field_values.all %}
@@ -140,6 +150,7 @@
{% endif %}
{% endwith %}
{% endif %}
+ {% endfor %}
{% endfor %}
diff --git a/netbox_custom_objects/views.py b/netbox_custom_objects/views.py
index 8a81acf..bcec4ff 100644
--- a/netbox_custom_objects/views.py
+++ b/netbox_custom_objects/views.py
@@ -18,8 +18,12 @@
from netbox.views.generic.mixins import TableMixin
from utilities.forms import ConfirmationForm
from utilities.htmx import htmx_partial
-from utilities.views import (ConditionalLoginRequiredMixin, ViewTab,
- get_viewname, register_model_view)
+from utilities.views import (
+ ConditionalLoginRequiredMixin,
+ ViewTab,
+ get_viewname,
+ register_model_view,
+)
from netbox_custom_objects.tables import CustomObjectTable
@@ -33,7 +37,7 @@ class CustomJournalEntryForm(JournalEntryForm):
"""
def __init__(self, *args, **kwargs):
- self.custom_object = kwargs.pop('custom_object', None)
+ self.custom_object = kwargs.pop("custom_object", None)
super().__init__(*args, **kwargs)
def get_return_url(self):
@@ -41,10 +45,13 @@ def get_return_url(self):
Override to return the correct URL for custom objects.
"""
if self.custom_object:
- return reverse('plugins:netbox_custom_objects:customobject_journal', kwargs={
- 'custom_object_type': self.custom_object.custom_object_type.name,
- 'pk': self.custom_object.pk
- })
+ return reverse(
+ "plugins:netbox_custom_objects:customobject_journal",
+ kwargs={
+ "custom_object_type": self.custom_object.custom_object_type.name,
+ "pk": self.custom_object.pk,
+ },
+ )
return super().get_return_url()
@@ -52,6 +59,7 @@ class CustomJournalEntryEditView(generic.ObjectEditView):
"""
Custom journal entry edit view that handles return URLs for custom objects.
"""
+
queryset = JournalEntry.objects.all()
form = CustomJournalEntryForm
@@ -59,18 +67,23 @@ def get_return_url(self, request, instance):
"""
Override to return the correct URL for custom objects.
"""
- if instance.assigned_object and hasattr(instance.assigned_object, 'custom_object_type'):
+ if instance.assigned_object and hasattr(
+ instance.assigned_object, "custom_object_type"
+ ):
# This is a custom object
- return reverse('plugins:netbox_custom_objects:customobject_journal', kwargs={
- 'custom_object_type': instance.assigned_object.custom_object_type.name,
- 'pk': instance.assigned_object.pk
- })
+ return reverse(
+ "plugins:netbox_custom_objects:customobject_journal",
+ kwargs={
+ "custom_object_type": instance.assigned_object.custom_object_type.name,
+ "pk": instance.assigned_object.pk,
+ },
+ )
# Fall back to standard behavior for non-custom objects
if not instance.assigned_object:
- return reverse('extras:journalentry_list')
+ return reverse("extras:journalentry_list")
obj = instance.assigned_object
- viewname = get_viewname(obj, 'journal')
- return reverse(viewname, kwargs={'pk': obj.pk})
+ viewname = get_viewname(obj, "journal")
+ return reverse(viewname, kwargs={"pk": obj.pk})
class CustomObjectTableMixin(TableMixin):
@@ -391,9 +404,21 @@ def get_object(self, **kwargs):
return get_object_or_404(model.objects.all(), **lookup_kwargs)
def get_extra_context(self, request, instance):
- fields = instance.custom_object_type.fields.all().order_by("weight")
+ fields = instance.custom_object_type.fields.all().order_by(
+ "group_name", "weight", "name"
+ )
+
+ # Group fields by group_name
+ field_groups = {}
+ for field in fields:
+ group_name = field.group_name or None # Use None for ungrouped fields
+ if group_name not in field_groups:
+ field_groups[group_name] = []
+ field_groups[group_name].append(field)
+
return {
"fields": fields,
+ "field_groups": field_groups,
}
@@ -631,7 +656,7 @@ def get(self, request, custom_object_type, **kwargs):
initial={
"assigned_object_type": content_type,
"assigned_object_id": obj.pk,
- }
+ },
)
else:
form = None
@@ -649,7 +674,9 @@ def get(self, request, custom_object_type, **kwargs):
"table": journal_table,
"base_template": self.base_template,
"tab": "journal",
- "form_action": reverse('plugins:netbox_custom_objects:custom_journalentry_add'),
+ "form_action": reverse(
+ "plugins:netbox_custom_objects:custom_journalentry_add"
+ ),
},
)