I am creating a simple web app. The user suppose to signup and can see their profile. The signup form is working fine and the user can signup perfectly.
In the forms.py I have function to raise validation error when the user signup with an existing username. Here is the problem. If there is an username types for example - 'userone' , and another user types 'user.one' it saves the new user with 'user.one'. But when the user wants to go to their profile, the problem arises in the URL. Because I am using username as slug, the dot(.) is not present in the URL which leads to the problem.
I have tried with re_path as mentioned in the Django document, but still getting errors.
forms.py to check unique username
def clean_username(self):
username = self.cleaned_data.get('username')
email = self.cleaned_data.get('email')
if username and User.objects.filter(username=username).exclude(email=email).count():
raise forms.ValidationError('This username already exists')
return username
views.py (signup class)
class SignupView(View):
form_class = SignupForm
template_name = 'webapp/user_signup.html'
def get(self, request):
form = self.form_class(None)
return render(request, self.template_name, {'form':form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user.set_password(password)
user.save()
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect('webapp:home')
views.py (for profile class)
class ProfileView(generic.DetailView):
model = User
slug_field = 'username'
template_name = 'webapp/user_profile.html'
urls.py
urlpatterns = [
# user signup link
path('user/signup/', views.SignupView.as_view(), name='signup'),
. . . .. . . .. . . ..
# user profile view
path('user/<slug:slug>/', views.ProfileView.as_view(), name='user_profile'),
]
HTML
{{article.user.first_name}} {{article.user.last_name}}
What should be a perfect approach for this?
The only problem here is that your URL pattern doesn't accept dots. You could change it to a str, which accepts anything:
path('user/<str:slug>/',
Or use a regex if you want to be a bit more selective about what you accept:
re_path(r'user/(?P<slug>[\w.]+)/$',
Related
I am very new to Django and this is my first project. I am trying to build a simple login/registration with MySQL as db and a custom user model. The user is successfully signing up but unable to login using correct pw and email.
I tried redoing my signup function and index function (login) and changed username to email in my custom model and index function.
view.py:
from django.shortcuts import render, redirect
from django.contrib.auth import login, logout, authenticate
from django.shortcuts import render, redirect
from .models import CustomUser
from .forms import CustomUserCreationForm, EmailAuthenticationForm
from django.contrib.auth.forms import AuthenticationForm
from .backends import EmailBackend
# sign up
def signup(request):
if request.user.is_authenticated:
# if user is already logged in, redirect to the appropriate page
return redirect('/home')
if request.method == 'POST':
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save(commit=True)
# Hash the password
user.set_password(user.password)
user.save()
else:
form = CustomUserCreationForm()
return render(request, 'signup.html', {'form': form})
# login
def index(request):
if request.method == 'POST':
form = EmailAuthenticationForm(data=request.POST)
if form.is_valid():
email = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = EmailBackend().authenticate(request, email=email, password=password)
if user is not None:
login(request, user)
return redirect('home')
else:
return redirect('error')
else:
form = EmailAuthenticationForm()
return render(request, 'index.html', {'form': form})
forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from .models import CustomUser
# custom sign up form
class CustomUserCreationForm(UserCreationForm):
first_name = forms.CharField(widget=forms.TextInput(attrs={'autofocus':'autofocus'}))
email = forms.EmailField()
class Meta(UserCreationForm):
editable = True
model = CustomUser
fields = ('first_name', 'last_name', 'email')
# custom login form:
class EmailAuthenticationForm(AuthenticationForm):
username = forms.EmailField(widget=forms.EmailInput(attrs={'autofocus': True}))
# clean username function to make sure the username is an email
def clean_username(self):
username = self.cleaned_data.get('username')
try:
CustomUser.objects.get(email=username)
return username
except CustomUser.DoesNotExist:
models.py:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
# custom user model to include extra fields:
class CustomUser(AbstractBaseUser):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField(unique=True, max_length=255)
is_staff = models.BooleanField(default=False)
# keeps track of whether the account was confirmed or not thgough email confirmation
is_active = models.BooleanField(default=True)
REQUIRED_FIELDS = ['first_name', 'last_name']
USERNAME_FIELD = 'email'
# changed the default manager to the custom manager for login
#classmethod
def get__by_natural_key(cls, email):
return cls.objects.get(email=email)
backends.py:
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User
from .models import CustomUser
class EmailBackend(BaseBackend):
def authenticate(self, request, email=None, password=None, **kwargs):
try:
user = CustomUser.objects.get(email=email)
except CustomUser.DoesNotExist:
return None
if user.check_password(password):
return user
def get_user(self, user_id):
try:
return CustomUser.objects.get(pk=user_id)
except CustomUser.DoesNotExist:
return None
All the similar posts I found were about staff or admin users.
The UserCreationForm already hashes the function, you are here hashing it a second time. You thus sign up with:
def signup(request):
if request.user.is_authenticated:
# if user is already logged in, redirect to the appropriate page
return redirect('/home')
if request.method == 'POST':
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
return redirect('name-of-some-view')
else:
form = CustomUserCreationForm()
In your EmailBackend.authenticate function, I see that you are loading a user and checking their password, but I don't see a login() function before you return user. Might this be the missing piece?
Here is the Django docs explaining the user login process for a django.contrib.auth.models.User object: https://docs.djangoproject.com/en/4.1/topics/auth/default/#how-to-log-a-user-in
I am trying to set up a login page and I am trying to use the #login_required decoration. However, whenever I try and log in with valid credentials I am re-directed to the 'login' page (set to re-direct unauthenticated users). I am not sure if the problem is in the #login_required decoration or perhaps the login() function is not authenticating the user.
Here is my code for the register form:
class RegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
confirm_password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ['first_name', 'last_name', 'username', 'email', 'password']
code for login function in views.py:
def login_user(request):
if request.method =="GET":
return render(request, "main/login.html", {})
else:
username = escape(request.POST['userfield'])
password = escape(request.POST['passfield'])
try:
user = User.objects.get(username=username)
except:
user = None
if user is None:
try:
user = User.objects.get(email=username)
except:
user = None
if user is None:
messages.info(request, "*Sorry, that username or email does not exist")
return redirect('login')
pword = user.password
if check_password(password, pword):
login(request, user)
return redirect('homepage')
else:
messages.info(request, '*Sorry, that was an incorrect password')
return redirect('login')
my model for User in models.py:
class User(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
username = models.CharField(max_length=100)
email = models.EmailField(unique=True)
password = models.CharField(max_length=100)
admin = models.BooleanField(default=False)
last_login = models.DateTimeField(null=True, blank=True)
and my function to reach the 'homepage' after login:
#login_required(redirect_field_name='login')
def homepage(request):
return render(request, "main/homepage.html", {})
When you make custom user model, so you should always use AbstractBaseUser.
Note: It's also not a good practice to name same your models, django already has User model in the django.contrib.auth, so change its name.
So, you haven't specified the custom user model, so you should not able to authenticate, as by default authentication model is User which is at django.contrib.auth. So, with the current code when you make superuser through python manage.py createsuperuser and then you authenticate, so it will work.
You should use #login_required(login_url='login') instead of #login_required(redirect_field_name='login').
You need to correctly authenticate the user before logging in.
from django.contrib.auth import authenticate, login
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
More information in the documentation
I am fairly new to Django so I wanted to know what should I do to make default authenticate function of Django accepts only email and password to login, assuming that in sign-up form I have both username and email. The following code does not work properly.However when I add
username = request.POST.get('username')
and change
user = authenticate(request, username=username email=email, password=password)
It logs in as expected.
The login View:
def LoginPage(request):
if request.method == 'POST':
email = request.POST.get('email')
password = request.POST.get('password')
user = authenticate(request, email=email, password=password)
if user is not None:
login(request, user)
print('logged')
return redirect('/inker/')
return render(request, 'authy/login.html')
The sign up view:
def SignUpPage(request):
form = CreateUserForm()
if request.method == 'POST':
form = CreateUserForm(request.POST)
if form.is_valid():
form.save()
return redirect('/login/')
context={'form':form}
return render(request, 'authy/signup.html', context)
The form module:
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = ['username','email', 'password1', 'password2']
As you can see in Sign Up view we have both username and email but in login form I want it to just login user based on only email. How can I implement this.
Also how can I compact both username and email into one input when user wants to login? pretty much like instagram, facebook login page
If you just want to access user using email, you're not using authentication on the first place. So, to solve your purpose, following approaches can be followed:
Get user email in url params in every request (so that user can be identified)
Set default password while creating the user account for all users and use that password while authentication (not recommended)
I have a django project with two apps, the first is called 'main', where the user can login and register and all the authentication stuff is made and the other is 'app' where only authenticated users can access.
Main.views:
def register(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
username = form.cleaned_data.get('username')
messages.success(request, f"New account created: {username}")
login(request, user)
return redirect("app:app_index")
else:
for msg in form.error_messages:
messages.error(request, f"{msg}: {form.error_messages[msg]}")
return render(request = request,
template_name = "main/register.html",
context={"form":form})
form = UserCreationForm
return render(request = request,
template_name = "main/register.html",
context={"form":form})
All the code works fine and allows me to login, register, logout... but now I need to export that user to the 'app' app in order to use the user data on the app template, how can I migrate the user and all its data?
I wonder I would be able to just do
app.views:
from main.views import user
but definitely this does not work, any suggestion will be much appreciated
Actually you can simply access use information an all your apps with object notation.
In my case, the user object has username attribute, so when I want to use the users' username I can simply type
user.username
And it will return the username saved on the database for the specific user that's loged in
I initially used to login using email and password but now want to change it in such a way that we can login using either mobile no or email address and password.I am not able to understand what parts to change in my code as I do not want to delete already existing user and data.
forms.py
from django.contrib.auth.models import User
from django import forms
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['first_name', 'last_name', 'email', 'password']
views.py
class UserFormView(View):
form_class = UserForm
template_name = 'main/registration_form.html'
def get(self, request):
form = self.form_class(None)
return render(request, self.template_name, {'form': form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
# cleaned (normalized) data
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user.username = email
user.set_password(password)
user.save()
# return user objects if credentials are correct
user = authenticate(username=email, password=password)
if user is not None:
if user.is_active:
login(request, user)
# request.user.username display their username
return redirect('main:register2')
return render(request, self.template_name, {'form': form})
class LoginForm(View):
form_class = UserForm
template_name = 'main/login_form.html'
def get(self, request):
form = self.form_class(None)
if error:
return render(request, self.template_name, {'form': form},
{'error': error})
else:
return render(request, self.template_name, {'form': form})
def post(self, request):
email = request.POST.get('email', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=email, password=password)
if user is not None and user.is_active:
# Correct password, and the user is marked "active"
auth.login(request, user)
# Redirect to a success page.
return HttpResponseRedirect("/main/home/")
else:
# Show an error page
error = True
return HttpResponseRedirect("/main/", {'error': error})
Please answer considering the fact that I am new to Django. Thanks in advance :)
In the post method of your view, identify what has the user given you. If it's an email, feed the Django's authenticate() method with the email. If not, then assume it's a phone number (you may also run additional validations in there) and use this in the authentication procedure.
Another approach would be to create a custom authenticate() method, which would look like this:
from django.contrib.auth.backends import ModelBackend
class MobilePhoneOrEmailModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
# the username could be either one of the two
if '#' in username:
kwargs = {'email': username}
else:
kwargs = {'mobile_phone': username}
try:
user = User.objects.get(**kwargs)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, username):
try:
return User.objects.get(pk=username)
except User.DoesNotExist:
return None
Let me know if this worked out well for you.
Credits to user user3282276 in this thread as well: Log in user using either email address or username in Django