Skip to content

Commit aaaac0b

Browse files
committed
Refactor get_object
1 parent 30b4a99 commit aaaac0b

File tree

3 files changed

+91
-8
lines changed

3 files changed

+91
-8
lines changed

netbox_custom_objects/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,31 @@ class CustomObjectsPluginConfig(PluginConfig):
5252
required_settings = []
5353
template_extensions = "template_content.template_extensions"
5454

55+
def ready(self):
56+
from .models import CustomObjectType
57+
from netbox_custom_objects.api.serializers import get_serializer_class
58+
59+
# Suppress warnings about database calls during app initialization
60+
with warnings.catch_warnings():
61+
warnings.filterwarnings(
62+
"ignore", category=RuntimeWarning, message=".*database.*"
63+
)
64+
warnings.filterwarnings(
65+
"ignore", category=UserWarning, message=".*database.*"
66+
)
67+
68+
# Skip database calls if running during migration or if table doesn't exist
69+
if is_running_migration() or not check_custom_object_type_table_exists():
70+
super().ready()
71+
return
72+
73+
qs = CustomObjectType.objects.all()
74+
for obj in qs:
75+
model = obj.get_model()
76+
get_serializer_class(model)
77+
78+
super().ready()
79+
5580
def get_model(self, model_name, require_ready=True):
5681
if "table" in model_name.lower() and "model" in model_name.lower():
5782
is_custom_object_model = True

netbox_custom_objects/models.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,6 @@ def register_custom_object_search_index(self, model):
493493
def get_model(
494494
self,
495495
fields=None,
496-
app_label=None,
497496
skip_object_fields=False,
498497
no_cache=False,
499498
_generating_models=None,
@@ -504,10 +503,6 @@ def get_model(
504503
505504
:param fields: Extra table field instances that need to be added the model.
506505
:type fields: list
507-
:param app_label: In some cases with related fields, the related models must
508-
have the same app_label. If passed along in this parameter, then the
509-
generated model will use that one instead of generating a unique one.
510-
:type app_label: Optional[String]
511506
:param skip_object_fields: Don't add object or multiobject fields to the model
512507
:type skip_object_fields: bool
513508
:param no_cache: Don't cache the generated model or attempt to pull from cache
@@ -517,8 +512,6 @@ def get_model(
517512
:return: The generated model.
518513
:rtype: Model
519514
"""
520-
if app_label is None:
521-
app_label = APP_LABEL
522515

523516
# Check if we have a cached model for this CustomObjectType
524517
model_name = self.get_table_model_name(self.pk).lower()
@@ -634,10 +627,10 @@ def wrapped_post_through_setup(self, cls):
634627
from netbox_custom_objects.api.serializers import get_serializer_class
635628
636629
get_serializer_class(model)
637-
'''
638630
639631
# Register the global SearchIndex for this model
640632
self.register_custom_object_search_index(model)
633+
'''
641634

642635
# Clean up: remove this model from the set of models being generated
643636
if _generating_models is not None:

netbox_custom_objects/utilities.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"AppsProxy",
99
"generate_model",
1010
"get_viewname",
11+
"get_sub_models",
1112
)
1213

1314

@@ -108,3 +109,67 @@ def generate_model(*args, **kwargs):
108109
apps.clear_cache = apps.clear_cache
109110

110111
return model
112+
113+
114+
def get_sub_models(custom_object, visited=None):
115+
"""
116+
Recursively find all related custom object models for a given CustomObject instance.
117+
118+
This function traverses through the custom object type's fields and finds all
119+
related custom object types that are referenced through OBJECT or MULTIOBJECT fields.
120+
121+
Args:
122+
custom_object: An instance of a dynamically created CustomObject class
123+
visited: Set of custom object type IDs already visited (used for cycle detection)
124+
125+
Returns:
126+
list: A list of model classes (including the original model) that are related
127+
through custom object relationships
128+
"""
129+
from netbox_custom_objects.models import CustomObjectType
130+
models = [custom_object]
131+
if visited is None:
132+
visited = set()
133+
134+
# Get the custom object type from the instance
135+
custom_object_type = custom_object.custom_object_type
136+
137+
# If we've already visited this type, return empty list to prevent infinite recursion
138+
if custom_object_type.id in visited:
139+
return []
140+
141+
# Add this type to visited set
142+
visited.add(custom_object_type.id)
143+
144+
# Get all fields of type OBJECT or MULTIOBJECT
145+
# Using string constants to avoid import issues
146+
object_fields = custom_object_type.fields.filter(
147+
type__in=["object", "multiobject"],
148+
related_object_type__isnull=False,
149+
related_object_type__app_label=APP_LABEL
150+
)
151+
152+
# For each object field, check if it references another custom object
153+
for field in object_fields:
154+
related_object_type = field.related_object_type
155+
156+
# Get the related custom object type
157+
related_custom_object_type = CustomObjectType.objects.get(
158+
object_type=related_object_type
159+
)
160+
161+
# Get the model for the related custom object type
162+
related_model = related_custom_object_type.get_model()
163+
164+
# Recursively get sub-models from the related type
165+
sub_models = get_sub_models(related_model(), visited.copy())
166+
167+
# Add the related model and its sub-models to our list
168+
if related_model not in models:
169+
models.append(related_model)
170+
171+
for sub_model in sub_models:
172+
if sub_model not in models:
173+
models.append(sub_model)
174+
175+
return models

0 commit comments

Comments
 (0)