I have written a custom form for the creation of a user.
The login works for the users created on the terminal with "python manage.py createsuperuser", but it doesn't work for the users created on the website. I have checked, and the creation form works; I mean, the user is created.
This is the form
class CustomUserCreationForm(UserCreationForm):
"""
A form that creates a user, with no privileges, from the given email and password.
"""
def __init__(self, *args, **kwargs):
super(CustomUserCreationForm, self).__init__(*args, **kwargs)
class Meta:
model = CustomUser
fields = ("email",)
The model
class CustomUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=254, unique=True, null=True)
first_name = models.CharField(max_length=254, blank=True)
second_name = models.CharField(max_length=254, blank=True)
email = models.EmailField(blank=True, unique=True)
address1 = models.CharField(max_length=254, blank=True)
address2 = models.CharField(max_length=254, blank=True)
area_code = models.CharField(max_length=20, blank=True)
country_code = models.CharField(max_length=10, blank=True)
date_joined = models.DateTimeField(_('date joined'), default=datetime.now())
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'address1', 'address2', 'area_code', 'country_code']
objects = CustomUserManager()
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_absolute_url(self):
return "/users/%s" % urlquote(self.email)
def get_full_name(self):
"""
Returns the first_name plus the last_name, with a space in between.
"""
return full_name.strip()
def get_short_name(self):
"""
Returns the first name for the user.
"""
return self.first_name
def email_user(self, subject, message, from_email=None):
"""
Sends an email to this user.
"""
send_email(subject, message, from_email, [self.email])
The register view
def register(request):
registered = False
if request.method == 'POST':
user_form = CustomUserCreationForm(data=request.POST)
if user_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
registered = True
else:
print user_form.errors
else:
user_form = CustomUserCreationForm()
return render(request, 'register.html', {'user_form': user_form, 'registered': registered})
The login view
def login(request):
if request.method == 'POST':
login_form = CustomUserLoginForm(data=request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
user = auth.authenticate(username=username, password=password)
if user:
if user.is_active:
auth.login(request, user)
return HttpResponseRedirect('/')
else:
return HttpResponse("Your account is disabled.")
else:
print "Invalid login details: {0}, {1}".format(username, password)
return HttpResponse("Invalid login details supplied. Get back to the homepage.")
else:
login_form = CustomUserLoginForm()
return render(request, 'login.html', {'login_form': login_form})
What am I missing here?
Thanks!
First fix the class:
class CustomUser(AbstractUser, PermissionsMixin):
AbstractUser is a real User Model, the BaseUser is just the meta class.
To create a new user you should use this line, in the form or in the register view:
User.objects.create_user(username=username,email=email,password=password)
Note that set_password is used to change the password, and it logs out this user after the password changes.
For almost three days I was trying to overcome same issue. I even wrote AUTHENTICATION_BACKEND. Only after that, a message was displayed on the form that the user is not active. When in save method I assigned True to user.is_active
def save(self):
user = super().save(commit=False)
user.is_active = True
user.save()
This worked for me. I hope it will work for you as well. (Note: I was not aware of rules and posted same message for similar questions to help others as well, when get notice not to do it. If anyone will see my answer for other questions please ignore it. The above answer is edited one and mostly related to this exact issue)
Related
so I have this form as on this image below. The point is that if the user clicks any of the fields, the text (which is essentialy a label, not a placeholder) rises up which shows that the clicked input field is active.
However, I've tried to create a customized user creation in Django, which is just to have the email as the main user identificator, not the username. Now everything else works as supposed, but the thing is that the email field always appears as active (you know, the css attribute).
I will now paste a scary amount of code and I will be happy if you could find out what's wrong. Sorry for the trouble
`class UserManager(BaseUserManager):
# handles user creation, havent' tested yet
def create_user(self, email, username, password=None):
# creates user with email as their id
if not email:
raise ValueError('Users must have an email address')
if not username:
raise ValueError('Users must have an username')
if not password:
raise ValueError('Users must have a password')
user = self.model(
email=self.normalize_email(email),
password=password,
username=username,
)
user.set_password(password)
user.save(user=self._db)
return user
class User(AbstractBaseUser):
# user model, was just trying out some stuff
username = models.CharField(verbose_name='username', max_length=30)
email = models.EmailField(verbose_name='email', unique=True,
max_length=60)
date_joined = models.DateTimeField(verbose_name='date joined',
auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login',
auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'email', 'password']
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True`
Now the form part
class RegisterForm(UserCreationForm):
# form to create a new user
class Meta:
model = User
fields = ('username', 'email)'
And the view part
class RegisterView(FormView):
# registration form
form_class = RegisterForm
template_name = 'register.html'
def post(self, request):
form = RegisterForm(request.POST)
if form.is_valid():
print('yes')
return redirect('login')
return render(request, 'register.html', {'form': form})
Again, I am so so sorry for the vaste amount of code, it's just that I'm lost and since I'm just learning this stuff it's hard for me to spot the error.
Thank you so much for any feedback
When a user clicks an account activation link, they are redirected to my activate view:
def activate(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = CustomUser.objects.get(pk=uid)
except(TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and account_activation_token.check_token(user, token):
user.is_active = True
user.save()
login(request, user)
return redirect('/')
else:
return HttpResponse('Activation link is invalid!')
For some reason, when they are redirected with redirect('/'), they are not automatically logged in, i.e., in the index template, user.is_authenticated is False.
If anybody knows why this is, and could help me automatically log the user in after clicking the activation link, I would be very grateful for their help.
Thanks,
Jack
I have a custom login view, and all works fine when I log users in with this. Confusingly, it uses the same code as the activate view to log users in.
def custom_login(request):
if request.user.is_authenticated:
return redirect('/')
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
if user.is_active:
print(user)
login(request, user)
return redirect('/')
else:
return redirect('/login/')
else:
return redirect('/login/')
else:
form = LoginForm(label_suffix="")
return render(request, 'core/login.html', {'form': form })
The relevant parts of my SETTINGS.py are:
# Specify login url
LOGIN_URL = "/login/"
# Specify login/ logout redirect URLs
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/"
Here is the CustomUser model:
class CustomUser(AbstractBaseUser):
username = models.CharField(max_length=200, unique=True, verbose_name='Username')
first_name = models.CharField(max_length=200, blank=True, null=True)
last_name = models.CharField(max_length=200, blank=True, null=True)
email = models.EmailField(max_length=255, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
about = models.TextField(max_length=1000, null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
university = models.ForeignKey(University, on_delete=models.CASCADE, blank=True,
null=True, default=None)
profile_image = models.ImageField(upload_to='profile_images/', blank=True, null=True)
objects = CustomUserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
def __str__(self):
return self.username
def has_perm(self, perm, obj=None):
if self.is_admin:
return True
return False
def has_module_perms(self, app_label):
if self.is_admin:
return True
return False
def num_points(self):
postVotes = self.post_set.aggregate(models.Count('votes'))["votes__count"]
commentVotes = self.comment_set.aggregate(models.Count('votes'))["votes__count"]
if postVotes is None: postVotes = 0
if commentVotes is None: commentVotes = 0
return postVotes + commentVotes
def num_posts(self):
return self.post_set.count()
def num_comments(self):
return self.comment_set.count()
def latest_activities(self):
# nola = number of latest activities
nola = 10
latest_posts = Post.objects.filter(user=self).order_by('-datetime')[:nola]
latest_comments = Comment.objects.filter(user=self).order_by('-datetime')[:nola]
latest_subcomments = Subcomment.objects.filter(user=self).order_by('-datetime')[:nola]
return sorted(chain(latest_posts, latest_comments, latest_subcomments),
key=lambda e: e.datetime, reverse=True)[:nola]
def save(self, *args, **kwargs):
# delete old file when replacing by updating the file
try:
this = CustomUser.objects.get(id=self.id)
if this.profile_image != self.profile_image:
this.profile_image.delete(save=False)
print("deleting_old_image")
except:
# when new photo then we do nothing, normal case
pass
super().save(*args, **kwargs)
I try to save two forms in registration. I can see the auth form save but the second form is not pass .is_valid(). Could you please let me know what is wrong?
Models.py
class School(models.Model):
id = models.IntegerField(primary_key=True)
Name = models.CharField(max_length=50, null=False)
Domain = models.CharField(max_length=50, null=False)
Mascot = models.ImageField(null=True, upload_to='mascot')
def delete(self, *args, **kwargs):
self.Mascot.delete()
super(School, self).delete(*args, **kwargs)
def __str__(self):
return self.Name
class HeepooUser(models.Model):
user = models.OneToOneField(User)
phone = models.CharField(max_length=15, null=True)
allow_phone = models.BooleanField(default=False)
school_id = models.IntegerField()
date_join = models.DateTimeField(auto_now_add=True)
forms.py
class UserForm(forms.ModelForm):
email = forms.EmailField(max_length=50, required=True)
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('email', 'password')
class RegisterForm(forms.ModelForm):
school_id = forms.ModelChoiceField(queryset=School.objects.all())
phone = forms.CharField(max_length=15, min_length=10, required=False)
class Meta:
model = HeepooUser
fields = ('phone', 'school_id')
views.py
def register(request):
registered = False
if request.method == 'POST':
user_form = UserForm(request.POST)
profile_form = RegisterForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
user.set_password(user.password)
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile = profile_form.save()
registered = True
else:
return HttpResponse('Wrong access1')
else:
user_form = UserForm()
profile_form = RegisterForm()
return render(request, "register.html", {
'user_form': user_form,
'profile_form': profile_form,
'registered': registered,
})
I try to save email and password to auth_user and school_id and phone to separate table.
All the best!
tested your code and what I've encountered when submitting a form is
school_id value must be an integer
I'm suggesting to set school_id/school to be a foreignKey of the School model
class HeepooUser(models.Model):
user = models.OneToOneField(User)
phone = models.CharField(max_length=15, null=True)
allow_phone = models.BooleanField(default=False)
school_id = models.ForeignKey(School)
date_join = models.DateTimeField(auto_now_add=True)
so that we could just do the forms like this
class RegisterForm(forms.ModelForm):
class Meta:
model = HeepooUser
exclude = ('allow_phone', 'user')
also I think you don't need to specify the form fields for UserForm since by default django user only requires a password, username, and email
The problem is with how binary ANDs work. If user_form.is_valid() returns False, the "if" statement marks the whole statement as False without needing to evaluate profile_form.is_valid(). Therefore, profile_form.is_valid() never gets called and it's errors dict will not get populated. Unfortunately, django's form is_valid() does more than just return a boolean and has the side effect of populating that errors dict.
if user_form.is_valid() and profile_form.is_valid():
...
One thing you might be able to do is something like this:
user_valid = False
if user_form.is_valid():
user_valid = True
profile_valid = False
if profile_form.is_valid():
profile_valid = True
if user_valid and profile_valid:
... do something
The above ensures that both forms get processed. There might be a better way to express it, but that's the idea.
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
I am using Django 1.5. I am a custom User model like this:
class User(AbstractBaseUser):
#id = models.IntegerField(primary_key=True)
#identifier = models.CharField(max_length=40, unique=True, db_index=True)
username = models.CharField(max_length=90, unique=True, db_index=True)
create_time = models.DateTimeField(null=True, blank=True)
update_time = models.DateTimeField(null=True, blank=True)
email = models.CharField(max_length=225)
#password = models.CharField(max_length=120)
external = models.IntegerField(null=True, blank=True)
deleted = models.IntegerField(null=True, blank=True)
purged = models.IntegerField(null=True, blank=True)
form_values_id = models.IntegerField(null=True, blank=True)
disk_usage = models.DecimalField(null=True, max_digits=16, decimal_places=0, blank=True)
objects = UserManager()
USERNAME_FIELD = 'email'
class Meta:
db_table = u'galaxy_user'
I have a custom authentication:
class AuthBackend:
def authenticate(self, username=None, password=None):
if '#' in username:
kwargs = {'email': username}
else:
kwargs = {'username': username}
try:
user = User.objects.get(**kwargs)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Even after entering the correct username and password check_password() always returning false so that I can't login. I tried that in terminal too:
user.check_password(password)
is always returning False.
#views.py:
def login_backend(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
state = "Username or Password Incorrect!"
if user is not None:
login(request, user)
return HttpResponseRedirect('/overview/')
else:
return render_to_response('login_backend.html', {'state':state}, context_instance=RequestContext(request))
else:
return render_to_response('login_backend.html', context_instance=RequestContext(request))
The problem is that when you create your CustomUser, you save the password in open-way(without hashing). Can you give me your RegistrationForm code?
In my case:
# forms/register.py
class RegistrationForm(forms.ModelForm):
"""
Form for registering a new account.
"""
class Meta:
model = CustomUser
fields = ['username', 'password', 'email']
Register-handler:
# views.py
def register(request):
"""
User registration view.
"""
if request.method == 'POST':
form = RegistrationForm(data=request.POST)
if form.is_valid():
user = form.save() # Save your password as a simple String
return redirect('/')
else:
form = RegistrationForm()
return render(request, 'news/register.html', {'form': form})
So when you try to login:
if user.check_password(password):
return user
check_password always returns False.
Solution:
To set password properly, you should redefine save() method in RegistrationForm:
# forms/register.py
class RegistrationForm(forms.ModelForm):
"""
Form for registering a new account.
"""
class Meta:
model = CustomUser
fields = ['username', 'password', 'email']
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.set_password(user.password) # set password properly before commit
if commit:
user.save()
return user
Or simply change handler:
def register(request):
"""
User registration view.
"""
if request.method == 'POST':
form = RegistrationForm(data=request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(request.POST["password"])
user.save()
return redirect('/')
else:
form = RegistrationForm()
return render(request, 'news/register.html', {'form': form})