I am working on a Django project where users will be able to change their usernames along with their first and last name in one form. In forms.py, I am trying to find out if the user exists. If so, it should display an error.
The problem is that if user wants to change his first and last name and leaves his username in the input, it raises a validation error. Obviously, that username already exists.
Is there a way to check if it equals the username of currently logged user and avoid displaying the error?
class ChangeNameForm(forms.ModelForm):
username = forms.CharField(max_length=30)
first_name = forms.CharField(max_length=255)
last_name = forms.CharField(max_length=255)
def clean_username(self):
username = self.cleaned_data['username']
try:
user = User.objects.get(username=username)
except user.DoesNotExist:
return username
raise forms.ValidationError(u'Username "%s" is already in use.' % username)
When ModelForms are bound to a model object, they have an attribute called 'instance', which is the model object itself. In your view, when request.method == 'POST', you're probably creating the form instance like this:
form = ChangeNameForm(request.POST, instance=request.user)
If that's the case, you can access the logged user from the form methods, and your validation method can be something like this:
def clean_username(self):
username = self.cleaned_data['username']
try:
user = User.objects.exclude(pk=self.instance.pk).get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(u'Username "%s" is already in use.' % username)
Consider using the .exists method, for it issues a faster query to your database than if you try to retrieve all the user information with the .get method. And the code gets a little cleaner too:
def clean_username(self):
username = self.cleaned_data['username']
if User.objects.exclude(pk=self.instance.pk).filter(username=username).exists():
raise forms.ValidationError(u'Username "%s" is already in use.' % username)
return username
Optionally, you can also follow these guidelines when raising the ValidationError.
I can't test this code right now, so I apologize if there's anything wrong.
You can write function to check the username if exists like this:
#ggorlen, thanks! Update:
from django.contrib.auth.models import User
def username_exists(username):
return User.objects.filter(username=username).exists()
from django.contrib.auth.models import User
def username_exists(username):
if User.objects.filter(username=username).exists():
return True
return False
This is how I managed to make it work (assuming you have a logged in user):
forms.py
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import User
class MyUserChangeForm(UserChangeForm):
def __init__(self, *args, **kwargs):
super(MyUserChangeForm, self).__init__(*args, **kwargs)
del self.fields['password']
class Meta:
model = User
fields = ('username', 'first_name')
views.py
def home(request):
if request.method == 'POST':
form = MyUserChangeForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
else:
form = MyUserChangeForm(instance=request.user)
return render(request, 'change_user.html', {"form": form})
Related
I'm a newbie in django and I'm having some problems, what I'm trying to do is a simple login system in Django with a custom backend and using postgresql as the main db.
The problem is, my authentication and login function is apparently working normally but the user is not actually logged in, I wrote a custom message to let me know when the user is logged in and my index is protected against anonymous user, so I basically can't access.
This code from my views.py
#login_required(login_url='signin')
def index(request):
return render(request, 'index.html')
def signin(request):
now = datetime.now()
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
print(username, password)
user = UserBackend.authenticate(UserBackend(), username=username, password=password)
if user is not None:
login(request, user, backend='core.backends.UserBackend')
print('Logged In') #### This print/checking is working fine!
return redirect('/')
#return render(request, 'index.html')
else:
messages.info(request, 'Invalid Username or Password! Try again')
return redirect("signin")
#else:
return render(request,'signin.html')
#user = auth.authenticate(username=username, password=password)
return render(request, 'signin.html')
This is my user class from models.py
class user(AbstractBaseUser):
id = models.AutoField(primary_key=True)
username = models.TextField()
real_name = models.TextField()
email = models.TextField()
password = models.TextField()
description = models.TextField()
last_login = models.DateField()
created_at = models.DateField()
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
class Meta:
managed = False
db_table = 'user'
def __str__(self) -> str:
return self.username
And my user backend from backends.py
class UserBackend(ModelBackend):
def authenticate(self, **kwargs):
username = kwargs['username']
password = kwargs['password']
print('user: ', username)
print('pass: ', password)
#try:
user_ = user.objects.get(username=username)
try:
print(user_.check_password(password))
if user_.check_password(password) is True:
return user_
except user.DoesNotExist:
pass
def get_user(self, user_id):
try:
return user.objects.get(pk=user_id)
except user.DoesNotExist:
return None
I tried changing the return on views.py to something like
return redirect(request.GET.get('next'))
but still not working :(
what should I do?
The key point, is how you are using your Backend. Although, lets start from the beginning...
Starting with your models, you are unnecessarily overwriting a lot of fields. Django's AbstractUser has these fields you are creating. In fact, the model contains the following fields with validators where appropriate:
username
first_name
last_name
email
is_staff as default false
is_active as default true
date_joined as default timezone.now()
last_login
a built-in function get_full_name (equivalent to your real_name field)
Also, id = models.AutoField(primary_key=True) is not necessary Django by default creates this kind of field automatically for every model. Most of the times used when you want a different kind of field as PK. Thus a simple model, would fulfill your requirements:
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
description = models.TextField()
The backend is pretty straight forward, check credentials and return an User object instance if everything is correct. ModelBackend already has a get_user method defined.
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth import get_user_model
User = get_user_model()
class UserBackend(ModelBackend):
def authenticate(self, request, **kwargs):
username = kwargs['username']
password = kwargs['password']
try:
user = User.objects.get(username=username)
pwd_valid = check_password(password, user.password)
if pwd_valid:
return user
else:
return None
except User.DoesNotExist:
return None
Now, about on how to use it. Quotting the documentation:
Behind the scenes, Django maintains a list of “authentication
backends” that it checks for authentication. When somebody calls
django.contrib.auth.authenticate() Django tries authenticating across
all of its authentication backends. If the first authentication method
fails, Django tries the second one, and so on, until all backends have
been attempted.
The list of authentication backends to use is specified in the
AUTHENTICATION_BACKENDS setting. This should be a list of Python path
names that point to Python classes that know how to authenticate.
These classes can be anywhere on your Python path.
So, first we need to add AUTHENTICATION_BACKENDS in settings.py:
AUTH_USER_MODEL = 'core.User'
LOGIN_URL = '/signin'
AUTHENTICATION_BACKENDS = [
'core.auth.backends.UserBackend',
'django.contrib.auth.backends.ModelBackend'
]
And lastly in views.py, we call the functions exactly as you would in a normal login function:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, authenticate
from django.contrib import messages
# Create your views here.
#login_required
def index(request):
return render(request, 'index.html')
def signin(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Confirm that backend is UserBackend
print(user.backend)
return redirect('core:index')
else:
messages.info(request, 'Invalid Username or Password! Try again')
return redirect("core:signin")
return render(request, 'signin.html')
I am a beginner in Django an am using version 2.2 .I created a user form to sign a user in the site but it cant add other field information to the database
I have tried adding other fields in the fields list add adding fields but nothing works`
forms.py
from django import forms
from django.contrib.auth import (
authenticate,
get_user_model
)
User = get_user_model()
class UserRegisterForm(forms.ModelForm):
username = forms.CharField(label='PUsername')
email = forms.EmailField(label='Email address')
email2 = forms.EmailField(label='Confirm Email')
password = forms.CharField(widget=forms.PasswordInput,label='Password')
password2 = forms.CharField(widget=forms.PasswordInput,label='ConfirmPassword')
age = forms.CharField(label='your age')
info = forms.CharField(label='info about you')
class Meta:
model = User
fields = [
'username',
'email',
'email2',
'password',
'password2',
'age'
'info'
]
def clean(self, *args, **kwargs):
username = self.cleaned_data.get('email')
email = self.cleaned_data.get('email')
email2 = self.cleaned_data.get('email2')
password = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password2')
if email != email2:
raise forms.ValidationError("Emails must match")
email_qs = User.objects.filter(email=email)
if password != password2:
raise forms.ValidationError("Passwords must match")
email_qs = User.objects.filter(email=email)
if email_qs.exists():
raise forms.ValidationError(
"This email has already been registered")
username_ex = User.objects.filter(username=username)
if username_ex.exists():
raise forms.ValidationError("This username is taken")
return super(UserRegisterForm, self).clean(*args, **kwargs)
views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate,get_user_model,login,logout
from .forms import CreateUserForms
import random
import string
def register_view(request):
if request.method=='POST':
frm=CreateUserForm(request.POST)
if frm.is_valid():
username, email, password = frm.cleaned_data['username'], frm.cleaned_data['email'], frm.cleaned_data['password1']
new_user = User.objects.create_user(username, email, password)
new_user.is_active = True # if you want to set active
new_user.save()
else:
frm=CreateUserForm()
return render(request,'registration/signup.html',{'form':frm})
def logout_view(request):
logout(request)
return redirect('/')
Expected results are the working form,but actual results are only saving the username, password and email
read the documentation on python super . you are using it in wrong way. calling super at last of the clean method mean it it execute the modelform clean method and code before super get modified .
The title of your question is
How can i create custom users with only forms?
The answer to that is: That's not possible. Whatever you want to save to the database, you have to have a corresponding model for it. Forms only determine what data is submitted by the clients and how that data is processed.
So if you want to save columns age and info for your users, you'll have to create a Model and the corresponding table and columns in your database to hold these values.
Now, the default Django User model only takes username, email and password (plus some booleans like is_staff, is_superuser, is_active etc...). If you want to save additional information, you have a few options that are well described in the official docs, either by extending the default User model or by substituting it.
The recommendation when starting a new Django project is to always at least substitute with your own User model (see this). If you will only ever have one type of user, then add the age and info fields on your custom User model. But if there's any chance that you might have different types of users where age and info might not be relevant in the future, extend your custom User model with a profile.
What part of this view is making me access this url and why can it not be accessed? I have never seen it before. Everything was fine on my development machine and now I've moved it to production on an Apache server and I can't figure out how to solve this error.
def new_user(request):
form = NewAccountForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
first_name = form.cleaned_data['first_name']
last_name = form.cleaned_data['last_name']
email = form.cleaned_data['email']
try:
user = User.objects.create_user(username, email, password)
except:
return HttpResponse("That username is already taken. Please try a different one.")
user.first_name = first_name
user.last_name = last_name
user.save()
if bool(request.FILES.get('picture', False)):
pic = request.FILES.get('picture')
profile = Profile.objects.create(user=user)
profile.picture = pic
profile.save()
else:
user.delete()
return HttpResponse("Please upload a profile picture.")
user = authenticate(username=username, password=password)
login(request, user)
return redirect('home')
else:
return HttpResponse("The form you submitted was invalid. Please enter appropriate input.")
The form action that triggers this view is:
<form action="app/new_user/" method="post" enctype="multipart/form-data">
I had the same problem.
I'm using class views, but I guess you may have a problem like mine.
The problem came from the fact that I was mixing login_required and ensure_csrf_cookie directives, like this:
class JsonBase(generic.View):
#method_decorator(login_required)
#method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
return JsonResponse({}, safe=False)
So I thought: if the user is not connected, for the get method, never mind it's ok, I just return an empty Json object, and in my descendant classes, I'll do the comparison if I need to if request.user.is_authenticated: [blabla].
So here's my final working "base" Json view:
class JsonBase(generic.View):
#method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
return JsonResponse({}, safe=False)
#method_decorator(ensure_csrf_cookie)
def post(self, request, *args, **kwargs):
return JsonResponse({}, safe=False)
This will be an easy question for someone out there. I searched around quite a bit and if there's a thread that addresses this perfectly, please direct me and close this out. I'm building a very simple web form within our organization's website using Django.
In forms.py I've got a class for the login page:
class userLogin(forms.Form):
user = forms.CharField(label = 'Username ', max_length = 25)
pwd = forms.CharField(label = 'Password ', widget = forms.PasswordInput)
def clean(self):
cleaned_data = super(userLogin, self).clean()
user = cleaned_data.get("user")
pwd = cleaned_data.get("pwd")
if not fs.authenticateUser(user, pwd):
raise forms.ValidationError("Invalid password!")
I have to authenticate against our website's API, which is what the fs.authenticate bit is about; it just returns True or False. In views.py I do this:
if request.method == 'POST':
user_form = userLogin(data = request.POST)
if user_form.is_valid():
return redirect('PickupAuthorization/main')
else:
pass
The authentication and redirection to the main page works splendidly, and the login form throws an invalid password message as expected when the credential is incorrect. What I want to know is how to make "user" available to views.py. If I try to reference user_form.user in the view, Python tells me that user is not an attribute of userLogin!
I haven't done much with OOP, so I'm imagining that this has a simple answer, but I can't freakin' find it. Thanks!
EDIT: I have had to shelve this project for the moment in favor of more pressing matters, but I will update it when I have a solution that works properly.
You need to use authenticate method provided by Django in order to get the user. The authenticate method returns a user if it finds one.
To authenticate a given username and password, use authenticate(). It takes two keyword arguments, username and password, and it returns a User object if the password is valid for the given username. If the password is invalid, authenticate() returns None:
Generally the flow using authenticate goes like this:
forms.py
class userLogin(forms.Form):
user = forms.CharField(label = 'Username ', max_length = 25)
pwd = forms.CharField(label = 'Password ', widget = forms.PasswordInput)
def __init__(self, *args, **kwargs):
self.user_cache = None # You need to make the user as None initially
super(LoginForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(userLogin, self).clean()
user = cleaned_data.get("user")
pwd = cleaned_data.get("pwd")
# Now you get the user
self.user_cache = authenticate(username=user, password=pwd)
# Do other stuff
return self.cleaned_data
# Function to return user in views
def get_user(self):
return self.user_cache
Views.py
if request.method == 'POST':
user_form = userLogin(data = request.POST)
# You can now get user using the get_user method
user = user_form.get_user()
# Do other stuff
Another thing I'd like to add is if your user has already logged in you can simply do request.user.username in your views.py to get the current user.
I am trying to create a simple login using Django authentication. All the code here is working but I am thinking that it is violating DRY principles. I will explain:
In my forms.py I have a simple login form:
class LoginForm(forms.Form):
email = forms.CharField()
password = forms.PasswordField()
def clean(self):
email = self.cleaned_data.get('email', None)
password = self.cleaned_data.get('password', None)
u = authenticate(email, password)
if u is None:
raise forms.ValidationError(ERROR_MSG)
if not u.is_active:
raise forms.ValidationError(ERROR_MSG)
so I am doing a check of User here already. However, in my views.py:
def login(request):
login_form = LoginForm(request or None)
if login_form.is_valid():
#This part is repeated
email = request.POST.get('email')
password = request.POST.get('password')
u = authenticate(email, password)
login(request, u)
return render(request, 'home.html', {})
I am querying the database again, which to me seems to violate DRY. Does anybody have a better way of doing this? I want to reuse the LoginForm for other uses, but also want to do it cleanly.
Any ideas?
Instead of doing the authenticate() twice, you can set the user on the form object and then use this user in your view.
forms.py
class LoginForm(forms.Form):
email = forms.CharField()
password = forms.PasswordField()
def clean(self):
email = self.cleaned_data.get('email', None)
password = self.cleaned_data.get('password', None)
self.u = authenticate(email, password) # set the user as an instance variable
if self.u is None:
raise forms.ValidationError(ERROR_MSG)
if not self.u.is_active:
raise forms.ValidationError(ERROR_MSG)
views.py
def login(request):
login_form = LoginForm(request or None)
if login_form.is_valid():
login(request, login_form.u) # directly pass the user by accessing from login_form object
return render(request, 'home.html', {})