Django - How to run consecutive forms? - python

I have a user registration form that asks for user information and also asks a question: "Are you a PSMC member?"
The options are:
rank = [
('Supporter', 'Supporter (non-member)'),
('Anak', 'Anak'),
('Uso', 'Uso'),
('Chief', 'Chief'),
]
If Supporter is selected, then the registration form proceeds and saves user info, etc. This part works fine. However, if Anak is selected, I want it to take the user to another form that asks additional questions.
In my forms.py, I have class RegisterForm which is the main registration form for all users. I also have class AnakRegisterForm which is what I want it to continue on to. I used Django's AuthenticationForm based off what I read from their website (but I could be wrong). I know the issue is in views.py register function. Specifically:
if rank == 'Anak':
anak_register(response)
During my debug session, after it moves response to anak_register function, it gets a bunch of scrambled information. I'm pretty lost, any help would be appreciated. Here is my code:
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(AuthenticationForm):
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(AnakRegisterForm, self).save(commit=False)
user.tribe = self.cleaned_data['tribe']
if commit:
user.save()
return user
class UsoRegisterForm(AuthenticationForm):
pass
class ChiefRegisterForm(AuthenticationForm):
pass
views.py
def register(response):
context = {}
if response.method == "POST":
form = RegisterForm(response.POST)
if form.is_valid():
form.save()
rank = form.cleaned_data.get('rank')
if rank == 'Anak':
anak_register(response)
else:
form.save()
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":
form = AnakRegisterForm(response.POST)
if form.request.is_valid():
form.save()
messages.success(response, 'Registration successful. Please login.')
return redirect('login')
else:
'''
context['register'] = form
'''
else:
form = AnakRegisterForm()
# context['register'] = form
# messages.error(request, 'Unsuccessful registration. Invalid information.')
# form = RegisterForm
return render(request=response, template_name='register/register.html', context={'form': form})
models.py
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")
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, first_name, last_name, tribe):
super().__init__(first_name, last_name)
self.tribe = tribe.title()
self.rank = User.rank[1]
EDIT
I changed AuthenticationForm to UserCreationForm and now it accepts the form. However, when I try to run it, I get the following error:
TypeError at /register/
__init__() missing 1 required positional argument: 'tribe'
If someone could point me in the right direction, I'd appreciate it!

Not sure how I missed this, but the best solution that I found is to implement Django Form Wizard. More information can be found here:
https://django-formtools.readthedocs.io/en/latest/wizard.html

Related

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')
]
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

Register Form not saving to database: Django?

Weird issue with by registration form, not sure i am doing wrong.
I have StudentProfile Model, that I am trying to save data from StudentResistrationForm but the data is not being saved into database
ERROR: NameError at /register/ name 'StudentProfile' is not defined
Is the view logic correct? What am I missing? Ideas please
model
class Accounts(AbstractUser):
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)
date_joined = models.DateTimeField('date joined', auto_now_add=True)
# asdd
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
class StudentProfile(models.Model):
user = models.OneToOneField('Accounts', related_name='student_profile')
# additional fields for students
AMEB_Ratings = models.PositiveIntegerField(default=0)
is_student = models.BooleanField('student status', default=False)
form
class StudentResistrationForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = (
'AMEB_Ratings',
)
def save(self, commit=True):
user = super(StudentResistrationForm, self).save(commit=False)
# user.first_name = self.cleaned_data['first_name']
# user.last_name = self.cleaned_data['last_name']
user.AMEB_Ratings = self.cleaned_data['AMEB_Ratings']
if commit:
user.save()
return user
class UserForm(forms.ModelForm):
class Meta:
model = get_user_model()
fields = ('username', 'email', 'password')
view
def registerStudent(request):
# Once register page loads, either it will send to the server POST data (if the form is submitted), else if it don't send post data create a user form to register
if request.method == "POST":
user_form = UserForm(request.POST)
form = StudentResistrationForm(request.POST)
if form.is_valid() and user_form.is_valid():
User = get_user_model()
username = user_form.cleaned_data['username']
email = user_form.cleaned_data['email']
password = user_form.cleaned_data['password']
new_user = User.objects.create_user(username=username, email=email, password=password)
Student_profile = StudentProfile()
Student_profile.user = new_user
Student_profile.AMEB_Ratings = request.POST['AMEB_Ratings']
# Student_profile = StudentProfile.create_user(AMEB_Ratings=AMEB_Ratings)
new_user.save()
Student_profile.save()
# form.save()
# AMEB_Ratings = form.cleaned_data['AMEB_Ratings']
return redirect('/')
else:
# Create the django default user form and send it as a dictionary in args to the reg_form.html page.
user_form = UserForm()
form = StudentResistrationForm()
# args = {'form_student': form, 'user_form': user_form }
return render(request, 'accounts/reg_form_students.html', {'form_student': form, 'user_form': user_form })
Looks like you have a few typos you currently are setting your email variable to the email data then setting it to the password data. Correct this first.
email = user_form.cleaned_data['email']
password = user_form.cleaned_data['password']

AttributeError: 'auth.User' has been swapped for 'accounts.Accounts'? How?

So I have a Accounts model that extends AbstractUser model.
I Also have StudentProfile and TeacherApplications that have a
one to one relation with Accounts.
I ALSO have two forms for teacher and student to fill out.
Requirement: Enable students/teachers to register via their forms.
Issue: In the student/teacher form I am asking for fields that the User model needs such as email, username, first and last name etc.. However I am getting this:.
Error: 1
AttributeError at /register/ Manager isn't available; 'auth.User' has
been swapped for 'accounts.Accounts'
Error 2
Student_profile.save()
^ TabError: inconsistent use of tabs and spaces in indentation
ISSUE: I need to register the user with user fields otherwise User fields would be null and generate error as well.. But is the best approach? Or what am I doing wrong?
models
class Accounts(AbstractUser):
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)
date_joined = models.DateTimeField('date joined', auto_now_add=True)
# asdd
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
class StudentProfile(models.Model):
user = models.OneToOneField('Accounts', related_name='student_profile')
# additional fields for students
AMEB_Ratings = models.PositiveIntegerField(default=0)
is_student = models.BooleanField('student status', default=False)
class TeacherApplications(models.Model):
user = models.OneToOneField('Accounts', related_name='teacher_profile')
# additional fields for teachers
instrument = models.TextField(max_length=500, blank=True)
skill = models.CharField(max_length=30, blank=True)
experience_in_years = models.PositiveIntegerField(blank=True)
is_teacher = models.BooleanField('teacher status', default=False)
forms
class StudentResistrationForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = (
# 'username',
'first_name',
'last_name',
'email',
'date_joined',
# 'password1',
# 'password2',
'AMEB_Ratings',
'is_student',
)
def save(self, commit=True):
user = super(StudentResistrationForm, self).save(commit=False)
# user.first_name = self.cleaned_data['first_name']
# user.last_name = self.cleaned_data['last_name']
user.AMEB_Ratings = self.cleaned_data['AMEB_Ratings']
if commit:
user.save()
return user
class TeacherRegistrationForm(forms.ModelForm):
class Meta:
model = TeacherApplications
fields = (
'instrument',
'skill',
'experience_in_years',
'is_teacher',
)
views
def registerStudent(request):
# Once register page loads, either it will send to the server POST data (if the form is submitted), else if it don't send post data create a user form to register
if request.method == "POST":
user_form = UserForm(request.POST)
form = StudentResistrationForm(request.POST)
if form.is_valid() and user_form.is_valid():
User = get_user_model()
username = user_form.cleaned_data['username']
email = user_form.cleaned_data['email']
email = user_form.cleaned_data['password']
new_user = User.objects.create_user(username=username, email=email, password=password)
Student_profile = StudentProfile()
Student_profile.user = new_user
Student_profile.AMEB_Ratings = request.POST['AMEB_Ratings']
# Student_profile = StudentProfile.create_user(AMEB_Ratings=AMEB_Ratings)
new_user.save()
Student_profile.save()
# form.save()
# AMEB_Ratings = form.cleaned_data['AMEB_Ratings']
return redirect('../home/')
else:
# Create the django default user form and send it as a dictionary in args to the reg_form.html page.
user_form = UserForm()
form = StudentResistrationForm()
args = {'form_student': form, 'user_form': user_form }
return render(request, 'accounts/reg_form_students.html', args)
def teacherApplication(request):
# # Once register page loads, either it will send to the server POST data (if the form is submitted), else if it don't send post data create a user form to register
# if request.method == "POST":
# form = TeacherRegistrationForm(request.POST)
# if form.is_valid():
# instrument = form.cleaned_data['instrument']
# skill = form.cleaned_data['skill']
# experience_in_years = form.cleaned_data['experience_in_years']
# is_teacher = form.cleaned_data['is_teacher']
# form.save()
# return redirect('../home/')
# else:
# # Create the django default user form and send it as a dictionary in args to the reg_form.html page.
# user_form = UserForm()
# form = StudentResistrationForm()
# return render(request, 'accounts/reg_form_teachers.html', {'form_student': form, 'user_form': user_form })
pass
I think you should change your model to something like this, I mean that Student and Teacher should inherit from account, so you dont need is_teacher and is_student anymore.
class StudentProfile(Accounts):
AMEB_Ratings = models.PositiveIntegerField(default=0)
class TeacherApplications(Accounts):
instrument = models.TextField(max_length=500, blank=True)
skill = models.CharField(max_length=30, blank=True)
experience_in_years = models.PositiveIntegerField(blank=True)
For your second error, I have to say the problem is indention and you should replace your tabs with 4 spaces.
Also I need your urls.py file to figure out it completely, I think problem might be there.
this link might be useful
Manager isn't available; User has been swapped for 'pet.Person'

django - linking the register form info to user profile model

I am trying to make a webapp that includes register/login operations. The login is working fine but the registration form is frustrating.
I have a class registration form that inherits from UserCreationForm. The users are created and seen in the admin page when created but the problem I am having is not being able to see them in my UserProfile model in the admin page. It does not link the information and I could not find a way the link them.
Here is the registration form:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
city = forms.CharField(required=False)
country = forms.CharField(required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
username = forms.CharField(required=True)
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'country',
'city',
'email'
)
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
#user.first_name = self.cleaned_data['first_name']
if commit:
user.save()
return user
Here are my models:
class UserProfile(models.Model):
user = models.OneToOneField( User,
on_delete=models.CASCADE
)
username = models.TextField(max_length=30, default="")
first_name = models.TextField(max_length=30, default="")
last_name = models.TextField(max_length=30, default="")
country = models.TextField(max_length=30, default="Which country are you from?")
city = models.TextField(max_length=30, default="Which city are you from?")
class ColorChoice(models.Model):
user = models.ForeignKey(
'UserProfile',
on_delete=models.CASCADE
)
color1 = models.IntegerField()
color2 = models.IntegerField()
color3 = models.IntegerField()
color4 = models.IntegerField()
color5 = models.IntegerField()
and my view.py that does the registering:
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
return redirect('color')
else:
form = RegistrationForm()
args = {'form': form}
return render(request, 'account/create_new.html', args)
Your form is only creating a new objects for User. You would need to add a signal for example to create a new object for UserProfile.
Something like this:
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
signals.post_save.connect(create_user_profile, sender=User)

Django UserCreationForm add request.user.pk ?

Hi i need add request user pk to UserCreationForm. I try many times, but with out results. In my dashboard i have user owner and i have user seller.
One model for all 3 types user. So Owner can create another user with role=seller, but for this i need add parent field with param=request.user.pk to seller
Custom UserModel
from django.db import models
from django.contrib.auth.models import AbstractUser
USER = 0
OWNER = 1
SELLER = 2
user_type_choice = (
(USER, 'User'),
(OWNER, 'Owner'),
(SELLER, 'Seller'),
)
class User(AbstractUser):
role = models.CharField(max_length=100, choices=user_type_choice, default=USER)
idn = models.CharField(max_length=10, blank=True, default=None, null=True)
mobile = models.CharField(max_length=15, blank=True, default=None, null=True)
parent = models.ForeignKey('self', null=True, blank=True)
tariff = models.IntegerField(default=None, null=True, blank=True)
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
forms.py
class SellerRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
first_name = forms.CharField(required=False)
last_name = forms.CharField(required=False)
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2') """try add and remove 'parent' here in felds """
def save(self, commit=True):
user = super(SellerRegisterForm, self).save(commit=False)
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.role = ROLE_SELLER
# try add user.parent = self.request.user.pk
# but it does not work
if commit:
user.save()
return user
views.py
def register_seller(request):
if request.method == 'POST':
form = SellerRegisterForm(request.POST) # create form object
if form.is_valid():
#try add new_form = form.save(commit=False)
# new_form.parent = request.user.pk
# new_form.save() but it not working
form.save()
return HttpResponseRedirect('/accounts/login')
else:
form = SellerRegisterForm()
return render(request, 'register_owner.html', {'form': form})
You need to pass the user object through the form's __init__ method.
class SellerRegisterForm(UserCreationForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(SellerRegisterForm, self).__init__(*args, **kwargs)
and in your view:
form = SellerRegisterForm(request.POST, user=request.user)
Then simply use self.user anywhere in your form class.

Categories