Reputation: 72
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.
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
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
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
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