Django 2 UserCreationForm not creating user - forms

I'm using django-email-as-username so users can authenticate without a username and use their email instead. It seems that when I try to register a new user, I'm redirected back to the form page and the user isn't created. Am I supposed to be including the cleaned_data in my view?
Any advice?
forms.py
from django import forms
from django.contrib.auth import (authenticate, get_user_model,
password_validation)
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.utils.translation import ugettext_lazy as _
from cuser.models import CUser
UserModel = get_user_model()
class AuthenticationForm(forms.Form):
"""
Base class for authenticating users. Extend this to get a form that accepts
email/password logins.
"""
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(attrs={'autofocus': True}),
)
password = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput,
)
error_messages = {
'invalid_login': _(
"Please enter a correct %(username)s and password. Note that both "
"fields may be case-sensitive."
),
'inactive': _("This account is inactive."),
}
def __init__(self, request=None, *args, **kwargs):
"""
The 'request' parameter is set for custom auth use by subclasses.
The form data comes in via the standard 'data' kwarg.
"""
self.request = request
self.user_cache = None
super().__init__(*args, **kwargs)
self.username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
def clean(self):
email = self.cleaned_data.get('email')
password = self.cleaned_data.get('password')
if email and password:
self.user_cache = authenticate(self.request, email=email, password=password)
if self.user_cache is None:
# An authentication backend may reject inactive users. Check
# if the user exists and is inactive, and raise the 'inactive'
# error if so.
try:
self.user_cache = UserModel._default_manager.get_by_natural_key(email)
except UserModel.DoesNotExist:
pass
else:
self.confirm_login_allowed(self.user_cache)
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name},
)
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
def confirm_login_allowed(self, user):
"""
Controls whether the given User may log in. This is a policy setting,
independent of end-user authentication. This default behavior is to
allow login by active users, and reject login by inactive users.
If the given user cannot log in, this method should raise a
``forms.ValidationError``.
If the given user may log in, this method should return None.
"""
if not user.is_active:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',
)
def get_user_id(self):
if self.user_cache:
return self.user_cache.id
return None
def get_user(self):
return self.user_cache
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given email and
password.
"""
error_messages = {
'password_mismatch': _("The two password fields didn't match."),
}
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(attrs={'autofocus': True}),
)
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput,
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput,
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
class Meta:
model = CUser
fields = []
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
def _post_clean(self):
super()._post_clean()
# Validate the password after self.instance is updated with form data
# by super().
password = self.cleaned_data.get('password2')
if password:
try:
password_validation.validate_password(password, self.instance)
except forms.ValidationError as error:
self.add_error('password2', error)
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(),
)
password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=_(
"Raw passwords are not stored, so there is no way to see this "
"user's password, but you can change the password using "
"this form."
),
)
class Meta:
model = CUser
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password'].help_text = self.fields['password'].help_text.format('../password/')
f = self.fields.get('user_permissions')
if f is not None:
f.queryset = f.queryset.select_related('content_type')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
from django.urls import reverse
from .forms import UserCreationForm
def index(request):
return HttpResponse("This will be the profile homepage.")
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts')
else:
form = UserCreationForm()
return render(request, 'accounts/register.html', {'form': form})
urls.py
from django.urls import path
from django.conf.urls import include, url
from accounts import views
urlpatterns = [
path('', views.index, name='index'),
path('register/', views.register, name='register'),
register.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div>
<h1>Register</h1>
<form method="post" action="">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
</div>
</body>
</html>

Have you looked at custom user documentation? Seems to mention exactly what you mentioned, but as a caveat it requires you to do it on the first migration

Related

ktor send email with html template

I am wondering what is the correct way of sending HTML templates with ktor via email.
This answer Sending Emails From Ktor Application can help inline HTML, or simple string but not hbs or other templates which can be used in ktor.
email service will work, but I do want to use a template. And doing it via MustacheContent will not work
package com.meet.utils.email
import com.meet.utils.Constants
import org.apache.commons.mail.DefaultAuthenticator
import org.apache.commons.mail.HtmlEmail
fun sendForgotPasswordEmail(token: String, emailTo: String) {
val email = HtmlEmail()
email.hostName = "smtp.sendgrid.net"
email.setSmtpPort(587)
email.setAuthenticator(
DefaultAuthenticator(
"apikey",
"API_KEY"
)
)
email.isSSLOnConnect = true
email.setFrom(Constants.EMAIL_FROM)
email.subject = "Forgot Password"
email.setHtmlMsg("<html><body><div style='background:red;'>Hello</div></body></html>")
email.addTo(emailTo)
email.send()
}
What I want to do is
email.sendTemplate(MustacheContent("forgotPassword.hbs", mapOf("token" to token)))
how I can send this?
resources/templates/reset.hbs
<html>
<body>
<h1>Hello</h1>
<p>Please visit the link below to reset your password</p>
Reset your password
</body>
</html>
You can compile and render a template via a Mustache factory to get an HTML string. Here is an example:
val factory = DefaultMustacheFactory("templates")
embeddedServer(Netty, port = 3333) {
install(Mustache) {
mustacheFactory = factory
}
routing {
post("/") {
val content = MustacheContent("forgotPassword.hbs", mapOf("token" to "my-token"))
val writer = StringWriter()
factory.compile(content.template).execute(writer, content.model)
val html = writer.toString()
// Send email with an HTML
}
}
}.start(wait = true)

Flask-login current user disappearing on page refresh/redirect

I have a login route:
#bp.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
guest = Guest(**mongo.db.guests.find_one({'username': form.username.data}))
if guest is None or not guest.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('auth.login'))
login_user(guest, remember=form.remember_me.data)
print(current_user)
flash(f'Logged in {guest.name} successfully')
next_url = request.args.get('next')
if not next_url or url_parse(next_url).netloc != '':
next_url = url_for('main.index')
return redirect(next_url)
return render_template('login.html', form=form)
When a user logins in 'successfully' it flashes and redirects the user correctly. However on the destination page (main.index or profile.view) the user is not logged in.
index route/html
#bp.route('/')
def index():
return render_template('index.html')
{% block content %}
<h2>Home page</h2>
<h1>Hi, {{ current_user.username }}!</h1>
<p><a href={{ url_for('auth.register') }}>Register</a></p>
<p><a href={{ url_for('auth.login') }}>Sign in</a></p>
<p><a href={{ url_for('main.index') }}>Home page</a> (accessible to anyone)</p>
<p><a href={{ url_for('profile.view') }}>Profile View</a> (login required)</p>
<p><a href={{ url_for('profile.edit') }}>Profile Edit</a> (login required)</p>
{% endblock %}
When redirecting to profile.view
#bp.route('/view')
#login_required
def view():
return 'This is the profile view'
It hits the #login_required decorator and redirects back to the login.
I'm using Flask-login (duh), and Flask-Pymongo for my database.
Why does the flask-login current user disappear on page refresh/redirect? Thanks!
My #login_manager.user_loader method was incorrect.
Changed:
#login_manager.user_loader
def load_user(user_id):
from flaskr.models import Guest
guest = mongo.db.guests.find_one({'_id': user_id})
if guest is None:
return None
return Guest(**guest)
To:
#login_manager.user_loader
def load_user(user_id):
from flaskr.models import Guest
from flask_pymongo import ObjectId
guest = mongo.db.guests.find_one({'_id': ObjectId(user_id)})
if guest is None:
return None
return Guest(**guest)
The database search was always failing because the user_id wasn't a Flask_Pymongo.ObjectId.

Flask Error: “Method Not Allowed The method is not allowed for the requested URL” (Login and Home Page)

#app.route("/")
#app.route("/home",methods=['GET', 'POST'])
def home():
if current_user.is_authenticated:
posts=mongo.db.articles
#allpost=posts.find().limit(5)
it=current_user.user_json['interest']
allpost=posts.find( {'NewsType': it } ).limit(10)
#flash(session['email'])
return render_template('home.html', posts=allpost)
return render_template('login.html', title='Login',form=LoginForm())
This is my code for the Home Page
#app.route("/login", methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = LoginForm()
if form.validate_on_submit():
users = mongo.db.users
loginuser_json = users.find_one({'email': form.email.data})
if loginuser_json and bcrypt.check_password_hash(loginuser_json['password'], form.password.data):
# Create a custom user and pass it to login_user:
loginuser = User(loginuser_json)
login_user(loginuser,duration=d)
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('home'))
return redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check username and password', 'danger')
return render_template('login.html', title='Login', form=form)
and this for the login route
When i write localhost:5000 in the browser it opens Login Page (because if user is not authenticated it should go to login page first)
But when i try to login from this page , it is giving an error "Method Not Allowed.The method is not allowed for the requested URL."
What should i do
In your code, it is not clear which code is called when the method is GET (usually when rendering your login page) and which is to be called when method is POST (called when you are submitting your form). To remove the mix up, do like this in your login
#app.route("/login", methods=['GET', 'POST'])
def login():
# code that runs for both GET and POST goes here
if request.method == 'POST':
if form.validate_on_submit():
#authenticate user and redirect to next page/home page
return render_template('login.html', title='Login', form=form) #this runs when method is get
Note the indentation and the respective code running under it
NOTE on your login template ensure the form method is post something like this
<form method=post>
<dl>
{{ render_field(form.email) }}
{{ render_field(form.password) }}
{{form.hidden_tag()}}
</dl>
<input type=submit value=Submit>
</form>
Lastly, on you home page, you have two routes that form the url_for(home) - though I do not think this is where your error is coming from - ensure both of them accept the method POST if you intend to use it there
#app.route("/",methods=['GET', 'POST'])#add post on this route
#app.route("/home",methods=['GET', 'POST'])
def home():
Hope this helps you sort out the error

How can I keep field data after validating false in flask wtform?

I am new to flask and don't know how to keep field data after a failing post.
Thanks for your helps ^_^.
Example:
views.py:
#app.route('/', methods=['GET', 'POST', ])
def index():
form = MyForm()
if request.method == 'GET':
return render_template('index.html', form=form)
elif request.method == 'POST':
if form.validate_on_submit():
# blabla...
return redirect('/')
else: # validate false
# how to keep field data in new page?
return render_template('index.html', form=form) # it failed
It didn't work because I implement my own html form fields, in order to solve the problem, I should write template like this:
index.html:
<form ...>
{{ form.fieldname(class_='form-control', placeholder='hint') }}
</form>
Instead of inheriting your Form class from wtforms.Form, inherit it from flask_wtf.FlaskForm
For example, Replace this
from wtforms import Form
class RegistrationForm(Form):
#fields...
With this
from flask_wtf import FlaskForm
class RegistrationForm(FlaskForm):
#fields...

Redirects in Flask/Werkzeug are not changing the URL

I am very knew to python web development, so please bear with me.
I am trying setup a very basic log-in using Flask and the below code:
#app.route('/')
def index():
if verifyLoggedIn():
someData = gatherSomeData()
return render_template('main.html', data=someData)
else:
return redirect(url_for('login'))
#app.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
if request.form['usr'] == user and request.form['pwd'] == password:
session['user'] = request.form['usr']
session['passwd'] = request.form['pwd']
return redirect(url_for('index'))
else:
return render_template('out.html',name=request.form['usr'])
else:
return render_template('login.html')
When I access the page by going to 'localhost:5000/', I correctly get forwarded to
'localhost:5000/login/'. After I log-in, however, the page loads 'main.html' but the url bar still shows 'localhost:5000/login/'. The problem with this, is that if I hit refresh button to get the new value of 'someData' I end up back at the log-in screen. I find that this is the case after any post action. Am I missing something very fundamental here?
Thanks to those who responded, but after much more searching, I managed to find that the answer was already on stackoverflow :p (sorry, I really did look around a lot before asking)
Url in browser not updated after call of redirect( url_for('xxx' )) in Flask with jQuery mobile
The problem was actually being caused by jquery mobile and my lack of a data-url attribute.
The following code works for me, it's basically the same as what you're doing:
from flask import Flask, request, render_template, session, redirect, url_for
app = Flask(__name__)
app.config['SECRET_KEY'] = 'sldkf'
#app.route('/')
def index():
if session['user']:
return render_template('main.html')
else:
return redirect(url_for('login'))
#app.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
user = 'test'
password = 'test'
if request.form['usr'] == user and request.form['pwd'] == password:
session['user'] = request.form['usr']
session['passwd'] = request.form['pwd']
return redirect(url_for('index'))
else:
return render_template('login.html')
else:
return render_template('login.html')
#app.route('/logout/', methods=['GET'])
def logout():
session['user'] = None
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(debug=True)
You should check to see if the user is authenticated in your 'login' route. If so redirect to the 'index' route using return redirect(url_for('index')).