So I've created 3 different users: admins, developers, and project managers. When I use the individual signup forms for each of these users and log out, it works, but then I when try to use the login form, it seems to me that it's acting like the signup form. Because when I input the same user details as the one I just created into the login form, it throws up the built-in error message, 'A user with that user name already exists' I'm not sure how to proceed from here.
Here's what I have so far.
models.py
class CustomUser(AbstractUser):
ACCOUNT_TYPE_CHOICES = (
('admin', 'Admin'),
('developer', 'Developer'),
('project_manager', 'Project Manager')
)
account_type = models.CharField(max_length=20, choices=ACCOUNT_TYPE_CHOICES)
login and signupviews
class LoginView(View):
def get(self, request):
# Render the login form
form = LoginForm()
return render(request, 'users/login.html', {'form': form})
def post(self, request):
# Get the form data from the request
form = LoginForm(request.POST)
# Validate the form data
if form.is_valid():
# Get the username and password from the form
username = form.cleaned_data['username']
password = form.cleaned_data['password']
# Authenticate the user
user = authenticate(request, username=username, password=password)
# If the user is authenticated, log them in and redirect to the homepage
if user is not None:
login(request, user)
if user.account_type == 'admin':
return redirect('admin_home')
elif user.account_type == 'developer':
return redirect('developer_home')
elif user.account_type == 'project_manager':
return redirect('projectmanager_home')
return redirect('home')
# If the user is not authenticated, render the login form again with an error message
else:
form.add_error(None, 'Invalid username or password')
return render(request, 'users/login.html', {'form': form})
else:
return render(request, 'users/login.html', {'form': form})
class LogoutView(View):
def get(self, request):
# Log out the user and redirect to the login page
logout(request)
return redirect('home')
class AdminSignUpView(CreateView):
form_class = AdminSignUpForm
template_name = 'users/admin_signup.html'
success_url = '/admin_home/'
def form_valid(self, form):
# Create the user and log them in
user = form.save()
login(self.request, user)
return redirect(self.success_url)
class DeveloperSignUpView(CreateView):
form_class = DeveloperSignUpForm
template_name = 'users/developer_signup.html'
success_url = '/developer_home/'
def form_valid(self, form):
# Create the user and log them in
user = form.save()
login(self.request, user)
return redirect(self.success_url)
class ProjectManagerSignUpView(CreateView):
form_class = ProjectManagerSignUpForm
template_name = 'users/projectmanager_signup.html'
success_url = '/projectmanager_home/'
def form_valid(self, form):
# Create the user and log them in
user = form.save()
login(self.request, user)
return redirect(self.success_url)
forms.py
class AdminSignUpForm(forms.ModelForm):
# Define the form fields for the admin sign-up form
account_type = forms.CharField(max_length=20, widget=forms.HiddenInput(), initial='admin')
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
email = forms.EmailField()
username = forms.CharField(max_length=150)
password = forms.CharField(max_length=128, widget=forms.PasswordInput())
class Meta:
model = CustomUser
fields = ['first_name', 'last_name', 'email', 'username', 'password']
class DeveloperSignUpForm(forms.ModelForm):
# Define the form fields for the developer sign-up form
account_type = forms.CharField(max_length=20, widget=forms.HiddenInput(), initial='developer')
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
email = forms.EmailField()
username = forms.CharField(max_length=150)
password = forms.CharField(max_length=128, widget=forms.PasswordInput())
class Meta:
model = CustomUser
fields = ['first_name', 'last_name', 'email', 'username', 'password']
class ProjectManagerSignUpForm(forms.ModelForm):
# Define the form fields for the admin sign-up form
account_type = forms.CharField(max_length=20, widget=forms.HiddenInput(), initial='project_manager')
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
email = forms.EmailField()
username = forms.CharField(max_length=150)
password = forms.CharField(max_length=128, widget=forms.PasswordInput())
class Meta:
model = CustomUser
fields = ['first_name', 'last_name', 'email', 'username', 'password']
class LoginForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ['username', 'password']
widgets = {
'password': forms.PasswordInput(),
}
login template
{% block content %}
<h1>Login</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Log in</button>
</form>
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% endblock %}
Let me know if you need any more information.
Related
I am following a tutorial for Login in Django and after finishing I found that the Login Form requires the Username and Password but I want to replace the username with the user's Email instead.
Here is the views.py
#login_required
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Your account has been created! You are now able to log in')
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form': form})
#login_required
def profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST,
request.FILES,
instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request, f'Your account has been updated!')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form': u_form,
'p_form': p_form
}
return render(request, 'users/profile.html', context)
Here is the forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
Here is the template
<form class="login100-form validate-form" method="POST">
<span class="login100-form-title p-b-34">
Account Login
</span>
<fieldset class="input100 m-b-20">
{% csrf_token %}
{{ form|crispy }}
</fieldset>
<div class="container-login100-form-btn">
<button class="login100-form-btn" type="submit" style="width: 90%">
Sign in
</button>
</div>
<div class="w-full text-center p-t-27 p-b-100">
<a class="txt2" href="{% url 'password_reset' %}">Forgot Password ?</a>
</div>
</form>
My question how to change from username to user's email?
To create a Django login with email We have to overwrite Default User, We can start writing models, but first you might want to create different app for your users (its a good practice).
Models.py
class User(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False) # a superuser
# notice the absence of a "Password field", that is built in.
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [] # Email & Password are required by default.
def get_full_name(self):
# The user is identified by their email address
return self.email
Django has built-in methods for the User Manager. We have to customize them in order to make our custom user model work correctly.
class UserManager(BaseUserManager):
def create_user(self, email, password=None):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
Then you need to run migrations.
python manage.py makemigrations
python manage.py migrate
Here's the post you can follow: here
go to your models.py, and add
USERNAME_FIELD = 'email'
remove username from form.
I'd recommend using django-allauth. It's a powerful tool for authenticating users and you can set the user to log-in with email instead of username by adding this to your settings.py:
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_AUTHENTICATION_METHOD='email'
You can do that in two ways. By using AbstractUser or AbstractBaseUser
AbstractUser: define your user class by subclassing AbstractUser . AbstractUser class has all the necessary fields that you might need in user(you can add more). You can do something like setting email as username field and making username field as null. Use this option if you are good with the existing fields on the User model and just want to remove the username field.
class User(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
AbstractBaseUser:define your user class by subclassing AbstractBaseUser.It doesnt have existing fields.Use this option if you want to start from scratch by creating your own, completely new User model.
class User(AbstractBaseUser):
email = models.EmailField(('email address'), unique=True)
active = models.BooleanField(default=True) # can login
staff = models.BooleanField(default=False) # staff user non super
admin = models.BooleanField(default=False) # superuser
first_name = models.CharField(max_length=30, blank=True, null= True)
last_name = models.CharField(max_length=30, blank=True, null= True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
You need to write your custom manager depending on your need.
You can create backends.py
def authenticate(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):
# print(user.username)
return user
except User.DoesNotExist:
return None
class EmailOrUsernameModelBackend(object):
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):
# print(user.username)
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
After that, add the following code in settings.py
AUTHENTICATION_BACKENDS = (
'users.backends.EmailOrUsernameModelBackend',
'django.contrib.auth.backends.ModelBackend',
)
Then modify views.py
from django.contrib.auth import login as auth_login
def ajax_login(request):
if request.method == 'POST':
username = request.POST.get('email', '').strip()
password = request.POST.get('password', '').strip()
if username and password:
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
auth_login(request, user, backend='users.backends.EmailOrUsernameModelBackend')
try:
order = Order.objects.filter(user=request.user)
except Order.DoesNotExist:
order = None
if order is not None:
exist_order = True
else:
exist_order = False
data = {'success': True, 'order': exist_order}
else:
data = {'success': False, 'message': 'User is not active'}
else:
data = {'success': False, 'message': 'Invalid username or password'}
return JsonResponse(data)
I want to add a custom field called phone but I get this error:
'User' object has no attribute 'phone'
What I did in models.py is:
class Customer(models.Model):
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
...
phone = models.CharField(max_length=17, null=True, unique=True)
...
def __str__(self):
return str(self.user)
and I have forms.py
class CreatUserForm(UserCreationForm):
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
first_name = forms.CharField()
last_name = forms.CharField()
email = forms.EmailField()
phone = forms.CharField(validators=[phone_regex], max_length=17)
class Meta:
model = User
fields = ['first_name', 'last_name', 'username', 'email', 'phone', 'password1', 'password2']
and finally in views.py I have the following function:
#unauthenticated_user
def registerPage(request):
form = CreatUserForm()
if request.method == 'POST':
form = CreatUserForm(request.POST)
if form.is_valid():
user = form.save()
username = form.cleaned_data.get('username')
group = Group.objects.get(name='customers')
user.groups.add(group)
first_name = user.first_name
last_name = user.last_name
email = user.email
phone = user.phone
Customer.objects.create(
user=user, first_name=first_name, last_name=last_name, email=email, phone=phone,
)
messages.success(request, username + ' created!')
return redirect('login')
context = {'form': form}
return render(request, 'main/page-register.html', context)
But I can't get phone from my users!
It is likely better to work with two forms here, one for the User, and one for the Customer, and then "link" the two at the view level:
class CustomerForm(ModelForm):
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message='Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.')
phone = forms.CharField(validators=[phone_regex], max_length=17)
class Meta:
model = Customer
fields = ['phone']
then we can combine both forms with:
#unauthenticated_user
def registerPage(request):
if request.method == 'POST':
form = UserCreationForm(request.POST, request.FILES)
form2 = CustomerForm(request.POST, request.FILES)
if form.is_valid() and form2.is_valid():
user = form.save()
form2.instance.user = user
form2.save()
group = Group.objects.get(name='customers')
user.groups.add(group)
messages.success(request, username + ' created!')
return redirect('login')
else:
form = UserCreationForm()
form2 = CustomerForm()
context = {'form': form, 'form2': forom2}
return render(request, 'main/page-register.html', context)
in the template, then both forms are rendered:
<form action="…" method="post">
{% csrf_token %}
{{ form1 }}
{{ form2 }}
Create customer
</form>
Your customer should not have the same fields as the user: you are then duplicating data, and it is hard to keep data in sync.
I created a field in my signup form asking users for a link to their linkedin profile.
I created a page that returns a list of all the users (mentor users) and noticed that I cannot access the linkedin link. I am not sure if its because I have not saved the link or I am not accessing it correctly.
This is what I have in models.py
class User(AbstractUser):
is_student = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
...
class Mentor(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True)
linkedin = models.URLField(max_length=200,null=True,blank=True)
def __str__(self):
return "Profile of user {}".format(self.user.username)
#receiver(post_save,sender=User)
def create_or_update(sender, instance, created, **kwargs):
if created:
post_save.connect(create_or_update, sender=User)
forms.py
class TeacherSignUpForm(UserCreationForm):
email = forms.EmailField(max_length=100)
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
linkedin = forms.URLField(max_length=200)
class Meta(UserCreationForm.Meta):
model = User
fields = ('email', 'username', 'first_name', 'last_name')
def save(self, commit=True):
self.instance.is_teacher = True
user = super(UserCreationForm, 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.save()
mentor = Mentor.objects.get_or_create(
user=user,
linkedin=self.cleaned_data['linkedin']
)
return user
#basic form
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
views.py (teachers.py)
class TeacherSignUpView(CreateView):
model = User
form_class = TeacherSignUpForm
template_name = 'registration/signup_form.html'
def get_context_data(self, **kwargs):
kwargs['user_type'] = 'teacher'
return super().get_context_data(**kwargs)
def form_valid(self, form):
user = form.save()
login(self.request, user)
return redirect('teachers:app-instructor-dashboard')
students.py (views)
#get list of mentors
def mentor_list(request):
mentors = User.objects.filter(is_teacher=True).select_related('mentor')
template_name = 'classroom/students/mentor_list.html'
context = {'mentors': mentors}
return render(request, template_name, context)
the html where I generate the list of mentors:
<ul id="menu-header-menu">
{% for user in mentors %}
<li>{{ user.first_name }} {{ user.last_name }}</li>
{% endfor %}
</ul>
From this list I only get the user's first and last name but cannot get their linkedin profile
You have use mentor in this line mentors = User.objects.filter(is_teacher=True).select_related('mentor') but not defined mentor anywhere.
Table are connected with this line:
user = models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True)
add related_name .
user = models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True, related_name='mentor')
Then makemigrations and migrate(I Hope you are familiar). Everything else is fine.
Also, print this to verify.
def mentor_list(request):
mentors = User.objects.filter(is_teacher=True).select_related('mentor')
# Edited, remember this is just for checking. this is not required
for user in mentors:
try:
print(user.mentor.linkedin)
except:
pass
template_name = 'classroom/students/mentor_list.html'
context = {'mentors': mentors}
return render(request, template_name, context)
I am really new to django. I have a model and 2 forms like this below extending the User model. The UserProfile is linked to the user model which would be where I have my extra field. I have seen numerous posts but still was't able to solve it. I would like to save the profile with additional parameters like the phone number stated below when the registration form is submitted, I have been spending hours trying to make it work, thanks a lot for your help in advance:
class UserProfile(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length = 50)
phone_number = models.CharField(max_length=12)
#In form.py
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username',
'first_name',
'last_name',
'email',
'password1',
'password2'
]
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
class RegistrationFormProfile(forms.ModelForm):
phone_number = forms.CharField(max_length = 12)
class Meta:
model = UserProfile
fields = [
'phone_number',
]
def save(self, commit=True):
profile.phone_number = self.cleaned_data['phone_number']
if commit:
profile.save()
return profile
#In views.py
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
profileForm = RegistrationFormProfile(request.POST)
if form.is_valid():
user = form.save()
if(profileForm.is_valid()):
profileForm.save()
return redirect('accounts/profile')
else:
return redirect('accounts/wrong')
else:
form = RegistrationForm()
profileForm = RegistrationFormProfile()
args = {'form' : form, 'profileForm' : profileForm}
return render(request, 'users/reg_form.html', args)
I'm trying to set up a registration page and I have a model CafeUser which serves as the profile for the User model. I used a tutorial to set up a signal receiver so I could create a CafeUser for all new registered users.
models.py:
class CafeUser(models.Model):
user = models.ForeignKey(User, unique=True, related_name="cafeuser")
phone_number = models.CharField(max_length=15)
birth_date = models.DateField('birthdate')
def __str__(self):
return self.user.first_name + " " + self.user.last_name
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
CafeUser.objects.create(user=instance)
views.py:
def register(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.cafeuser.birth_date = form.cleaned_data.get('birth_date')
user.cafeuser.phone_number = form.cleaned_data.get('phone_number')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('cafe:index')
else:
form = SignUpForm()
return render(request, 'cafe/register.html', {'form': form})
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.')
birth_date = forms.DateField(help_text='Rquired.')
phone_number = forms.CharField(max_length=15)
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2',)
When I try testing the registration, the user is created, however, the CafeUser is not:
IntegrityError at /register/ null value in column "birth_date"
violates not-null constraint DETAIL: Failing row contains (7, 8,
null, ).
register.html:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="submit">Register</button>
</form>
Please let me know how to fix this and any other tips you might have for improving this unit of code.
The problem was that the CafeUser was not saving. Based on what I read in the docs somewhere, I had thought that calling user.save() was supposed to automatically call save() on the profile module as well - and I'm still not sure why it doesn't. So I just wrote the call user.cafeuser.save() myself anyway:
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.cafeuser.birth_date = form.cleaned_data.get('birth_date')
user.cafeuser.phone_number = form.cleaned_data.get('phone_number')
user.cafeuser.save() # Apparently this had to be added
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('cafe:index')