NUSav77
NUSav77

Reputation: 72

Django - Saving updated information to existing user

I'm creating a multi-step user form for registration. The idea behind it, is when the user registers, it gets their registration information. Then, it saves the users information. In the registration form, if the user selected a certain option (rank = 'Anak'), they are redirected to a base form that obtains the tribe name. What I want it to do, is save the tribe name into that users account that was just created, but I am having trouble doing this since there is no save() function for base forms in Django.

forms.py

class RegisterForm(UserCreationForm):
    email = forms.EmailField(
        initial='',
        required=True,
        help_text='Please enter a valid email address'
    )

    rank = forms.ChoiceField(
        label='Are you a PSMC member?',
        choices=SavBlock.models.User.rank,
        initial=False,
        required=True,
        help_text='Member accounts will be validated with your HC.',
    )

    class Meta:
        model = User
        # username = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
        fields = ['username', 'first_name', 'last_name', 'email',
                  'rank', 'password1', 'password2']

    def save(self, commit=True):
        user = super(RegisterForm, self).save(commit=False)
        user.email = self.cleaned_data['email']
        user.ranking = self.cleaned_data['rank']
        if commit:
            user.save()
        return user


class AnakRegisterForm(Form):
    tribe = forms.ChoiceField(
        label='What tribe are you from, Uce?',
        choices=SavBlock.models.Anak.tribe,
        initial=False,
        required=True,
        help_text='Member accounts will be validated with your HC.'
    )

    class Meta:
        model = Anak
        fields = ['tribe']

    def save(self, commit=True):
        user = super(RegisterForm, self).save(commit=False)
        user.tribe = self.cleaned_data['tribe']
        if commit:
            user.save()
        return user

views.py

def register(response):
    context = {}
    # temp_key = {}
    if response.method == "POST":
        form = RegisterForm(response.POST)
        if form.is_valid():
            form.save()
            if form.cleaned_data.get('rank') == 'Anak':
                # temp_key[form.cleaned_data.get('username')] = form.cleaned_data
                return redirect('anak_register')
                # anak_register(form.cleaned_data)

            messages.success(response, 'Registration successful. Please login.')
            return redirect('login')
        else:
            context['register'] = form
    else:
        form = RegisterForm()
        context['register'] = form

    return render(request=response, template_name='register/register.html', context={'form': form})


def anak_register(response):
    context = {}
    if response.method == 'POST':
        anak_form = AnakRegisterForm(response.POST)
        if anak_form.is_valid():
            # TODO: The form obtains the tribe information from the user. Now we must save this information into the
            #  users account.

            """
            anak_form = RegisterForm.save(register)
            return anak_form
            """
        else:
            context['register'] = anak_form

        render(request=response, template_name='register/register.html', context={'form': anak_form})

    else:
        anak_form = AnakRegisterForm(response.POST)
        context['anak_register'] = anak_form
        # messages.error(request, 'Unsuccessful registration. Invalid information.')

    return render(request=response, template_name='register/anak_register.html', context={'form': anak_form})

Before redirected to anak_register, the UserCreationForm saves the users information. The anak_form holds some information that I want associated to the users account that was just created, but like I said, I'm not sure how to do it. There may be an easier way of doing this

models.py

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        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.save(using=self._db)
        return User

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

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

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

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


class User(AbstractBaseUser, PermissionsMixin):
    rank = [
        ('Supporter', 'Supporter (non-member)'),
        ('Anak', 'Anak'),
        ('Uso', 'Uso'),
        ('Chief', 'Chief'),
    ]

    tribe = [
        ('NaKoaHema', 'Na Koa Hema'),
        ('Alakai', 'Alaka\'i')
    ]

    username = models.CharField("user name", max_length=50, default='', unique=True)
    email = models.EmailField("email address", max_length=30, unique=True, blank=True)
    first_name = models.CharField("first name", max_length=50)
    last_name = models.CharField("last name", max_length=50)
    is_active = models.BooleanField('active', default=True)
    # password = models.CharField("password", unique=True, max_length=32, default='')
    id = models.AutoField(primary_key=True)
    is_staff = models.BooleanField('staff status', default=False)
    date_joined = models.DateField('date_joined', default=timezone.now)
    ranking = models.CharField(choices=rank, max_length=50, default="Supporter")
    tribe = models.CharField(choices=tribe, max_length=50, default="None")

    objects = UserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email', 'password']  # 'ranking'

    # Magic method returns string of self
    def __str__(self):
        return f"User {self.first_name} {self.last_name} rank {self.rank}".strip()

    @property
    def get_full_name(self):
        return f"{self.first_name} {self.last_name}".strip()


class Anak(User):
    def __init__(self, tribe, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.tribe = tribe.title()
        self.rank = User.rank[1]

    tribe = [
        ('NaKoaHema', 'Na Koa Hema'),
        ('Alakai', 'Alaka\'i')
    ]

Upvotes: 0

Views: 415

Answers (1)

Egor Wexler
Egor Wexler

Reputation: 1944

I am not 100% sure that correctly understood your intentions, but as I see you redefine Django's user with own class to extend it with additional things (rank, tribe).

The more elegant way to do it (in my opinion) is to create an additional model which will hold specific fields and be connected one-to-one with the standard Django User model. So your models.py will be:

from django.db import models
from django.contrib.auth.models import User

class Anak(models.Model):
    rank = [
        ('Supporter', 'Supporter (non-member)'),
        ('Anak', 'Anak'),
        ('Uso', 'Uso'),
        ('Chief', 'Chief'),
    ]

    tribe = [
        ('NaKoaHema', 'Na Koa Hema'),
        ('Alakai', 'Alaka\'i')
    ]

    user = models.OneToOneField(User, on_delete=models.CASCADE) # one Anak instance associated with User instance
    tribe = models.CharField(choices=tribe, max_length=50, default="None")
    ranking = models.CharField(choices=rank, max_length=50, default="Supporter")

    def save(self, *args, **kwargs):
        # Create dependent User if not exist
        if not self.user.pk:
            self.user = User.objects.create_user(username=self.user.username, password=self.user.password)
            self.user.is_staff = False

        # logic that you need before saving (if needed)
        self.tribe = tribe.title()
        self.rank = User.rank[1]

        self.user.save()  # mandatory as create_user is not recognized as save operation

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

Thus, now you may use your forms to create user and Anak associated one-to-one with the user.

Additional tip - is when user already created and logged in - your request.user will contain an object of User model with the related user. So you may use it to create/update related Anak. I.e.:

if response.method == 'POST':
    anak = models.Anak.objects.get_or_create(user=response.user)  # get Anak if exists, create - otherwise
    anak_form = AnakRegisterForm(instance=anak, data=response.POST)  # update anak from above
    if anak_form.is_valid():
        anak_form.save()

Thus, you will use standard Django users (without the need to redefine User by yourself) plus keeping Anak information in a separate table which is created only when needed as an extension to standard User behaviour

Upvotes: 1

Related Questions