Here is a generic python implementation for cascade delete. We assume that the 'foreign key' is an ObjectId of the parent.
The enter method is called when the context is entered, and it automatically invokes the discover_collections method to find collections with ObjectId references. The exit method is called when the context is exited, and it closes the MongoDB client connection.
Using the class as a context manager ensures that the MongoDB connection is properly managed, and the discover_collections method is called at the appropriate time.
Remember to replace <mongodb_connection_string> with your actual MongoDB connection string and 'your_database_name' with the name of your database.
Call the class within a context:
with CascadeDelete('<mongodb_connection_string>', 'your_database_name') as cascade_delete:
cascade_delete.delete(ObjectId('parent1'))
The CascadeDelete class implementation:
from pymongo import MongoClient
from bson.objectid import ObjectId
class CascadeDelete:
""" Usage:
with CascadeDelete('<mongodb_connection_string>', 'your_database_name') as cascade_delete:
cascade_delete.delete(ObjectId('parent1'))
"""
def __init__(self, connection_string, database_name):
self.client = MongoClient(connection_string)
self.db = self.client[database_name]
self.collections = []
def __enter__(self):
self.discover_collections()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.client.close()
def discover_collections(self):
for collection_name in self.db.list_collection_names():
collection = self.db[collection_name]
if self._has_objectid_references(collection):
self.collections.append(collection)
def delete(self, document_id):
deleted_documents = set()
self._delete_documents(document_id, deleted_documents)
for collection in self.collections:
collection.delete_many({'_id': {'$in': list(deleted_documents)}})
def _delete_documents(self, document_id, deleted_documents):
deleted_documents.add(document_id)
for collection in self.collections:
document = collection.find_one({'_id': document_id})
if document:
for key, value in document.items():
if isinstance(value, ObjectId) and key != '_id':
self._delete_documents(value, deleted_documents)
@staticmethod
def _has_objectid_references(collection):
sample_document = collection.find_one()
if not sample_document:
return False
for value in sample_document.values():
if isinstance(value, ObjectId):
return True
return False
I hope this can help someone...
Kind regards