Home > Net >  Django 3.2 post_save signal invoked for all model instances even if 1 instance is used in the code t
Django 3.2 post_save signal invoked for all model instances even if 1 instance is used in the code t

Time:01-10

Is post_save signal called for all the mailbox model instances when we click save button in django model admin? Does the post_save signal work continuously in Apache server?When it is executed behind Apache, just when we refresh the page it again saving the same password in next password column as the previous password column.I need to check password history for 5 passwords to not repeat.The post_save code:

def check_oldest_password_instance(mbox):
    passwd_list =  []
    oldest_password = None
    oldest_password_datetime = datetime.now()
    pfield = '1'
    n = 0
    mphistory = Phistory.objects.filter(mbox=mbox)
    if mphistory:
        print('======phistory exists====', mphistory)
        for p in mphistory:
            if p.password1:
                passwd_list.append(p.password1)
                oldest_password_datetime = p.datetime1
                oldest_password = p.password1
                pfield = '1'
            if p.password2:
                passwd_list.append(p.password2)
                if oldest_password_datetime > p.datetime2:
                    oldest_password_datetime = p.datetime2
                    oldest_password = p.password2
                    pfield = '2'
            if p.password3:
                passwd_list.append(p.password3)
                if oldest_password_datetime > p.datetime3:
                    oldest_password_datetime = p.datetime3
                    oldest_password = p.password3
                    pfield = '3'
            if p.password4:
                passwd_list.append(p.password4)
                if oldest_password_datetime > p.datetime4:
                    oldest_password_datetime = p.datetime4
                    oldest_password = p.password4
                    pfield = '4'
            if p.password5:
                passwd_list.append(p.password5)
                if oldest_password_datetime > p.datetime5:
                    oldest_password_datetime = p.datetime5
                    oldest_password = p.password5
                    pfield = '5'
        print(len(passwd_list),pfield,'passwd_list_len,pfield_oldest')
        n = len(passwd_list)
        # For new mailbox, check if all 5 values are populated 
        # if n == 0:
        #     pfield = '1'
        if n == 1:
            pfield = '2'
        if n == 2:
            pfield = '3'
        if n == 3:
            pfield = '4'
        if n == 4:
            pfield = '5'
        print(pfield,n, 'pfield-n------------------')
    else:
        oldest_password = None
        n = 0
        pfield = '1'

    return (pfield, passwd_list)


def mbox_post_save(sender, instance, created, **kwargs):
    mphistory = None
    new_phistory = None
    mphistory = None
    print('post save callllll')
    if created:
        # Store the hashed password in Phistory table
        #----------------
        new_phistory = Phistory()
        new_phistory.mbox = instance
        new_phistory.password1 = instance.mpassword
        new_phistory.datetime1 = datetime.now()
        new_phistory.save()
        #----------------
    else:
        print('# edit mbox post_save')

        # Store the hashed password in Phistory table
        #----------------
        try:
            mphistory = Phistory.objects.get(mbox=instance)
            print(mphistory,'mppppp=======================')
        except Exception as e:
            print(e)
        if mphistory:
            print('PHISTORY--------')
            (oldest_pfield, passwd_list) = check_oldest_password_instance(instance)
            if oldest_pfield == '1':
                mphistory.password1 = instance.mpassword
                mphistory.datetime1 = datetime.now()
            elif oldest_pfield == '2':
                mphistory.password2 = instance.mpassword
                mphistory.datetime2 = datetime.now()
            elif oldest_pfield == '3':
                mphistory.password3 = instance.mpassword
                mphistory.datetime3 = datetime.now()
            elif oldest_pfield == '4':
                mphistory.password4 = instance.mpassword
                mphistory.datetime4 = datetime.now()
            elif oldest_pfield == '5':
                mphistory.password5 = instance.mpassword
                mphistory.datetime5 = datetime.now()
            mphistory.save()
        else:
            if not mphistory:
                print('# Add new phistory object 1st time for existing mbox')
                new_phistory = Phistory()
                new_phistory.mbox = instance
                new_phistory.password1 = instance.mpassword
                new_phistory.datetime1 = datetime.now()
                new_phistory.save()
        #----------------

    return

The way the post_save signal is connected:

post_save.connect(mbox_post_save, sender=Mbox)

CodePudding user response:

To answer your questions:

Post_save will still kick in if you save an object from the /admin section

It should be listening for all saves of a particular object - it should not be kicking off post_save routines for every object of that type. If this is happening to you there is likely something else going wrong, but I can't see it in the code provided here.

To check the existence of previous passwords is fairly simple. However I think you are making a mistake to put all this as a post_save signal. If you do it post_save you will have saved the mailbox password already, a change you will need to reverse if your password history check fails.

This is better put in the clean() function of your form (or validate() function of your API), so you can case an error if there is a history match.

I'd do it something like this:

forms.py

#first some support functions

def get_password_history(mbox):
     #Is there an existing Phistory?
     try:
         mphistory = Phistory.objects.get(mbox=mbox)
     except Phistory.DoesNotExist
         return False
     return mphistory    


def is_password_used(mphistory, password):
    #look for the password in the existing records
    password_list = [
                mphistory.password1, 
                mphistory.password2, 
                mphistory.password3, 
                mphistory.password4, 
                mphistory.password5, 
            ]
    if password in password_list:
         return True
    return False

def update_password_history(mphistory, password):
    #I haven't bothered with datetime here as the oldest is always the highest,
    #but if you need it you can add lines for that field to do a similar swap.
    mphistory.password5  = mphistory.password4 
    mphistory.password4  = mphistory.password3 
    mphistory.password3  = mphistory.password2
    mphistory.password2  = mphistory.password1 
    mphistory.password1  = password
    mphistory.save()

then in your form.py clean method (or validation method for an API)

def clean(self):
    cleaned_data = super().clean()
    history = get_password_history(self)
    if history:
        previous_password_exists = is_password_used(history, cleaned_data.get('mpassword')
        if previous_password_exists:
            raise forms.ValidationError(
                "The password has been used in the last five changes"
            )
        else:
            #the below could also be done in your view after form.is_valid for clarity
            update_password_history(history, cleaned_data.get('mpassword'))
                           

Now you can keep your post_save just for creating a new password history in the event of mailbox creation.

def mbox_post_save(sender, instance, created, **kwargs):
    print('post save phistory creation')
    if created:
        # Store the hashed password in Phistory table
        #----------------
        new_phistory = Phistory()
        new_phistory.mbox = instance
        new_phistory.password1 = instance.mpassword
        new_phistory.datetime1 = datetime.now()
        new_phistory.save()
        #----------------

    return
  • Related