I have created an extended User model and a form to register it. Since the form is filled out by an admin and not a user, the UserCreationForm has a random password generator to fill out password1 and 2, then set new password. This is great for a new user, but every time an admin edits the user profile, it will set a new password. I've looked at a few dozen examples here and on big G but can't seem to find a usable solution to know if the user has a password set.
I am re-using this form for update view, which is where I don't want the random password to be generated again. I tried doing the same if statement check as the username but it doesn't work the same way as the auth\form.py user.set_password is looking for password1.
class EmployeeRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True, widget=forms.EmailInput(attrs={'class': 'form-control mb-2',
'placeholder': 'Email address'}))
first_name = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control mb-2',
'placeholder': 'First name'}))
last_name = forms.CharField(
widget=forms.TextInput(attrs={'class': 'form-control mb-2', 'placeholder': 'Last name'}))
password1 = None
password2 = None
class Meta:
model = User
fields = ['email', 'first_name', 'last_name']
def clean(self):
password = User.objects.make_random_password(length=10,
allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
self.cleaned_data['password1'] = password
self.cleaned_data['password2'] = password
return super().clean()
def save(self, commit=True):
user = super().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']
if user.username == '':
if len(user.first_name.split()) and len(user.last_name.split()) > 1:
username = f'{"".join(user.first_name.split()[:2])}{"."}{"".join(user.last_name.split())}'
elif len(user.first_name.split()) > 1:
username = f'{"".join(user.first_name.split()[:2])}{"."}{user.last_name}'
elif len(user.last_name.split()) > 1:
username = f'{user.first_name}{"."}{"".join(user.last_name.split())}'
else:
username = f'{user.first_name}{"."}{user.last_name}'
username = username.lower()
user.username = username
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user
Update
Here is the profile_edit view where I verify the details of an existing user and save changes made through the form.
#login_required()
def profile_edit(request, slug, *args, **kwargs):
"""
Used for updating the Employee profile
"""
employee = Employee.objects.get(slug=slug)
employee_user = User.objects.get(id=employee.user.id)
form = EmployeeForm(request.POST or None, request.FILES or None, instance=employee)
user_form = EmployeeRegistrationForm(request.POST or None, instance=employee_user)
if 'cancel' in request.POST:
return redirect('corehr:employees')
elif request.method == 'POST':
form = EmployeeForm(request.POST or None, request.FILES or None, instance=employee)
user_form = EmployeeRegistrationForm(request.POST or None, instance=employee_user)
if form.is_valid() and user_form.is_valid():
form.save(commit=False)
user_form.save()
form.save()
messages.success(request, f'{employee.get_full_name}{" was updated successfully."}')
return redirect(reverse_lazy('corehr:profile', kwargs={'slug': slug}))
else:
messages.error(request, 'Please correct the errors listed below')
else:
form = EmployeeForm(request.POST or None, request.FILES or None, instance=employee)
user_form = EmployeeRegistrationForm(request.POST or None, instance=employee_user)
context = {'form': form, 'user_form': user_form}
return render(request, 'coreHR/profile_edit.html', context)
I ended up taking the suggestions from Abdul and Ranu. The are both right, so I just created a UserUpdateForm to update the data rather to 're-create' the user again. It all works as smooth as ever.
class UserUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = ['email', 'first_name', 'last_name']
widgets = {
'email': forms.EmailInput(attrs={'class': 'form-control mb-2'}),
'first_name': forms.TextInput(
attrs={'class': 'form-control mb-2'}),
'last_name': forms.TextInput(
attrs={'class': 'form-control mb-2'}),
}
I replaced the reference in the profile_edit view from EmployeeRegistrationForm to UserUpdateForm. This was so simple an I was busting my head trying to work around several complex solutions which were going against the basics.
Related
I am trying to make a registration form for a dentist and a student, where I have a choice field for a dentist and a student. What I want to happen is, when dentist is picked, I should be able to see the specialties field in the html as well as Django to pick that form, and for student, to pick student_email and institution. I am confused to how to write its code in the template and I looked almost everywhere and couldn't find anything that could help with what I want. I also included an image with what the registration image looks like. I know I can use select and option in html template but still a bit confused about them as well. If you could show me a better way to apply my idea, please let me know.
form.py
from django import forms
from django_countries.fields import CountryField
class RegistrationForm(forms.Form):
Specialties = [
('pediatric','Pediatric'),
('oral surgeon','Oral Surgeon'),
('periodontist','Periodontist (Restorative; Esthetic)'),
('orthodontist','Orthodonsit'),
('endodontist','Endodontist'),
('prosthodontist','Prosthodontist'),
('oral pathologist','Oral Pathologist'),
('oral radiologist','Oral Radiologist'),
('public health dentist','Public Health Dentist'),
('research and academics','Research and Academics'),
]
username = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True, unique=True)
email = forms.EmailField(widget=forms.EmailInput(attrs={'class':'form-control'}), required=True, unique=True)
student_email = forms.EmailField(widget=forms.EmailInput(attrs={'class':'form-control'}), required=True, unique=True)
password = forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control'}), required=True)
password_repeat = forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control'}), required=True)
first_name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
last_name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
date_of_birth = forms.DateField(label = "Date of Birth", widget=forms.SelectDateWidget([x for x in range(1920,2021)]), required=True)
country = CountryField().formfield(required=True)
gender = forms.ChoiceField(widget=forms.RadioSelect, choices=[('male','Male'),('female','Female')], required=True)
specialty = forms.CharField(widget=forms.Select(choices= Specialties), required=True)
institution = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
dentist_or_student = forms.ChoiceField(widget=forms.RadioSelect, choices=[('dentist','Dentsit'),('student','Student')], required=True)
def clean_student_email(self):
data = self.cleaned_data['student_email']
if "#edu" not in data: #Check if the student's email is educational or not
raise forms.ValidationError("Email must be #edu")
return data
views.py
def user_register(request):
template_name = 'accounts/signup.html'
if request.method == 'POST':
form = RegistrationForm(request.POST)
# Check for validity
if form.is_valid():
if form.cleaned_data['dentist_or_student'] == 'dentist':
if User.objects.filter(username=form.cleaned_data['username']).exists():
return render(request, template_name, {
'form': form,
'error_message': 'Username already exists'
})
elif User.objects.filter(email=form.cleaned_data['email']).exists():
return render(request, template_name, {
'form': form,
'error_message': 'Email already exists'
})
elif form.cleaned_data['password'] != form.cleaned_data['password_repeat']:
return render(request,template_name, {
'form': form,
'error_message': 'Passwords do not match'
})
else:
# Create the user
user = User.objects.create_user(
form.cleaned_data['username'],
form.cleaned_data['email'],
form.cleaned_data['password']
)
user.first_name = form.cleaned_data['first_name']
user.last_name = form.cleaned_data['first_name']
user.dentist_or_student = form.cleaned_data['dentist']
user.date_of_birth = form.cleaned_data['date_of_birth']
user.country = form.cleaned_data['country']
user.gender = form.cleaned_data['gender']
user.save()
# Login the user
login(request, user)
# redirect to Homepage
return HttpResponseRedirect('home')
elif form.cleaned_data['dentist_or_student'] == 'student':
if User.objects.filter(username=form.cleaned_data['username']).exists():
return render(request, template_name, {
'form': form,
'error_message': 'Username already exists'
})
elif User.objects.filter(email=form.cleaned_data['student_email']).exists():
return render(request, template_name, {
'form': form,
'error_message': 'Email already exists'
})
elif form.cleaned_data['password'] != form.cleaned_data['password_repeat']:
return render(request,template_name, {
'form': form,
'error_message': 'Passwords do not match'
})
else:
# Create the user
user = User.objects.create_user(
form.cleaned_data['username'],
form.cleaned_data['student_email'],
form.cleaned_data['password']
)
user.first_name = form.cleaned_data['first_name']
user.last_name = form.cleaned_data['first_name']
user.dentist_or_student = form.cleaned_data['student']
user.date_of_birth = form.cleaned_data['date_of_birth']
user.country = form.cleaned_data['country']
user.gender = form.cleaned_data['gender']
user.save()
# Login the user
login(request, user)
# redirect to Homepage
return HttpResponseRedirect('home')
else:
messages.error(request, 'Please pick if either you are a Dentist or a Student before continuing the form')
return redirect('register')
# No post data available, just show the webpage
else:
form = RegistrationForm()
return render(request, template_name, {'form': form})
enter image description here
You are using a CharField (see docs) for a field that has options. Try using a ChoiceField (see docs) instead.
Something like:
specialty = forms.ChoiceField(choices= Specialties, required=True)
You should add both into your html, and make them hidden.
Then add an alert into your Select, and OnChange you can make hidden=false of the selected form.
YOURSELECT.addEventListener("change", document.getElementById(YOURSELECT.value).removeAttribute("hidden"););
I want logged in user redirect to index when they attempt to go to the register form.
I manage to do to that, but I can't validate the register form when the a user who is not logged in complete it.
I have a custom view for signup:
class SignupView(UserPassesTestMixin, FormView):
template_name = 'main/auth/register.html'
form_class = forms.UserCreationForm
def test_func(self):
self.request.user.is_authenticated
def handle_no_permission(self):
if self.request.user.is_authenticated:
return redirect('main:index')
return self.get(self.request)
def get_success_url(self):
redirect_to = self.request.GET.get`enter code here`('next', 'main:index')
return redirect_to
def form_valid(self, form):
response = super().form_valid(form)
form.save()
email = form.cleaned_data.get('email')
first_name = form.cleaned_data.get('first_name')
raw_password = form.cleaned_data.get('password1')
logger.info('Nuevo registro para email=%s a través de SignupView', email)
user = authenticate(email=email, password=raw_password)
login(self.request, user)
form.send_mail()
return response
methods
test_func(self) and handle_no_permission(self) are used to test if the user is authenticated
but I think the problem is in return self.get(self.request) I use it to load the form but when I submit the form with correct data, it POST it to validate it, but just reload it again. I suppose I have to call form_valid but I can't figure out how.
Any help would be appreciated!! Regards
You can use the dispatch method of django CBV. Like below.
class SignupView(UserPassesTestMixin, FormView):
template_name = 'main/auth/register.html'
form_class = forms.UserCreationForm
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('main:index')
return super().dispatch(request, *args, **kwargs)
# other methods
This is function based views and its simple
views.py
to register
def register(request):
if request.user.is_authenticated:
return redirect(reverse('post_list'))
else:
if request.method == 'POST':
user_form = UserRegistrationForm(request.POST)
if user_form.is_valid():
new_user = user_form.save(commit=False)
new_user.set_password(
user_form.cleaned_data['password1']
)
new_user.save()
Profile.objects.create(user=new_user)
return render(
request,
'account/register_done.html',
{'new_user': new_user}
)
else:
user_form = UserRegistrationForm()
return render(
request,
'account/register.html',
{'user_form': user_form}
)
forms.py
class UserRegistrationForm(forms.ModelForm):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput(attrs={
'class': 'myfieldclass',
'placeholder': 'Password ...',
'type': 'password',
'name': 'password'
}))
password2 = forms.CharField(label='Repeat Password', widget=forms.PasswordInput(attrs={
'class': 'myfieldclass',
'placeholder': 'Repeat Password ...',
'type': 'password',
'name': 'password'
}))
username = forms.CharField(label='Username', widget=forms.TextInput(attrs={
'class': 'myfieldclass',
'placeholder': 'Username ...',
'type': 'text',
'name': 'username'
}))
email = forms.CharField(label='Email', widget=forms.EmailInput(attrs={
'class': 'myfieldclass',
'placeholder': 'Email ...',
'type': 'email',
'name': 'email'
}))
class Meta:
model = User
fields = ('username', 'email')
help_texts = {
'username': '',
}
def clean_password2(self):
cd = self.cleaned_data
if cd['password1'] != cd['password2']:
raise forms.ValidationError(
'Passwords dosent Matched.')
return cd['password2']
ERROR: repetitive record violates the singular constraint "user_otherinfo_user_id_key"
DETAIL: The key "(user_id) = (52)" already exists.
models.py
class OtherInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
phone = models.CharField(max_length=11,verbose_name="phone number")
location = models.CharField(max_length=50,verbose_name="location")
profile_image = models.FileField(blank = True,null = True,verbose_name="image")
class Meta:
verbose_name_plural = 'Other informations'
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
OtherInfo.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.otherinfo.save()
forms.py
class LoginForm(forms.Form):
username = forms.CharField(label = "Username")
password = forms.CharField(label = "Parola",widget = forms.PasswordInput)
class RegisterForm(forms.ModelForm):
password1 = forms.CharField(max_length=100, label='Parola',widget=forms.PasswordInput())
password2 = forms.CharField(max_length=100, label='Parola again', widget=forms.PasswordInput())
phone_number = forms.CharField(required=False, max_length=11, label='Phone Number')
location = forms.CharField(required=False, max_length=50, label='Location')
profile_image = forms.FileField(required=False, label="Image")
class Meta:
model = User
fields = [
'first_name',
'last_name',
'email',
'username',
'password1',
'password2',
'phone_number',
'location',
'profile_image',
]
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords do not match!")
return password2
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
class ProfileForm(forms.ModelForm):
class Meta:
model = OtherInfo
fields = ('phone', 'location', 'profile_image')
views.py
#login_required
#transaction.atomic
def updateprofile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, instance=request.user.otherinfo)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, _('Your profile has been successfully updated!'))
return redirect('/user/profile/')
else:
messages.error(request, _('Please correct the following error.'))
else:
user_form = UserForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.otherinfo)
return render(request, 'user/update_user.html', {
'user_form': user_form,
'profile_form': profile_form
})
def register(request):
form = RegisterForm(request.POST or None,request.FILES or None )
if form.is_valid():
user = form.save(commit=False)
first_name = form.cleaned_data.get('first_name')
last_name = form.cleaned_data.get('last_name')
username = form.cleaned_data.get("username")
email = form.cleaned_data.get('email')
password = form.cleaned_data.get('password1')
phone = form.cleaned_data.get('phone_number')
location = form.cleaned_data.get('location')
profile_image = form.cleaned_data.get('profile_image')
user.set_password(password)
user.save()
new_user = authenticate(username=user.username, first_name=first_name, last_name=last_name, email=email, password=password)
OtherInfo.objects.create(user=new_user,phone=phone,location=location,
profile_image=profile_image)
login(request,new_user)
messages.info(request,"Successfully Register ...")
return redirect("/")
context = {
"form" : form
}
return render(request,"user/register.html",context)
In Django, the user can register before updating the profile. When I add the profile update code, now the user is failing to register. How can I solve the problem?
It's simply a matter of not creating twice a OtherInfo for the same User:
in the post_save handler of saving a User, you create a OtherInfo object for the user.
in your view, you first create the User, hence you already create the OtherInfo associated with the user.
then you have OtherInfo.objects.create(user=new_user,phone=phone,location=location,
profile_image=profile_image)
This last instruction will fail, because you're trying to create a second OtherInfo for the same user. So you can do two things:
Remove the post_save signal handler that creates the OtherInfo object
or
Update new_user.otherinfo by setting the various values and saving it instead of creating it.
You need to delete the data entry with id=51.
The error you get is related to database, that the entry with id you are trying to create already exists.
Drop your database and create a new one(If you are not on a production environment)
i want to create a registration view so, it takes username, password, and email for registration and i am using Django User Model, but it is giving me an error.
AttributeError at /signup/
'SignUpForm' object has no attribute 'cleaned_data'
please tell me how to fix this,
forms.py
class SignUpForm(forms.Form):
username = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Username'}))
email = forms.EmailField(widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'Email'}))
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Password'}))
confirm_password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder':
'Confirm Password'}), label="Confirm Password")
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(SignUpForm, self).__init__(*args, **kwargs)
self.fields['username'].validators.append(ForbiddenUsernamesValidator)
self.fields['username'].validators.append(InvalidUsernameValidator)
self.fields['username'].validators.append(UniqueUsernameIgnoreCaseValidator)
self.fields['email'].validators.append(UniqueEmailValidator)
def clean(self):
super(SignUpForm, self).clean()
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if password and password != confirm_password:
self._errors['password'] = self.error_class(['Passwords don\'t match'])
return self.cleaned_data
views.py
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST or None)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
email = form.cleaned_data.get('email')
print(username)
print(password)
print(email)
messages.add_message(request, messages.SUCCESS, 'YOU ARE REGISTRED SUCCESSFULLY')
return render(request, 'authentication/signup.html', {})
else:
return render(request, 'authentication/signup.html', {'form': form})
else:
return render(request, 'authentication/signup.html', {'form': SignUpForm})
def clean(self):
self.cleaned_data = super(SignUpForm, self).clean()
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if password and password != confirm_password:
self._errors['password'] = self.error_class(['Passwords don\'t match'])
return self.cleaned_data
This is my code for autologin after registration.
def authenticationRegister(request):
if request.method == 'POST':
registrationForm = MyRegistrationForm(request.POST)
if registrationForm.is_valid():
user_present = registrationForm.save()
request.session['RegistrationForm_error'] = None
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=username,password=password)
if user is not None:
auth.login(request,user)
request.session['username'] = user.email
request.session['id'] = user.id
return HttpResponseRedirect('/home/')
else:
request.session['RegistrationForm_error'] = registrationForm.errors
return HttpResponseRedirect('/authenticate/')
return HttpResponseRedirect('/authenticate/')
The problem with this code is that auth.authenticate() is returning None.
When I put my user.login() statement outside if then the Anonymous User error appears.
The form is getting saved in db. but auth.authenticate() is returning None.
Code for MyRegistrationForm
class MyRegistrationForm(UserCreationForm):
error_messages = {
'duplicate_email': _("A user with that email already exists."),
'password_mismatch' : _("The two password fields didn't match."),
}
username = forms.CharField(
widget = forms.TextInput(attrs={'placeholder':'Enter Username','class': 'form-control input', 'size': '20'}),
help_text = _("Required. 30 characters or fewer. Letters, digits and #/./+/-/_ only."))
email = forms.EmailField(
widget = forms.TextInput(attrs={'placeholder': 'Email', 'class': 'form-control input', 'size': '20'}))
password1 = forms.CharField(
widget = forms.PasswordInput(attrs={'placeholder': 'Password','class': 'form-control input', 'size': '20'}))
password2= forms.CharField(
widget = forms.PasswordInput(attrs={'placeholder': 'Confirm Password','class': 'form-control input', 'size': '20'}),
help_text = _("Enter the same password as above, for verification."))
class Meta:
model = get_user_model()
fields = ('username', 'email', 'password1', 'password2')
def clean_email(self):
email = self.cleaned_data["email"]
try:
get_user_model()._default_manager.get(email=email)
except get_user_model().DoesNotExist:
return email
raise forms.ValidationError(
self.error_messages['duplicate_email'],
code = 'duplicate_email',)
def save(self, commit=True):
user = super(MyRegistrationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(self.error_messages['password_mismatch'], code = 'password_mismatch', )
return password2
You don't have a key in the POST data called password, because your password fields are password1 and password2. So your dict get is simply returning the default value, the empty string.
As Alasdair says in the comments, you should be using the form cleaned_data anyway. And since you can only get to that point if the form is valid, you know that the values exist, so you shouldn't be using get with a default: just use the standard dict access.
username = form.cleaned_data['username']
password = form.cleaned_data['password1']