@@ -77,7 +77,7 @@ def post_delete_receiver(sender, instance, origin, **kwargs):
7777 parent_pk = getattr (instance , field_name , None )
7878
7979 # Decrement the parent's counter by one
80- if parent_pk is not None and not hasattr (instance , " _previously_removed" ):
80+ if parent_pk is not None and not hasattr (instance , ' _previously_removed' ):
8181 update_counter (parent_model , parent_pk , counter_name , - 1 )
8282
8383
@@ -87,38 +87,48 @@ def post_delete_receiver(sender, instance, origin, **kwargs):
8787
8888def connect_counters (* models ):
8989 """
90- Register counter fields and connect post_save & post_delete signal handlers for the affected models.
90+ Register counter fields and connect signal handlers for their child models.
91+ Ensures exactly one receiver per child (sender), even when multiple counters
92+ reference the same sender (e.g., Device).
9193 """
92- for model in models :
94+ connected = set () # child models we've already connected
9395
96+ for model in models :
9497 # Find all CounterCacheFields on the model
95- counter_fields = [
96- field for field in model ._meta .get_fields () if type (field ) is CounterCacheField
97- ]
98+ counter_fields = [field for field in model ._meta .get_fields () if isinstance (field , CounterCacheField )]
9899
99100 for field in counter_fields :
100101 to_model = apps .get_model (field .to_model_name )
101102
102103 # Register the counter in the registry
103104 change_tracking_fields = registry ['counter_fields' ][to_model ]
104- change_tracking_fields [f"{ field .to_field_name } _id" ] = field .name
105+ change_tracking_fields [f'{ field .to_field_name } _id' ] = field .name
106+
107+ # Connect signals once per child model
108+ if to_model in connected :
109+ continue
110+
111+ # Ensure dispatch_uid is unique per model (sender), not per field
112+ uid_base = f'countercache.{ to_model ._meta .label_lower } '
105113
106114 # Connect the post_save and post_delete handlers
107115 post_save .connect (
108116 post_save_receiver ,
109117 sender = to_model ,
110118 weak = False ,
111- dispatch_uid = f'{ model . _meta . label } . { field . name } '
119+ dispatch_uid = f'{ uid_base } .post_save' ,
112120 )
113121 pre_delete .connect (
114122 pre_delete_receiver ,
115123 sender = to_model ,
116124 weak = False ,
117- dispatch_uid = f'{ model . _meta . label } . { field . name } '
125+ dispatch_uid = f'{ uid_base } .pre_delete' ,
118126 )
119127 post_delete .connect (
120128 post_delete_receiver ,
121129 sender = to_model ,
122130 weak = False ,
123- dispatch_uid = f'{ model . _meta . label } . { field . name } '
131+ dispatch_uid = f'{ uid_base } .post_delete' ,
124132 )
133+
134+ connected .add (to_model )
0 commit comments