Noob question. I am building a login/registration pages, on the registration page, I am using flask_wtf to verify certain things like length of password, email format and whether the two password a user supplies match. Here is the flask_wtf code I am using to do that.
# import statements omitted for brevity
class RegistrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email',validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(),Length(min=4, max=20), EqualTo('password')])
submit = SubmitField('Sign Up')
After checking the input, I am using sqlalchemy to check if the username and email already exists in my DB. The problem I am facing right now is I cant get flask_wtf to verify the form. I can type whatever I want and it will be converted to a sql query. Here are my two flask routes that handle registration and user input validation.
#app.route('/register',methods=['GET','POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
return redirect(url_for('check_user_input'))
return render_template('register.html',form=form)
#app.route('/status',methods=['POST'])
def check_user_input():
name = request.form.get("username")
email = request.form.get("email")
password = request.form.get("password")
if db.execute("SELECT * FROM DB WHERE username= :username",{"username":name}).rowcount==1:
return render_template("404.html", message="Sorry username already exists")
elif db.execute("SELECT * FROM DB WHERE email= :email",
{"email":email}).rowcount==1:
return render_template("404.html", message="Sorry email already exists")
else:
db.execute("INSERT INTO DB (username,email,password) VALUES
(:username,:email,:password)",
{"username":name, "email":email,"password":password})
db.commit()
return render_template("success.html")
How can I get flask_wtf form to do its verification first and then hand the input to check_user_input() function?
My register.html contains the following line.
<form class="form-signin" method="POST" action="{{url_for('check_user_input')}}">
Any help would be greatly appreciated.
A way is to add a custom validation to the form :
class RegistrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email',validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(),Length(min=4, max=20), EqualTo('password')])
submit = SubmitField('Sign Up')
def validate(self):
rv = FlaskForm.validate(self)
if not rv:
return False
if db.execute("SELECT * FROM DB WHERE username= :username",{"username":self.username.data}).rowcount>0:
self.username.errors.append('Sorry username already exists')
return False
if db.execute("SELECT * FROM DB WHERE email= :email", {"email":self.email.data}).rowcount>0:
self.email.errors.append('Sorry email already exists')
return False
return True
Related
I have a Streamlit application that lets the user login (by checking with SQLite databse) and then do some action. My problem is that I'm trying to print a welcome message with the user's username but I'm getting 'None' instead.
This is my Login function:
def show_login_page():
with loginSection:
if st.session_state['loggedIn'] == False:
username = st.text_input (label="Username", value="", placeholder="Enter your username")
password = st.text_input (label="Password", value="", placeholder="Enter password", type="password")
hashed_password = make_hash(password)
st.button("Login", on_click=LoggedIn_Clicked, args= (username, hashed_password))
return username
Then after clicking the login button, user's information will be checked with the database
def LoggedIn_Clicked(username, password):
# check with database
if login_user(username, password):
st.session_state['loggedIn'] = True
else:
st.session_state['loggedIn'] = False
st.error("Invalid username or password")
After sucessfully logging in, the session_state will change and the user will login to the main page
with headerSection:
st.title("Streamlit Application")
#first run will have nothing in session_state
if 'loggedIn' not in st.session_state:
st.session_state['loggedIn'] = False
show_login_page()
else:
if st.session_state['loggedIn']:
show_logout_button()
# getting the username from login page
# Problem is here
usr = show_login_page()
show_main_page(usr)
else:
show_login_page()
This is the main page function:
def show_main_page(username):
with mainSection:
st.header(f"Hello {username}")
# Do some action
processingClicked = st.button("Start Processing", key="processing")
if processingClicked:
st.balloons()
I've tried many ways was to get the username variable from the login_page to the main_page function to no avail. Help would be appreciated.
I tried to reset password manually by passing key But it always says
AttributeError : 'str' object has no attribute 'get'
I googled many times even if i saw Manual Password with hiding password field Django but no luck favor me.
// In my form #forms.py
class UserResetPasswordForm(forms.Form):
new_password1 = forms.CharField(
label=_("New Password"),
widget=forms.PasswordInput(attrs={"autocomplete":"off"}),
strip=False,
help_text=password_validation.password_validators_help_text_html(),
)
new_password2 = forms.CharField(
label=_("New Password Confirmation"),
strip=False,
widget=forms.PasswordInput(attrs={"autocomplete":"off"}),
)
In Url
path('password-reset/confirm/<str:reg_key>/', accounts_view.myreset_password_confirm, name='myreset_password_confirm'),
In View #views.py
def myreset_password_confirm(request, reg_key):
user = User.objects.filter(activate_key=reg_key).first()
if user is not None:
if request.method == 'POST':
form = UserResetPasswordForm(request.POST)
if form.is_valid():
#password1 = form.cleaned_data.get('new_password1')
#password2 = form.cleaned_data.get('new_password2')
password1 = request.POST['new_password1']
password2 = request.POST['new_password2']
if password1 and password2:
if password1 != password2:
raise ValidationError("Your password didn't match.")
password_validation.password_validators_help_text_html()
return password2
print(password2)
user.activate_key = ''
user.set_password(password2)
user.save()
print("Print User")
print(type(user))
messages.success(request, f"Your password for {user.email} has been reset successfully!")
return HttpResponseRedirect('/')
else:
form = UserResetPasswordForm()
return render(request, 'reset_password_confirm.html', {'form':form})
else:
print("U R Boomed")
messages.error(request, "Your requested URL is not Valid, Please try with valid URL.")
return HttpResponseRedirect('/')
Please help, how to solve it?
You are returning return password2, which is not proper HttpResonse. I don't think you wanted to do that. Just delete this line.
i'm trying to generated token after login using drf. i'm using emailbackend for login with email and password but its not working with user created by api and with user created by admin its working
backends.py:
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(email=username)
except UserModel.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
Token serializers:
class AuthCustomTokenSerializer(serializers.Serializer):
'''
Changing Token auth to use email instead username
'''
email = serializers.EmailField(label=_("Email"))
password = serializers.CharField(
label=_("Password",),
style={'input_type': 'password'},
trim_whitespace=False
)
def validate(self, attrs):
email = attrs.get('email')
password = attrs.get('password')
print(email, password)
if email and password:
user = authenticate(username=email, password=password)
print("this is user", user)
# The authenticate call simply returns None for is_active=False
# users. (Assuming the default ModelBackend authentication
# backend.)
if not user:
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg, code='authorization')
else:
msg = _('Must include "username" and "password".')
raise serializers.ValidationError(msg, code='authorization')
attrs['user'] = user
return attrs
login view:
#csrf_exempt
#api_view(["POST"])
#permission_classes((AllowAny,))
def login(request):
serializer = AuthCustomTokenSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, _ = Token.objects.get_or_create(user=user)
return Response({token: token.key}, status=status.HTTP_200_OK)
with admin login:
user login create by api:
register api:
Thanks, Great.
This means that authenticate(username=email, password=password) does not return a user.
Do you work with a degugger ? or may be add a
print(email, password) just after the auth call.
print what comes back from auth . print(authenticate(username=email, password=password))
My guess is that username is not email or somthing like that :)
Edit
How to debug:
login with admin user stop just before this line:
authenticate(username=email, password=password)
check and print the email and password
Do the same with API user check and print the email and password
see that values are the same .
login to django admin site check all premissions flag groups etc etc that are different between both users
try to login to admin page with the api user (set up the correct flags is_active etc)
try in the django manage.py shell or from admin user page to create new password for the api user and retest
i am creating a website where a banks loggs in with its username which is a code but i wanted that bank could log in with its first_name.
i am using default user model for registration.
but authenticate() function works only with username so what i wanted to do is that bank fill their name and function finds the value of username with corrosponding name in the database and then use authenticate() function to log the bank in.
my login function in view.py
def login(request):
if request.method == 'POST':
name = request.POST.get('first_name')
password = request.POST.get('password')
username = ????????
user = authenticate(username=username, password=password)
if user:
if user.is_active and has_role(user,Banker):
auth_login(request,user)
return HttpResponseRedirect(reverse('business:dashboard'))
else:
messages.error(request,"Your account is not active")
return render(request,'accounts/bank_login.html')
else:
messages.error(request,"Invalid Username or Password")
return render(request,'accounts/bank_login.html')
else:
if request.user.is_authenticated:
return HttpResponseRedirect(reverse('business:dashboard'))
else:
return render(request,'accounts/bank_login.html')
so please anybody could tell what should i write in that username to get the value of username from database
**my models.py **
from django.db import models
from django.contrib import auth
# Create your models here.
class User(auth.models.User,auth.models.PermissionsMixin):
def __str__(self):
return self.user.username
This is slightly more complex than doing a simple query. Also, you cannot rule our that two users with the same first name will choose the same password. Here the first user found is taken
Something like this:
from django.contrib.auth.hashers import check_password
firstnameusers = User.objects.filter(first_name=name)
for usr in firstnameusers:
if check_password(password, usr.password):
username = usr.username
break
Note that you will need to write some code to handle the case where a user is not found.
I used this in my views.py file and it works perfectly
def login(request):
if request.method == 'POST':
name = (request.POST.get('name')).upper()
username = (get_user_model().objects.all().filter(first_name = name)).values("username")[0]["username"]
password = request.POST.get('password')
user = authenticate(username=username, password=password)
Here I need to add an extra confirmation password in my form.I used Django's modelform. I also need to validate both passwords. It must raise a validation error if password1 != password2.
Here is my forms.py:
class UserForm(forms.ModelForm):
password=forms.CharField(widget=forms.PasswordInput())
class Meta:
model=User
fields=('username','email','password')
class UserProfileForm(forms.ModelForm):
YESNO_CHOICES = (('male', 'male'), ('female', 'female'))
sex = forms.TypedChoiceField(choices=YESNO_CHOICES, widget=forms.RadioSelect)
FAVORITE_COLORS_CHOICES=(('red','red'),('blue','blue'))
favorite_colors = forms.MultipleChoiceField(required=False,widget=forms.CheckboxSelectMultiple, choices=FAVORITE_COLORS_CHOICES)
dob = forms.DateField(widget=forms.DateInput(format = '%d/%m/%Y'),
input_formats=('%d/%m/%Y',))
class Meta:
model=UserProfile
fields=('phone','picture','sex','favorite_colors','dob')
And here is my registration function:
def register(request):
registered = False
if request.method == 'POST':
user_form = UserForm(data=request.POST)
profile_form = UserProfileForm(data=request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
user.set_password(user.password)
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
profile.save()
registered = True
else:
print user_form.errors, profile_form.errors
else:
user_form = UserForm()
profile_form = UserProfileForm()
return render(request,
'mysite/register.html',
{'user_form': user_form, 'profile_form': profile_form, 'registered': registered} )
Use clean like
class UserForm(forms.ModelForm):
password=forms.CharField(widget=forms.PasswordInput())
confirm_password=forms.CharField(widget=forms.PasswordInput())
class Meta:
model=User
fields=('username','email','password')
def clean(self):
cleaned_data = super(UserForm, self).clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
if password != confirm_password:
raise forms.ValidationError(
"password and confirm_password does not match"
)
def clean(self):
cleaned_data = super(UserAccountForm, self).clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
if password != confirm_password:
self.add_error('confirm_password', "Password does not match")
return cleaned_data
Try this for forms.py:
class UserForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput())
password_confirm = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields=('username','email','password')
And this in views.py:
if user_form.is_valid() and profile_form.is_valid() and user_form.cleaned_data['password'] == user_form.cleaned_data['password_confirm']:
...
elif user_form.data['password'] != user_form.data['password_confirm']:
user_form.add_error('password_confirm', 'The passwords do not match')
You can have a look at how Django does it for UserCreationForm.
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
Here password2 refers to the confirm_password field, under the assumption that it appears after the password field. Trying to use the same implementation for clean_password may lead to an error that the confirm_password data wasn't found.
This has the advantage that you're raising the error for a particular Field, instead of the whole form, which you can then render appropriately in your template.
However, if you're trying to validate data across multiple fields, the documentation advises overriding the clean() method, as answered by Savai.
The source code is available here.
Confirm password is already available in UserCreationForm
class UserRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ("username", "email", "password1", "password2")
This will automatically verify password1==password2 when form.is_valid() is called.
Here I am only taking email as a separate form arguments for further clean up and validation