Search code examples
pythonflaskflask-sqlalchemyregistrationflask-security

Flask-Security: Customizing Registration


I'm trying to use Flask-Security to login and register users. However, I'm trying to do two things. The first being change the default /registration to /signup which I believe I've done correctly. The second is I want to customize the registration form. I can get the page loaded but whenever I submit the form nothing happens. No data is sent to the backend or inserted into the sqlite database.

I'm also not sure if I need to write my own app.route function to handle creating a new user and/or checking if they already exist. I'd like to be able to use flask-security's register but just tweak it. If that's even possible.

Here's my main python file:

from flask import Flask, redirect, flash, session, request, render_template
from flask_sqlalchemy import SQLAlchemy

import os, time


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
app.config['DATABASE'] = 'data.db'
app.config['SECURITY_TRACKABLE'] = True
app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_REGISTER_URL'] = '/signup'
app.config['SECURITY_REGISTER_USER_TEMPLATE'] = 'security/register.html'
# enable this if you want to send email and confirm
# app.config['SECURITY_CONFIRMABLE'] = True
db = SQLAlchemy(app)

from user import Security, User, user_datastore
from forms import logform, signupform, RegisterForm

security = Security(app, user_datastore, register_form=signupform,  login_form=logform,
                    confirm_register_form=signupform)

db.create_all()
db.session.commit()


@app.route('/')
def hello_world():
    log = logform(csrf_enabled=False)
    flash("HELLO.")
    return render_template("index.html", form=log)


@app.route('/login')
def login():
    print "/LOGIN REQUESTED"
    form = logform(csrf_enabled=False)
    return render_template("security/login_user.html", login_user_form=form, security=security)


#not sure if I need this function at all
@app.route('/signup', methods=['GET', 'POST'])
def signup():
    print "/SIGNUP REQUESTED"
    form = signupform(csrf_enabled=False)
    # form = RegisterForm()

    if request.method == 'GET':
        return render_template("security/register.html", register_user_form=form, security=security)

    else:
        #log = logform(csrf_enabled=False)
        if form.validate_on_submit() or form.validate():
            print "HERE NOW SOMEONE SUBMITTED SOMETHING"
            print form.email.data
            print form.username.data
            print form.password.data
            user_datastore.create_user(form)
            db.session.commit()

        return redirect('/')

I'm not sure if I need the /signup function or if flask-security will handle it on default. Next here's my logform and signupform classes:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo
from flask_security.forms import LoginForm, RegisterForm, Form


class logform(LoginForm):
    login = StringField('login', validators=[DataRequired()])
    # password = PasswordField('password', validators=[DataRequired()])


class signupform(RegisterForm):
    # email = StringField('email', validators=[Email(), DataRequired()])
    username = StringField('username', [DataRequired()])
    # password = PasswordField('password', validators=[DataRequired()])

Here's the current registration form I'm trying to use. I'm not sure if I should use url_for('signup') as flask-security's default registration form uses url_for_security('register').

<!DOCTYPE html>

{% extends "base.html" %}

{% block content %}

    <div id="signup">
        <h1>Create Account.</h1>
        <h3>TESTING</h3>
        {# not sure what to put as my action. default flask-security uses url_for_security('register') #}
        <form id="logfrm" name='register_form' method="POST" action="{{ url_for('signup') }}">
            {# figure out how to make input required #}

            {{ register_user_form.email(placeholder="email", type="email") }}<br>
            {{ register_user_form.username(placeholder="username") }}<br>
            {{ register_user_form.password(placeholder="password", type="password") }}<br>
            {# register_user_form.confirm(placeholder="confirm password") #}<br>
            {{ register_user_form.submit }}
            <input type="submit" value="SIGNUP" >

        </form>
    </div>

{% endblock %}

Any help would be great, thanks!


Thanks for the help. That really cleared up most of the trouble I was having. The only other thing wrong was in my form. I was forgetting that RegisterForm requires a password confirmation so I also added: {{ register_user_form.password_confirm(...) }}

and to also include a app.config['SECRET_KEY'] = 'blah' in my main file so that the registration doesn't run into an error.


Solution

  • You don't need to define a new route for the registration.

    In your signupform class that is inherited from RegisterForm you only need to override the validate method with your custom logic and return True or False as appropriate.

    In your html template for registration the action url is {{ url_for_security('register') }}

    The same applies for a custom login form - the url action though is {{ url_for_security('login') }}