View on GitHub

neo4j-flask

A microblog application powered by Flask and Neo4j.

Download this project as a .zip file Download this project as a tar.gz file

Register a User

Before any content can be created on our blog, users will need to be able to sign up for an account. When successful, this will create a node in the database with a :User label and username and password properties, where the password is hashed.

The registration page is located at /register and will accept both GET and POST requests. A GET request will be sent when a visitor lands on the page, and a POST request will be sent when they fill out the registration form. In views.py, the /register view is defined by the following:

from .models import User, get_todays_recent_posts
from flask import Flask, request, session, redirect, url_for, render_template, flash

app = Flask(__name__)

@app.route('/register', methods=['GET','POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']

        if len(username) < 1:
            flash('Your username must be at least one character.')
        elif len(password) < 5:
            flash('Your password must be at least 5 characters.')
        elif not User(username).register(password):
            flash('A user with that username already exists.')
        else:
            session['username'] = username
            flash('Logged in.')
            return redirect(url_for('index'))

    return render_template('register.html')

The request variable is a Flask object that parses the incoming request, allowing you to access the request’s data. For example, the method of the request (either GET or POST or whatever is allowed) is stored on request.method. As I said before, when a user lands on the page a GET request is sent. When a user fills out the registration form, a POST request is sent. This view checks the method type, and if it is a GET request it simply returns the template register.html with Flask’s render_template(), which looks into the blog/templates directory for templates and passes any necessary context (in this case, an error message). However, if it is a POST request, the username and password are parsed from the request and a user is created if their inputs meet all of the criteria. To understand this better, we’ll have to look at part of the User class that was defined in models.py.

from py2neo import Graph, Node, Relationship
from passlib.hash import bcrypt
from datetime import datetime
import uuid

graph = Graph()

class User:
    def __init__(self, username):
        self.username = username

    def find(self):
        user = graph.find_one("User", "username", self.username)
        return user

    def register(self, password):
        if not self.find():
            user = Node("User", username=self.username, password=bcrypt.encrypt(password))
            graph.create(user)
            return True
        else:
            return False

An object of class User is initialized with a username argument. The User.find() method uses py2neo’s Graph.find_one() method to find a node in the database with label :User and the given username, returning a py2neo.Node object. Recall that a uniqueness constraint was created for :User nodes based on the username property, so there will not be more than one user with the given username. The User.register() method checks if a user with that username already exists in the database; if not, then a user is created with the given username and password by passing the py2neo.Node object to the Graph.create() method. True is returned to indicate that the registration was successful.

Finally, to fully understand the registration procedure we should take a look at the register.html template:

{% extends "layout.html" %}
{% block body %}
  <h2>Register</h2>
    <form action="{{ url_for('register') }}" method="post">
        <dl>
            <dt>Username:</dt>
            <dd><input type="text" name="username"></dd>
            <dt>Password:</dt>
            <dd><input type="password" name="password"></dd>
        </dl>
        <input type="submit" value="Register">
    </form>
{% endblock %}

Recall in views.py that the register() method defined a variable error with a string telling the user what they did wrong. The variables passed along with render_template() are called context and are made available in the context of the template. Thus, they can be accessed with double curly braces in the respective template .html file. If error is not None, then it is displayed to the visitor.

The form sends a POST request to the /register view due to action="{{ url_for('register') }}", where url_for() is a Flask method for accessing URLs defined in view functions. The argument to url_for is a string and refers to the name of the function that handles the view. Because the function that handles the view is named register (def register():), we pass the string “register” to the url_for function.

The form’s data is accessed with the input’s name; for example, the string that the user types into the username text box is accessed with request.form['username'] because the input was defined as <input type="text" name="username">.

Next: Login a User