@@ -360,16 +360,28 @@ def add(cls, ci_type_name,
360360 _sync = False ,
361361 ** ci_dict ):
362362 """
363- add ci
364- :param ci_type_name:
365- :param exist_policy: replace or reject or need
366- :param _no_attribute_policy: ignore or reject
367- :param is_auto_discovery: default is False
368- :param _is_admin: default is False
369- :param ticket_id:
370- :param _sync:
371- :param ci_dict:
372- :return:
363+ Create a new Configuration Item (CI) or update existing based on unique constraints.
364+
365+ Handles complete CI creation workflow including validation, uniqueness checks,
366+ password encryption, computed attributes, relationship creation, and caching.
367+
368+ Args:
369+ ci_type_name (str): Name of the CI type to create
370+ exist_policy (ExistPolicy): How to handle existing CIs (REPLACE/REJECT/NEED)
371+ _no_attribute_policy (ExistPolicy): How to handle unknown attributes (IGNORE/REJECT)
372+ is_auto_discovery (bool): Whether CI is created by auto-discovery process
373+ _is_admin (bool): Whether to skip permission checks
374+ ticket_id (int, optional): Associated ticket ID for audit trail
375+ _sync (bool): Whether to execute cache/relation tasks synchronously
376+ **ci_dict: CI attribute values as key-value pairs
377+
378+ Returns:
379+ int: ID of the created or updated CI
380+
381+ Raises:
382+ 400: If unique constraints violated, required attributes missing, or validation fails
383+ 403: If user lacks permissions for restricted attributes
384+ 404: If CI type not found or referenced CI not exists
373385 """
374386 now = datetime .datetime .now ().strftime ('%Y-%m-%d %H:%M:%S' )
375387 ci_type = CITypeManager .check_is_existed (ci_type_name )
@@ -512,6 +524,24 @@ def add(cls, ci_type_name,
512524 return ci .id
513525
514526 def update (self , ci_id , _is_admin = False , ticket_id = None , _sync = False , ** ci_dict ):
527+ """
528+ Update an existing Configuration Item with new attribute values.
529+
530+ Performs comprehensive CI update including validation, constraint checks,
531+ password handling, computed attributes processing, and change tracking.
532+
533+ Args:
534+ ci_id (int): ID of the CI to update
535+ _is_admin (bool): Whether to skip permission checks
536+ ticket_id (int, optional): Associated ticket ID for audit trail
537+ _sync (bool): Whether to execute cache/relation tasks synchronously
538+ **ci_dict: CI attribute values to update as key-value pairs
539+
540+ Raises:
541+ 400: If unique constraints violated or validation fails
542+ 403: If user lacks permissions for restricted attributes
543+ 404: If CI not found
544+ """
515545 now = datetime .datetime .now ().strftime ('%Y-%m-%d %H:%M:%S' )
516546 ci = self .confirm_ci_existed (ci_id )
517547 ci_type = ci .ci_type
@@ -1420,14 +1450,31 @@ def build_by_attribute(cls, ci_dict):
14201450 parent_attr = AttributeCache .get (parent_attr_id )
14211451 child_attr = AttributeCache .get (child_attr_id )
14221452 attr_value = ci_dict .get (parent_attr .name )
1453+ if attr_value != 0 and not attr_value :
1454+ continue
14231455 value_table = TableMap (attr = child_attr ).table
1424- for child in value_table .get_by (attr_id = child_attr .id , value = attr_value , only_query = True ).join (
1425- CI , CI .id == value_table .ci_id ).filter (CI .type_id == item .child_id ):
1426- _relations .add ((ci_dict ['_id' ], child .ci_id ))
1456+ attr_value_list = [attr_value ] if not isinstance (attr_value , list ) else attr_value
1457+
1458+ matching_cis = value_table .get_by (
1459+ attr_id = child_attr .id ,
1460+ only_query = True
1461+ ).join (
1462+ CI , CI .id == value_table .ci_id
1463+ ).filter (
1464+ CI .type_id == item .child_id ,
1465+ value_table .value .in_ (attr_value_list )
1466+ ).all ()
1467+
1468+ for ci in matching_cis :
1469+ _relations .add ((ci_dict ['_id' ], ci .ci_id ))
1470+
14271471 if relations is None :
14281472 relations = _relations
14291473 else :
1430- relations &= _relations
1474+ if item .constraint == ConstraintEnum .Many2Many :
1475+ relations |= _relations
1476+ else :
1477+ relations &= _relations
14311478
14321479 cls .delete_relations_by_source (RelationSourceEnum .ATTRIBUTE_VALUES ,
14331480 first_ci_id = ci_dict ['_id' ],
@@ -1447,14 +1494,31 @@ def build_by_attribute(cls, ci_dict):
14471494 parent_attr = AttributeCache .get (parent_attr_id )
14481495 child_attr = AttributeCache .get (child_attr_id )
14491496 attr_value = ci_dict .get (child_attr .name )
1497+ if attr_value != 0 and not attr_value :
1498+ continue
14501499 value_table = TableMap (attr = parent_attr ).table
1451- for parent in value_table .get_by (attr_id = parent_attr .id , value = attr_value , only_query = True ).join (
1452- CI , CI .id == value_table .ci_id ).filter (CI .type_id == item .parent_id ):
1453- _relations .add ((parent .ci_id , ci_dict ['_id' ]))
1500+ attr_value_list = [attr_value ] if not isinstance (attr_value , list ) else attr_value
1501+
1502+ matching_cis = value_table .get_by (
1503+ attr_id = parent_attr .id ,
1504+ only_query = True
1505+ ).join (
1506+ CI , CI .id == value_table .ci_id
1507+ ).filter (
1508+ CI .type_id == item .parent_id ,
1509+ value_table .value .in_ (attr_value_list )
1510+ ).all ()
1511+
1512+ for ci in matching_cis :
1513+ _relations .add ((ci .ci_id , ci_dict ['_id' ]))
1514+
14541515 if relations is None :
14551516 relations = _relations
14561517 else :
1457- relations &= _relations
1518+ if item .constraint == ConstraintEnum .Many2Many :
1519+ relations |= _relations
1520+ else :
1521+ relations &= _relations
14581522
14591523 cls .delete_relations_by_source (RelationSourceEnum .ATTRIBUTE_VALUES ,
14601524 second_ci_id = ci_dict ['_id' ],
0 commit comments