Django AbstractUser password is not hashing - django

I've used as a model AbstractUser extended by custom fields, created form automatically by ModelForm. The problem is that, users except superuser cannot log in to system. I think it's reason, their passwords are not hashing. Where should I make it ? Here are my codes.
forms.py:
class CustomUserSignUpForm(ModelForm):
class Meta:
model = CustomUser
fields = ['username', 'password', 'user_image', 'role', 'branch', 'license_number', 'fin_number', 'first_name', 'last_name', 'patronymic', 'phone_number', 'email', 'voen_number', 'is_active']
views.py:
def sign_up(request):
if request.method == 'POST':
form = CustomUserSignUpForm(request.POST)
if form.is_valid():
form.save()
else:
form = CustomUserSignUpForm()
context = {
'form': form,
}
return render(request, 'sign_up.html', context)
models.py:
class CustomUser(AbstractUser):
patronymic = models.CharField(_('Ata adı'), max_length=150, blank=True)
role = models.ForeignKey(Role, on_delete=models.CASCADE, blank=True, null=True)
user_image = models.FileField(_('Profil şəkli'), upload_to='static/assets/images/user-images', blank=True)
branch = models.ForeignKey(Branch, on_delete=models.CASCADE, blank=True, null=True)
phone_number = models.CharField(_('Telefon'), max_length=20, blank=True)
voen_number = models.CharField(_('VÖEN'), max_length=30, blank=True)
fin_number = models.CharField(_('FİN'), max_length=20, blank=True)
license_number = models.CharField(_('Lisenziya'), max_length=40, blank=True)
def __str__(self):
return self.username

To define a function to hash that password, you must inherited save method for you user form
class CustomUserSignUpForm(forms.ModelForm):
............
def save(self, commit=True):
# Save the provided password in hashed format
user = super(CustomUserSignUpForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user

This override of ModelForm is better off, because:
I check if the user exists.
I Hash de password if the password is not encoded.
class UsuarioAdmin(admin.ModelAdmin):
...
def save_model(self, request, obj, form, change):
try:
user_database = USUARIO.objects.get(pk=obj.pk)
except Exception:
user_database = None
if user_database is None \
or not (check_password(form.data['password'], user_database.password)
or user_database.password == form.data['password']):
obj.password = make_password(obj.password)
else:
obj.password = user_database.password
super().save_model(request, obj, form, change)

Related

Django Attach variables from one step to another

How would I grab the 2nd form and add it to the first form then selectively not allow that user to login. In the Doctorwizard done function.
maybe add a variable status?
etc.
username,password,email,first_name,last_name,verified
views.py
from django.core.files.storage import FileSystemStorage
import os
from django.conf import settings
class DoctorWizard(SessionWizardView):
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'doctor'))
template_name = "registration/signup.html"
form_list = [SignUpForm,verify]
def done(self, form_list, **kwargs):
data=process_data(form_list)
return redirect('home')
forms.py
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
last_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
class Meta:
model = Profile
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2', )
class verify(forms.Form):
verified = forms.ImageField(required=True)
class Meta:
model = Profile
fields = ('verified',)
models.py
class Profile(AbstractUser):
bio = models.TextField(max_length=100, blank=True)
phone_number = PhoneNumberField(max_length=25, region="US")
birth_date = models.DateField(blank = True, null = True)
is_doctor = models.BooleanField(default=False)
verified = models.ImageField(upload_to='media/doctor')
date_created = models.DateTimeField(auto_now_add=True)
avatar = models.ImageField(default='default.png', upload_to='')
def done(self, form_list, **kwargs):
process_data(form_list)
userCreate = form_list[0]
userCreate.save()
username = userCreate.cleaned_data.get('username')
raw_password = userCreate.cleaned_data.get('password1')
user = authenticate(username=username, password=raw_password)
if user:
user.verified=form_list[1].cleaned_data.get('verified')
user.is_doctor=True
user.is_active=False
user.save()
Just grab the user and access it's fields.

Django Forn Not Saving Extra Information

I extended the Django AbstratUser so that users can use email to sign in and signup, these work perfectly. The problem I am facing, however, is that the extra information on the extended model is not storing the information in the database, even though the user gets created. Once I hit the submit button, the user and extended model get created, and while the user model stores the information, the extended model is always empty.
I have tried using both signals and #transaction_atomic, yet, I have not been able to figure it out. Maybe I am missing out something, I do not know.
Models.py
class Company(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
name= models.CharField(_('Company name'), max_length=250)
...
#more information
...
class Meta:
verbose_name = _('Company')
verbose_name_plural = _('Companies')
def __str__(self):
return self.name
forms.py
class CompanySignUpForm(CustomUserCreationForm):
name = forms.CharField(widget=TextInput(attrs={'placeholder': 'Company name'}))
...
#more fields
...
class Meta(CustomUserCreationForm.Meta):
model = User
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_company = True
user.save()
company = Company.objects.create(user=user)
company.name = self.cleaned_data.get('name')
...
#more information
...
return user
Views.py
def company_signup(request):
if request.method == 'POST':
form = CompanySignUpForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'accounts/templates/company_success.html')
else:
form = CompanySignUpForm()
return render(request, 'accounts/templates/company_signup.html', context={
'title': _('Create a Company Account'),
'form': form,
})
Edit:
Thanks to #Mandrup, I was able to extend his solution to fit my need.
forms.py
class CompanySignUpForm(CustomUserCreationForm):
name = forms.CharField(widget=TextInput(attrs={'placeholder': 'Company name'}))
number_of_employees = forms.CharField(widget=NumberInput(attrs={'placeholder': 'Number of employees'}))
phone = forms.CharField(widget=TextInput(attrs={'placeholder': 'Contact Number'}))
country = forms.ModelChoiceField(queryset=Country.objects.all(), required=True, empty_label="Country")
class Meta(CustomUserCreationForm.Meta):
model = User
#transaction.atomic
def save(self, commit=True):
user = super(CompanySignUpForm, self).save(commit=False)
if commit:
user.is_company = True
user.save()
name = self.cleaned_data.get('name')
number_of_employees = self.cleaned_data.get('number_of_employees')
phone = self.cleaned_data.get('phone')
country = self.cleaned_data.get('country')
company = Company(user=user, name=name, number_of_employees=number_of_employees, phone=phone, country=country)
company.save()
return user
Edit:
This worked for me when i tried to create an extended user profile. I changed it to fit your needs.
Model:
class Company(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
name= models.CharField(max_length=250)
...
#more information
...
def __str__(self):
return self.name
Form:
class RegisterUserForm(UserCreationForm):
class Meta:
model = User
fields = ["username", "email", "password1", "password2"]#add whatever fields you want to here
def save(self, commit=True):
user = super(RegisterUserForm, self).save(commit=False)
if commit:
user.save()
company = Company(user=user, name='Company name')
company.save()
return user

Django - ModelForm initial data from database

In my Django project i create an app to have additional information about registered users. So my model looks like this:
class UserProfile(models.Model):
class Meta:
verbose_name_plural = u'User Profile'
user = models.OneToOneField(User)
birthday = models.DateField(blank=True, null=True)
avatar = models.ImageField(upload_to='media/profile/avatar', blank=True, null=True)
name = models.CharField(blank=True, null=True, max_length=20)
surname = models.CharField(blank=True, null=True, max_length=50)
phone = models.CharField(blank=True, null=True, max_length=12)
def __unicode__(self):
return '%s' % self.user
In user profile i create modelform where user can fill or edit the fields from UserProfile model:
class ExtraProfileDataForm(ModelForm):
name = forms.CharField(label=(u'Enter your name'))
surname = forms.CharField(label=(u'Enter your surname'))
phone = forms.CharField(label=(u'Enter your phone'))
birthday = forms.DateField(label=(u'Enter birthday'))
avatar = forms.ImageField(label=(u'Enter avatar'))
class Meta:
model = UserProfile
fields = ('name', 'surname', 'phone', 'birthday', 'avatar')
def __init__(self, *args, **kwargs):
super(ExtraProfileDataForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
This is the view of the model form:
#login_required
def UserFullDataForm(request):
if request.method == 'POST':
form = ExtraProfileDataForm(request.POST)
if form.is_valid():
profile_user = request.user
user_profile = UserProfile(user=profile_user)
user_profile.name = form.cleaned_data['name']
user_profile.surname = form.cleaned_data['surname']
user_profile.phone = form.cleaned_data['phone']
user_profile.birthday = form.cleaned_data['birthday']
user_profile.avatar = form.cleaned_data['avatar']
user_profile.save()
return HttpResponseRedirect('/profile/')
else:
return render(request, 'profiles/extra_profile.html', {'form':form})
else:
form = ExtraProfileDataForm()
context = {'form':form}
return render (request, 'profiles/extra_profile.html', context)
But i want to load on ExtraProfileDataForm initial data from model UserProfile if the fields not empty. I searched how to do that on Django documentation website, but nothing found. Can somebody help me to understand how to do it? Thanks a lot.
You use the instance parameter.
Note that you are doing much more work than necessary here; most of your view can be cut.
#login_required
def UserFullDataForm(request):
try:
profile = request.user.userprofile
except UserProfile.DoesNotExist:
profile = UserProfile(user=request.user)
if request.method == 'POST':
form = ExtraProfileDataForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/profile/')
else:
form = ExtraProfileDataForm(instance=profile)
return render(request, 'profiles/extra_profile.html', {'form':form})
Similarly, in your form, you don't need the overridden __init__ method because you're manually specifying all the fields anyway; you can add required=False on each one there. However, you could make this even shorter by adding the labels in the model definition; then your entire modelform could just be:
class ExtraProfileDataForm(ModelForm):
class Meta:
model = UserProfile
fields = ('name', 'surname', 'phone', 'birthday', 'avatar')
One final note: you're consistently using three-space indentation, which is a bit, well, odd. Most Python programmers prefer two or four.

Django form with many-to-many relationship does not save

I have a custom registration form for my users to add a profile on my app. However, a bug has recently popped up in that the form is not saving the information that is put into all the fields.
My user model, MyUser has a ManyToMany relationship with another model, Interest, and this is where the issues are arising. I am not sure if it is the RegistrationForm or the register view that is causing it, so I have included both below, as well as the model code.
I also have a view for the users to update their profile, also included, once it is created, and this is working absolutely perfectly. This is the personal view.
As I say, it is only the Interest field that is not being returned, even though it is being filled in on the registration page.
Any help or advice is much appreciated, thanks.
models.py
class Interest(models.Model):
title = models.TextField()
def __unicode__(self):
return self.title
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
date_of_birth = models.DateField()
course = models.ForeignKey(Course, null=True)
location = models.ForeignKey(Location, null=True)
interests = models.ManyToManyField(Interest, null=True)
bio = models.TextField(blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['date_of_birth']
views.py
def register(request):
if request.method == 'POST':
form = RegistrationForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('/friends/home/')
else:
form = RegistrationForm()
template = "adduser.html"
data = { 'form': form, }
return render_to_response(template, data, context_instance=RequestContext(request))
#login_required(login_url='/friends/login/')
def personal(request):
"""
Personal data of the user profile
"""
profile = request.user
if request.method == "POST":
form = ProfileForm(request.POST, instance=profile)
if form.is_valid():
form.save()
messages.add_message(request, messages.INFO, _("Your profile information has been updated successfully."))
return redirect('/friends/success/')
else:
form = ProfileForm(instance=profile)
template = "update_profile.html"
data = { 'section': 'personal', 'form': form, }
return render_to_response(template, data, context_instance=RequestContext(request))
forms.py
class RegistrationForm(forms.ModelForm):
"""
Form for registering a new account.
"""
email = forms.EmailField(widget=forms.TextInput, label="Email")
password1 = forms.CharField(widget=forms.PasswordInput,
label="Password")
password2 = forms.CharField(widget=forms.PasswordInput,
label="Password (again)")
course = forms.ModelChoiceField(queryset=Course.objects.order_by('title'))
location = forms.ModelChoiceField(queryset=Location.objects.order_by('location'))
class Meta:
model = MyUser
fields = [
'first_name',
'last_name',
'date_of_birth',
'email',
'password1',
'password2',
'course',
'location',
'interests',
'bio',
]
def __init__(self, *args, **kwargs):#Sort interests alphabetically
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['interests'].queryset = Interest.objects.order_by('title')
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise forms.ValidationError("Passwords don't match. Please enter again.")
return self.cleaned_data
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user
Since you use commit=false for the super(RegistrationForm, self).save call, it doesn't save the many-to-many field. You therefore need to add self.save_m2m() after user.save() in your save() method of RegistrationForm.
See https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
EDIT: save_m2m() is on the Form, not the Model

How to customize user profile when using django-allauth

I have a django project with the django-allauth app. I need to collect additional data from the user at signup. I came across a similar question here but unfortunately, no one answered the profile customization part.
Per the documentation provided for django-allauth:
ACCOUNT_SIGNUP_FORM_CLASS (=None)
A string pointing to a custom form class (e.g. ‘myapp.forms.SignupForm’) that is used during signup to ask the user for additional input (e.g. newsletter signup, birth date). This class should implement a ‘save’ method, accepting the newly signed up user as its only parameter.
I am new to django and am struggling with this. Can someone provide an example of such a custom form class? Do I need to add a model class as well with a link to the user object like this ?
Suppose you want to ask the user for his first/last name during signup. You'll need to put these fields in your own form, like so:
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30, label='Voornaam')
last_name = forms.CharField(max_length=30, label='Achternaam')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
Then, in your settings point to this form:
ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'
Note that SignupForm cannot be defined in the same file as form overrides through ACCOUNT_FORMS or SOCIALACCOUNT_FORMS, because that would lead to a circular import error.
That's all.
Using the solution suggested by pennersr I was getting a DeprecationWarning:
DeprecationWarning: The custom signup form must offer a def signup(self, request, user) method DeprecationWarning)
This is because as of version 0.15 the save method has been deprecated in favour of a def signup(request, user) method.
So to solve this, the code of the example should be like this:
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30, label='Voornaam')
last_name = forms.CharField(max_length=30, label='Achternaam')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
Here's what worked for me combining a few of the other answers (none of them are 100% complete and DRY).
In yourapp/forms.py:
from django.contrib.auth import get_user_model
from django import forms
class SignupForm(forms.ModelForm):
class Meta:
model = get_user_model()
fields = ['first_name', 'last_name']
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
And in settings.py:
ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'
This way it uses the model forms so that it's DRY, and uses the new def signup. I tried putting 'myproject.myapp.forms.SignupForm' but that resulted in a error somehow.
#Shreyas: The below solution may not be the cleanest, but it works. Please let me know if you have any suggestions to clean it up any further.
To add information that does not belong to the default user profile, first create a model in yourapp/models.py. Read the general django docs to learn more about it, but basicly:
from django.db import models
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
organisation = models.CharField(organisation, max_length=100, blank=True)
Then create a form in yourapp/forms.py:
from django import forms
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30, label='Voornaam')
last_name = forms.CharField(max_length=30, label='Achternaam')
organisation = forms.CharField(max_length=20, label='organisation')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
# Replace 'profile' below with the related_name on the OneToOneField linking back to the User model
up = user.profile
up.organisation = self.cleaned_data['organisation']
user.save()
up.save()
In your users/forms.py you put:
from django.contrib.auth import get_user_model
class SignupForm(forms.ModelForm):
class Meta:
model = get_user_model()
fields = ['first_name', 'last_name']
def save(self, user):
user.save()
In settings.py you put:
ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'
In this way you don't break DRY principle by multiplicity User models fields definition.
I've tried many different tutorials and all of them is missing something, repeating unnecessary code or doing weird things, bellow follows my solution that joins all the options that I've found, it's working, I have already put it in production BUT it still not convincing me because I would expect to receive first_name and last_name inside the functions that I attached to Users create to avoid creating a profile inside the form but I couldn't, by the away I think it will help you.
Models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
nationality = models.CharField(max_length=2, choices=COUNTRIES)
gender = models.CharField(max_length=1, choices=GENDERS)
def __str__(self):
return self.user.first_name
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
Forms.py
class SignupForm(forms.ModelForm):
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
class Meta:
model = Profile
fields = ('first_name', 'last_name', 'nationality', 'gender')
def signup(self, request, user):
# Save your user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
user.profile.nationality = self.cleaned_data['nationality']
user.profile.gender = self.cleaned_data['gender']
user.profile.save()
Settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'apps.profile.forms.SignupForm'
#models.py
from django.conf import settings
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(default='users/default.png', upload_to='users')
fields = models.ForeignKey('Field' ,null=True ,on_delete=models.SET_NULL)
category = models.ForeignKey('Category' ,null=True ,on_delete=models.SET_NULL)
description = models.TextField()
interests = models.ManyToManyField('Interests')
...
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
...
def userprofile_receiver(sender, instance, created, *args, **kwargs):
if created:
userprofile = UserProfile.objects.create(user=instance)
else:
instance.userprofile.save()
post_save.connect(userprofile_receiver, sender=settings.AUTH_USER_MODEL)
#forms.py
class SignupForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SignupForm, self).__init__(*args, **kwargs)
self.fields['first_name'].widget = forms.TextInput(attrs={'placeholder': 'Enter first name'})
self.fields['last_name'].widget = forms.TextInput(attrs={'placeholder': 'Enter last name'})
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
interests = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, help_text="Choose your interests", queryset=Interests.objects.all())
image = forms.ImageField(help_text="Upload profile image ")
fields = forms.ChoiceField(help_text="Choose your fields ")
category = forms.ChoiceField(help_text="Choose your category")
class Meta:
model = UserProfile
fields = ('first_name', 'last_name', 'name', 'image', 'fields', 'category', 'description', 'phone', 'facebook', 'twitter', 'skype', 'site', 'address', 'interests' ,'biography')
widgets = {
...
'description': forms.TextInput(attrs={'placeholder': 'Your description'}),
'address': forms.TextInput(attrs={'placeholder': 'Enter address'}),
'biography': forms.TextInput(attrs={'placeholder': 'Enter biography'}),
....
}
def signup(self, request, user):
# Save your user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
user.userprofile.image = self.cleaned_data.get('image')
user.userprofile.fields = self.cleaned_data['fields']
user.userprofile.category = self.cleaned_data['category']
user.userprofile.description = self.cleaned_data['description']
interests = self.cleaned_data['interests']
user.userprofile.interests.set(interests)
user.userprofile.save()
# settings.py or base.py
ACCOUNT_SIGNUP_FORM_CLASS = 'nameApp.forms.SignupForm'
That is it. (:
Create a Profile Model with user as OneToOneField
class Profile(models.Model):
user = models.OneToOneField(User, verbose_name=_('user'), related_name='profiles')
first_name=models.CharField(_("First Name"), max_length=150)
last_name=models.CharField(_("Last Name"), max_length=150)
mugshot = ImageField(_('mugshot'), upload_to = upload_to, blank=True)
phone= models.CharField(_("Phone Number"), max_length=100)
security_question = models.ForeignKey(SecurityQuestion, related_name='security_question')
answer=models.CharField(_("Answer"), max_length=200)
recovery_number= models.CharField(_("Recovery Mobile Number"), max_length=100)
city=models.ForeignKey(City,related_name='city', blank=True, null=True, help_text=_('Select your City'))
location=models.ForeignKey(Country,related_name='location', blank=True, null=True, help_text=_('Select your Location'))