Joel G Mathew
Joel G Mathew

Reputation: 8061

Overriding model save method does not allow assignment of values

I am attempting to override a model's save method and assign a value to the field when the field is saved:

My models.py:

def NewInviteCode():
    import secrets
    print(secrets.token_urlsafe(16))


class Invitation(models.Model):
    id = models.AutoField(primary_key=True, unique=True)
    name = models.CharField(max_length=300, default='', blank=True)
    email = models.EmailField(max_length=100, default='')
    mobile = models.CharField(max_length=15, default='', blank=True)
    last_sent = models.DateTimeField(default=timezone.now)
    num_invitations = models.IntegerField(default=1)
    uniqcode = models.CharField(max_length=300, default='', blank=True)

    def save(self, *args, **kwargs):
        if not self.uniqcode:
            self.uniqcode = NewInviteCode()
            print(f"Saved new unique code: {self.uniqcode}")
        if self.num_invitations:
            self.num_invitations = self.num_invitations + 1
        print(f"Sending invitation to {self.email}..")
        SendInviteActual(self.email)
        print(f"Parameters to be saved are: Code:{self.uniqcode} Name: {self.name} Email: {self.email}")
        super().save(*args, **kwargs)  # Call the "real" save() method.

Unfortunately, on my code execution:

fktewVPm63tV-YqXxWPNxQ
Saved new unique code: None
Sending invitation to [email protected].
Mail sent
Parameters to be saved are: Code:None Name: joel Email: [email protected]
2019-05-26 20:04:48,972 django.request ERROR    Internal Server Error: /clinic/sendinvites

What's the problem with the following lines of code?

if not self.uniqcode:
    self.uniqcode = NewInviteCode()
    print(f"Saved new unique code: {self.uniqcode}")

Upvotes: 1

Views: 45

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476574

Your NewInviteCode does not return anything, it only prints something. If a Python function does not return something explicitly, it returns None. You should return a value, like:

def NewInviteCode():
    from secrets import token_urlsafe
    return token_urlsafe(16)

That being said, you can set a callable as default= parameter [Django-doc] for field, like:

def new_invite_code():
    from secrets import token_urlsafe
    return token_urlsafe(16)

class Invitation(models.Model):
    id = models.AutoField(primary_key=True, unique=True)
    name = models.CharField(max_length=300, default='', blank=True)
    email = models.EmailField(max_length=100, default='')
    mobile = models.CharField(max_length=15, default='', blank=True)
    last_sent = models.DateTimeField(default=timezone.now)
    num_invitations = models.IntegerField(default=1)
    uniqcode = models.CharField(max_length=300, default=new_invite_code, blank=True)

We here thus do not call the function we pass new_invite_code, not the result of new_invite_code, we thus do not write brackets (like new_invite_code()).

this makes your models and safe(..) function simpler.

Note: according to the PEP-8 style guide, you probably should rename NewInviteCode to new_invite_code, like I did in the second code fragment.

Upvotes: 1

Related Questions