11import decimal
22import re
3- import warnings
43from datetime import date , datetime
54
65import django_filters
5150
5251from netbox_custom_objects .constants import APP_LABEL , RESERVED_FIELD_NAMES
5352from netbox_custom_objects .field_types import FIELD_TYPE_CLASS
53+ from netbox_custom_objects .utilities import generate_model
5454
5555
5656class UniquenessConstraintTestError (Exception ):
@@ -421,6 +421,9 @@ def _after_model_generation(self, attrs, model):
421421 # Get the set of fields that were skipped due to recursion
422422 skipped_fields = attrs .get ("_skipped_fields" , set ())
423423
424+ # Collect through models during after_model_generation
425+ through_models = []
426+
424427 for field_object in all_field_objects .values ():
425428 field_name = field_object ["name" ]
426429
@@ -432,15 +435,28 @@ def _after_model_generation(self, attrs, model):
432435 # Fields might be skipped due to recursion prevention
433436 if hasattr (model ._meta , 'get_field' ):
434437 try :
435- model ._meta .get_field (field_name )
438+ field = model ._meta .get_field (field_name )
436439 # Field exists, process it
437440 field_object ["type" ].after_model_generation (
438441 field_object ["field" ], model , field_name
439442 )
443+
444+ # Collect through models from M2M fields
445+ if hasattr (field , 'remote_field' ) and hasattr (field .remote_field , 'through' ):
446+ through_model = field .remote_field .through
447+ # Only collect custom through models, not auto-created Django ones
448+ if (through_model and through_model not in through_models and
449+ hasattr (through_model ._meta , 'app_label' ) and
450+ through_model ._meta .app_label == APP_LABEL ):
451+ through_models .append (through_model )
452+
440453 except Exception :
441454 # Field doesn't exist (likely skipped due to recursion), skip processing
442455 continue
443456
457+ # Store through models on the model for yielding in get_models()
458+ model ._through_models = through_models
459+
444460 def get_collision_safe_order_id_idx_name (self ):
445461 return f"tbl_order_id_{ self .id } _idx"
446462
@@ -601,22 +617,14 @@ def wrapped_post_through_setup(self, cls):
601617
602618 TM .post_through_setup = wrapped_post_through_setup
603619
604- # Suppress RuntimeWarning about model already being registered
605- # TODO: Remove this once we have a better way to handle model registration
606- with warnings . catch_warnings ():
607- warnings . filterwarnings (
608- "ignore" , category = RuntimeWarning , message = ".*was already registered.*"
620+ try :
621+ model = generate_model (
622+ str ( model_name ),
623+ ( CustomObject , models . Model ),
624+ attrs ,
609625 )
610-
611- try :
612- model = type (
613- str (model_name ),
614- (CustomObject , models .Model ),
615- attrs ,
616- )
617- finally :
618- # Restore the original method
619- TM .post_through_setup = original_post_through_setup
626+ finally :
627+ TM .post_through_setup = original_post_through_setup
620628
621629 # Register the main model with Django's app registry
622630 try :
@@ -634,6 +642,9 @@ def wrapped_post_through_setup(self, cls):
634642 # Cache the generated model
635643 if not no_cache :
636644 self ._model_cache [self .id ] = model
645+ # Do the clear cache now that we have it in the cache so there
646+ # is no recursion.
647+ apps .clear_cache ()
637648
638649 # Register the serializer for this model
639650 if not manytomany_models :
@@ -663,6 +674,7 @@ def create_model(self):
663674 # Ensure the ContentType exists and is immediately available
664675 ct = self .get_or_create_content_type ()
665676 features = get_model_features (model )
677+ ct .features = features + ['branching' ]
666678 ct .public = True
667679 ct .features = features
668680 ct .save ()
@@ -1423,7 +1435,7 @@ def save(self, *args, **kwargs):
14231435 "managed" : True ,
14241436 },
14251437 )
1426- old_through_model = type (
1438+ old_through_model = generate_model (
14271439 f"TempOld{ self .original .through_model_name } " ,
14281440 (models .Model ,),
14291441 {
@@ -1454,7 +1466,7 @@ def save(self, *args, **kwargs):
14541466 "managed" : True ,
14551467 },
14561468 )
1457- new_through_model = type (
1469+ new_through_model = generate_model (
14581470 f"TempNew{ self .through_model_name } " ,
14591471 (models .Model ,),
14601472 {
0 commit comments