1717from extras .models .customfields import CustomField
1818
1919from .common import UnresolvedReference
20+ from .compat import in_version_range
2021from .plugin_utils import content_type_id , get_object_type , get_object_type_model
2122
2223logger = logging .getLogger (__name__ )
162163 name = "logical_service_name_no_device_or_vm" ,
163164 model_class = get_object_type_model ("ipam.service" ),
164165 condition = Q (device__isnull = True , virtual_machine__isnull = True ),
166+ max_version = "4.2.99" ,
165167 ),
166168 ObjectMatchCriteria (
167169 fields = ("name" , "device" ),
168170 name = "logical_service_name_on_device" ,
169171 model_class = get_object_type_model ("ipam.service" ),
170172 condition = Q (device__isnull = False ),
173+ max_version = "4.2.99" ,
171174 ),
172175 ObjectMatchCriteria (
173176 fields = ("name" , "virtual_machine" ),
174177 name = "logical_service_name_on_vm" ,
175178 model_class = get_object_type_model ("ipam.service" ),
176179 condition = Q (virtual_machine__isnull = False ),
180+ max_version = "4.2.99" ,
181+ ),
182+ ObjectMatchCriteria (
183+ fields = ("name" , "parent_object_type" , "parent_object_id" ),
184+ name = "logical_service_name_on_parent" ,
185+ model_class = get_object_type_model ("ipam.service" ),
186+ condition = Q (parent_object_type__isnull = False ),
187+ min_version = "4.3.0"
177188 ),
178189 ],
179190 "dcim.modulebay" : lambda : [
201212 model_class = get_object_type_model ("ipam.fhrpgroup" ),
202213 )
203214 ],
215+ "tenancy.contact" : lambda : [
216+ ObjectMatchCriteria (
217+ # contacts are unconstrained in 4.3.0
218+ # in 4.2 they are constrained by unique name per group
219+ fields = ("name" , ),
220+ name = "logical_contact_name" ,
221+ model_class = get_object_type_model ("tenancy.contact" ),
222+ min_version = "4.3.0" ,
223+ )
224+ ],
225+ "dcim.devicerole" : lambda : [
226+ ObjectMatchCriteria (
227+ fields = ("name" ,),
228+ name = "logical_device_role_name_no_parent" ,
229+ model_class = get_object_type_model ("dcim.devicerole" ),
230+ condition = Q (parent__isnull = True ),
231+ min_version = "4.3.0" ,
232+ ),
233+ ObjectMatchCriteria (
234+ fields = ("slug" ,),
235+ name = "logical_device_role_slug_no_parent" ,
236+ model_class = get_object_type_model ("dcim.devicerole" ),
237+ condition = Q (parent__isnull = True ),
238+ min_version = "4.3.0" ,
239+ )
240+ ],
204241}
205242
206243@dataclass
@@ -223,6 +260,9 @@ class ObjectMatchCriteria:
223260 model_class : type [models .Model ] | None = None
224261 name : str | None = None
225262
263+ min_version : str | None = None
264+ max_version : str | None = None
265+
226266 def __hash__ (self ):
227267 """Hash the object match criteria."""
228268 return hash ((self .fields , self .expressions , self .condition , self .model_class .__name__ , self .name ))
@@ -414,6 +454,9 @@ class CustomFieldMatcher:
414454 custom_field : str
415455 model_class : type [models .Model ]
416456
457+ min_version : str | None = None
458+ max_version : str | None = None
459+
417460 def fingerprint (self , data : dict ) -> str | None :
418461 """Fingerprint the custom field value."""
419462 if not self .has_required_fields (data ):
@@ -450,6 +493,9 @@ class GlobalIPNetworkIPMatcher:
450493 model_class : type [models .Model ]
451494 name : str
452495
496+ min_version : str | None = None
497+ max_version : str | None = None
498+
453499 def _check_condition (self , data : dict ) -> bool :
454500 """Check the condition for the custom field."""
455501 return data .get (self .vrf_field , None ) is None
@@ -510,6 +556,9 @@ class VRFIPNetworkIPMatcher:
510556 model_class : type [models .Model ]
511557 name : str
512558
559+ min_version : str | None = None
560+ max_version : str | None = None
561+
513562 def _check_condition (self , data : dict ) -> bool :
514563 """Check the condition for the custom field."""
515564 return data .get (self .vrf_field , None ) is not None
@@ -584,6 +633,9 @@ class AutoSlugMatcher:
584633 slug_field : str
585634 model_class : type [models .Model ]
586635
636+ min_version : str | None = None
637+ max_version : str | None = None
638+
587639 def fingerprint (self , data : dict ) -> str | None :
588640 """Fingerprint the custom field value."""
589641 if not self .has_required_fields (data ):
@@ -649,7 +701,10 @@ def _get_autoslug_matchers(model_class) -> list:
649701@lru_cache (maxsize = 256 )
650702def _get_model_matchers (model_class ) -> list [ObjectMatchCriteria ]:
651703 object_type = get_object_type (model_class )
652- matchers = _LOGICAL_MATCHERS .get (object_type , lambda : [])()
704+ matchers = [
705+ x for x in _LOGICAL_MATCHERS .get (object_type , lambda : [])()
706+ if in_version_range (x .min_version , x .max_version )
707+ ]
653708
654709 # collect single fields that are unique
655710 for field in model_class ._meta .fields :
0 commit comments