Django pre_save signal - would an exception fail the transaction?

From the docs:

There's no way to tell what the value of an ID will be before you call save(), because the value is determined by your database, not by Django.

So if your pre-save processing requires the user.id, I am afraid this is not possible.


Here is the two part answer:

  1. Yes, raising an exception in the pre_save() signal will abort the call to the save() method. For example, I did this in my pre_save():

    if SOME_TEST:
        raise exceptions.ParseError(detail="I reject your data. Here is why.")
    

    Note: This was using the DRF exceptions, but I will assume you know how to raise whatever exception you prefer.

  2. You could use the post_save() signal to get the id AFTER the save() method. But if that will not work for you, then below is my original explanation of some tactics to do what you want in the pre_save():

In the pre_save you can access User.objects.all() (if you import the User model, of course). If you are using the standard User model then the User.id value is AUTO INCREMENT. So if you NEED to know what the value WILL be for the new user, just get the highest User.id currently in the database (ie: the id of the last user created). Here is a simple query to do this:

last_user = User.objects.latest('id')

Then of course, last_user.id will give you the value of the id for the last user created and if you add one to that you will have the next user id. Of course, if you have a busy site you may have some problems with simultaneous user creation. The risk is that this value is received by two (or more) User creation attempts and one (or more) of them ends up wrong.

Of course, you can always set another field to be primary_key=True and this will replace the id field on the model. Then you can devise any sort of indexing mechanism that you can dream up! You could use a hash value of a unique characteristic. For example: The User.username has a Unique constraint, you could hash or hex encode that as a numeric ID to pre-determine the User.id. Or you could leave the id field in place and set it manually in the pre_save by assigning a value to obj.id. It could be anything you want. Even a hash value!