Cipher
Cipher

Reputation: 2122

Error in user.id while saving data in Model - Commit False not working

I am using AbstractBaseUser for that i have created a managers.py for creating superuser.

Everything is working except these line in managers.py. Even i tried to print(user.id) before user.save(using=self._db) it is printing None

user.save(using=self._db)
user.created_by_id = user.id
user.updated_by_id = user.id
user.save()

I have also used commit=False in user.save(using=self._db, commit=False) still it is giving error. See the errors at bottom.

In my models.py i have tweo field created_by & updated_by which should should not be null. Thats why i am saving in Managers.py which is creating all this error.

How can i solve this issue. Is there better approach. This condition is only required for creating superuser.

Models.py

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(_('email address'), unique=True)
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    is_active = models.BooleanField(_('is user active'), default=True)
    profile_image = models.ImageField(default='user-avatar.png', upload_to='users/', null=True, blank=True)
    is_staff = models.BooleanField(_('staff'), default=True)

    role = models.ForeignKey(Role, on_delete=models.CASCADE)
    phone_number = models.CharField(max_length=30, blank=True, null=True)
    address = models.CharField(max_length=100, blank=True, null=True)

    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, related_name='user_created_by')
    created_on = models.DateTimeField(_('user created on'), auto_now_add=True)
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, related_name='user_updated_by')
    updated_on = models.DateTimeField(_('user updated on'), auto_now=True)

Managers.py

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.role_id = 1
        user.first_name = "Super"
        user.last_name = "Admin"
        user.save(using=self._db)
        user.created_by_id = user.id
        user.updated_by_id = user.id
        user.save()
        return user

    def create_user(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)

Error - for commit=False

super().save(*args, **kwargs)
TypeError: save() got an unexpected keyword argument 'commit'

Error - Without Commit False

raise utils.IntegrityError(*tuple(e.args))
django.db.utils.IntegrityError: (1048, "Column 'created_by_id' cannot be null")

Upvotes: 0

Views: 345

Answers (1)

ruddra
ruddra

Reputation: 51978

commit=False works for ModelForms. It does not work for model instances. As the error said in your question, there is no keyword argument named commit exists in save method.

Also, before saving the user object, it can't produce any primary key. I would recommend to make created_by null able. So that you can avoid integrity error.

created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, related_name='user_created_by', null=True, default=None)

updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, related_name='user_updated_by', null=True, default=None)

Still, if you want to enforce this restriction, then you can do that in forms or serializer or override the save method as well in the User model.

For example:

class User(...):
   ...
   def save(self, *args, **kwargs):
       if not self.is_superuser:
          if not self.created_by or not self.updated_by:
               raise Exception("Add these fields")  # can be custom exception

       super(User, self).save(*args, **kwargs)

Upvotes: 1

Related Questions