Sending and receiving custom signals and ensuring it is atomic

I want to create a custom signal and send the signal after some database operations. The receiver also performs database operations. I want to make sure all the database operations before sending the signal and, in the signal receiver is atomic meaning either all fail, or all succeed.

Consider the following code:

def send_my_signal():
    # Database operation 1
    # Database Operation 2
    my_signal.send(sender=MyModel)

The receiver:

@receiver(my_signal)
def my_receiver(sender, **kwargs):
    # Database operation 3
    # Database operation 4

I want either all database operations 1 to 4 succeed or fail together.
By asking from generative AI models, one proposed solution is to wrap the body of the function that performs database operations and sends the signal inside the transaction.atomic() context manager. So, the code would look like this:

def send_my_signal():
    with transaction.atomic():
        # Database operation 1
        # Database Operation 2
        my_signal.send(sender=MyModel)

But I could not find any resource or anything in Django documentation confirming this. It’s not like I am calling the receiver of the signal inside the transaction.atomic() context manager, I am just sending the signal there.

Hi, sending a signal do call the receivers as per the documentation (Signals | Django documentation | Django) states: saying

All four methods return a list of tuple pairs [(receiver, response), ...], representing the list of called receiver functions and their response values.

means that receivers are necessarilly called by the send method so that it can return the response of each receiver.

Thus, the operations done in receivers are in the atomic context. However, if you have control on both signal sending and receivers, it looks like you do not need signal and you’d rather directly call the receivers