update
This commit is contained in:
parent
e0c0e834fe
commit
4692b037a0
@ -67,6 +67,16 @@ parser.add_argument(
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Force delete cascade all records instead of archiving",
|
help="Force delete cascade all records instead of archiving",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--soft",
|
||||||
|
action="store_true",
|
||||||
|
help="Just archive the records instead of deleting",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--refresh-cache",
|
||||||
|
action="store_true",
|
||||||
|
help="Refersh the related models cache",
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
db_name = args.db_name
|
db_name = args.db_name
|
||||||
@ -85,18 +95,24 @@ color_log.Show(INFO, ("Available databases:", odoo.db.list()))
|
|||||||
try:
|
try:
|
||||||
odoo.login(db_name, USERNAME, PASSWORD)
|
odoo.login(db_name, USERNAME, PASSWORD)
|
||||||
color_log.Show(
|
color_log.Show(
|
||||||
0,
|
OK, f"Connected to {HOST}:{PORT}, DB: {db_name}, Model: {base_model}"
|
||||||
f"Connected to Odoo at {HOST}:{PORT}, Database: {db_name}, Model: {base_model}",
|
|
||||||
)
|
)
|
||||||
|
except odoorpc.error.RPCError as e:
|
||||||
|
color_log.Show(FAIL, f"Login failed: {e} (check credentials or database)")
|
||||||
|
exit(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
color_log.Show(FAIL, f"Fail to Connect to Odoo Server {e}")
|
color_log.Show(FAIL, f"Connection error: {e} (check host/port)")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
# Convert DOMAIN from string to list
|
# Convert DOMAIN from string to list
|
||||||
try:
|
try:
|
||||||
domain_filter = ast.literal_eval(domain_str)
|
domain_filter = ast.literal_eval(domain_str)
|
||||||
except Exception as e:
|
if not isinstance(domain_filter, list) or not all(
|
||||||
color_log.Show(FAIL, f"Invalid DOMAIN format: {e}")
|
isinstance(t, (tuple, list)) and len(t) == 3 for t in domain_filter
|
||||||
|
):
|
||||||
|
raise ValueError("Domain must be a list of 3-tuples")
|
||||||
|
except (ValueError, SyntaxError) as e:
|
||||||
|
color_log.Show(FAIL, f"Invalid domain format: {e}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
@ -136,6 +152,7 @@ def get_related_fields(db_name, base_model):
|
|||||||
if os.path.exists(cache_file):
|
if os.path.exists(cache_file):
|
||||||
with open(cache_file, "r") as f:
|
with open(cache_file, "r") as f:
|
||||||
related_models = json.load(f)
|
related_models = json.load(f)
|
||||||
|
color_log.Show(INFO, f"Loaded related models for {base_model} from cache.")
|
||||||
return related_models
|
return related_models
|
||||||
|
|
||||||
color_log.Show(INFO, f"Fetching related models for {base_model} from Odoo...")
|
color_log.Show(INFO, f"Fetching related models for {base_model} from Odoo...")
|
||||||
@ -145,7 +162,6 @@ def get_related_fields(db_name, base_model):
|
|||||||
all_model_ids = odoo.env["ir.model"].search([])
|
all_model_ids = odoo.env["ir.model"].search([])
|
||||||
all_models = odoo.env["ir.model"].read(all_model_ids, ["model"])
|
all_models = odoo.env["ir.model"].read(all_model_ids, ["model"])
|
||||||
model_names = [model["model"] for model in all_models]
|
model_names = [model["model"] for model in all_models]
|
||||||
|
|
||||||
# Use multiprocessing to fetch related fields
|
# Use multiprocessing to fetch related fields
|
||||||
with mp.Pool(processes=process_size) as pool:
|
with mp.Pool(processes=process_size) as pool:
|
||||||
results = pool.starmap(
|
results = pool.starmap(
|
||||||
@ -169,6 +185,7 @@ def get_related_fields(db_name, base_model):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
# Experimental function
|
||||||
def delete_records_cascade(
|
def delete_records_cascade(
|
||||||
record_ids, model_name, process_count, related_models, db_name
|
record_ids, model_name, process_count, related_models, db_name
|
||||||
):
|
):
|
||||||
@ -211,7 +228,9 @@ def delete_records_cascade(
|
|||||||
)
|
)
|
||||||
process_count += 1
|
process_count += 1
|
||||||
except odoorpc.error.RPCError as e:
|
except odoorpc.error.RPCError as e:
|
||||||
color_log.Show(NOTICE, f"Access denied for model {related_model}: {e}")
|
color_log.Show(
|
||||||
|
NOTICE, f"Access denied for model {related_model}: {e}"
|
||||||
|
)
|
||||||
skipped_models_cache.add(related_model) # Add to cache
|
skipped_models_cache.add(related_model) # Add to cache
|
||||||
break # Skip further processing for this model
|
break # Skip further processing for this model
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -244,6 +263,33 @@ def delete_records_cascade(
|
|||||||
color_log.Show(FAIL, f"Error deleting {model_name} records: {e}")
|
color_log.Show(FAIL, f"Error deleting {model_name} records: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def unreference_records(record_id, model_name, process_count, related_models):
|
||||||
|
"""Force delete a record and its related records."""
|
||||||
|
for related_model, fields in related_models.items():
|
||||||
|
related_model_obj = odoo.env[related_model]
|
||||||
|
for field in fields:
|
||||||
|
try:
|
||||||
|
# Find records in the related model that reference the record_id
|
||||||
|
related_ids = related_model_obj.search([(field, "=", record_id)])
|
||||||
|
if related_ids:
|
||||||
|
# Unreference the record_id by setting the field to False
|
||||||
|
related_model_obj.browse(related_ids).write({field: False})
|
||||||
|
color_log.Show(
|
||||||
|
OK,
|
||||||
|
f"Unreferenced {record_id} in {related_model} ({field}) for {len(related_ids)} records.",
|
||||||
|
)
|
||||||
|
except odoorpc.error.RPCError as e:
|
||||||
|
color_log.Show(
|
||||||
|
NOTICE,
|
||||||
|
f"Access denied while unreferencing {record_id} in {related_model} ({field}): {e}",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
color_log.Show(
|
||||||
|
NOTICE,
|
||||||
|
f"Error while unreferencing {record_id} in {related_model} ({field}): {e}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Function to delete records in parallel
|
# Function to delete records in parallel
|
||||||
def delete_record(batch, model_name, process_count, related_models):
|
def delete_record(batch, model_name, process_count, related_models):
|
||||||
"""Process a batch of records - archive or delete based on references."""
|
"""Process a batch of records - archive or delete based on references."""
|
||||||
@ -266,13 +312,31 @@ def delete_record(batch, model_name, process_count, related_models):
|
|||||||
color_log.Show(
|
color_log.Show(
|
||||||
INFO, f"{process_count}: Archived {model_name} ID {record_id}"
|
INFO, f"{process_count}: Archived {model_name} ID {record_id}"
|
||||||
)
|
)
|
||||||
else:
|
if (not is_referenced and not args.soft) or args.force:
|
||||||
model.unlink([record_id])
|
model.unlink([record_id])
|
||||||
deleted_count += 1
|
deleted_count += 1
|
||||||
color_log.Show(
|
color_log.Show(
|
||||||
INFO, f"{process_count}: Deleted {model_name} ID {record_id}"
|
INFO, f"{process_count}: Deleted {model_name} ID {record_id}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if args.force:
|
||||||
|
try:
|
||||||
|
unreference_records(
|
||||||
|
record_id, model_name, process_count, related_models
|
||||||
|
)
|
||||||
|
model.unlink([record_id])
|
||||||
|
archived_count += 1
|
||||||
|
color_log.Show(
|
||||||
|
INFO,
|
||||||
|
f"{process_count}: Force deleted {model_name} ID {record_id}",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
skipped_count += 1
|
||||||
|
color_log.Show(
|
||||||
|
NOTICE,
|
||||||
|
f"{process_count}: Skipped {model_name} ID {record_id} after force delete attempt: {e}",
|
||||||
|
)
|
||||||
|
else:
|
||||||
skipped_count += 1
|
skipped_count += 1
|
||||||
color_log.Show(
|
color_log.Show(
|
||||||
INFO, f"{process_count}: Skipped {model_name} ID {record_id}: {e}"
|
INFO, f"{process_count}: Skipped {model_name} ID {record_id}: {e}"
|
||||||
@ -304,27 +368,14 @@ def main():
|
|||||||
for i in range(0, len(record_ids), process_size)
|
for i in range(0, len(record_ids), process_size)
|
||||||
]
|
]
|
||||||
processes = []
|
processes = []
|
||||||
if args.force:
|
|
||||||
delete_records_cascade(record_ids, base_model, 0, related_models, db_name)
|
|
||||||
# for i, batch in enumerate(batch_list, start=1):
|
|
||||||
# process = mp.Process(
|
|
||||||
# target=delete_records_cascade,
|
|
||||||
# args=(batch, base_model, f"Process-{i}", related_models,db_name),
|
|
||||||
# )
|
|
||||||
# processes.append(process)
|
|
||||||
# process.start()
|
|
||||||
|
|
||||||
# for process in processes:
|
|
||||||
# process.join()
|
|
||||||
else:
|
|
||||||
for i, batch in enumerate(batch_list, start=1):
|
for i, batch in enumerate(batch_list, start=1):
|
||||||
|
# delete_record(batch, base_model, f"Process-{i}", related_models)
|
||||||
process = mp.Process(
|
process = mp.Process(
|
||||||
target=delete_record,
|
target=delete_record,
|
||||||
args=(batch, base_model, f"Process-{i}", related_models),
|
args=(batch, base_model, f"Process-{i}", related_models),
|
||||||
)
|
)
|
||||||
processes.append(process)
|
processes.append(process)
|
||||||
process.start()
|
process.start()
|
||||||
|
|
||||||
for process in processes:
|
for process in processes:
|
||||||
process.join()
|
process.join()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user