用户注册
每个用户都要注册一个账号。注册成功将在数据库中创建一个 User 结点,具有 username 和 password 属性,密码经过哈希处理。
注册页面位于 /register,接受 GET 和 POST 请求。访问者打开页面时发出 GET 请求,填写注册表单时发出 POST 请求。views.py 中 /register 视图定义如下:
from .models import User, get_recent_assets
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')
request 变量是 Flask 对象,用来处理传入请求,可以用来访问请求的数据。比如请求的方法就储存在 request.method,无论是 GET、POST 还是其它可能的类型。如上所述,访问者打开页面时发出 GET 请求,填写注册表单时发出 POST 请求。此视图检查方法的类型,如果是 GET 请求就简单地交由 Flask render_template() 返回来自 asset/templates 目录的 register.html 模版,并传递必要的上下文(在此例中,为错误信息)。然而, 如果是 POST 请求,username 和 password 将被处理,假如满足一切条件,用户将被创建。为了更好地理解,我们看下 models.py 中定义的 User 类。
from py2neo import Graph, Node, Relationship, authenticate
from passlib.hash import bcrypt
from datetime import datetime
import os
import uuid
url = os.environ.get('GRAPHENEDB_URL', 'http://localhost:7474')
username = os.environ.get('NEO4J_USERNAME')
password = os.environ.get('NEO4J_PASSWORD')
if username and password:
authenticate(url.strip('http://'), username, password)
graph = Graph(url + '/db/data/')
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
User 类的一个对象由 username 参数初始化。User.find() 方法调用 py2neo 的 Graph.find_one() 方法用给定用户名在数据库中查找标签为 User 的结点,返回一个 py2neo.Node 对象。对于 User 结点我们基于用户名属性建立了唯一性约束,则不能有多于一个用户使用同一用户名。User.register() 方法检查数据库中是否有同名用户;如果没有,用给定用户名和密码创建用户,传递 py2neo.Node 对象给 Graph.create() 方法。注册成功返回 True。
最后,为了完整地理解注册过程,我们必须要看下 register.html 模版:
{% 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 %}
views.py 中的 register() 方法用 flash 提供了 message 字符串。与 render_template() 一起传递的变量叫做 “上下文”,可以在相对应的 .html 模版中使用双大括号访问。在此是通过嵌入的 layout.html:
<!doctype html>
<title>My Assets</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div class="page">
<h1>My Assets</h1>
<div class="metanav">
{% if session.username %}
Logged in as {{ session.username }}
{% endif %}
<a href="{{ url_for('index') }}">Home</a>
{% if not session.username %}
<a href="{{ url_for('register') }}">Register</a>
<a href="{{ url_for('login') }}">Login</a>
{% else %}
<a href="{{ url_for('profile', username=session.username) }}">Profile</a>
<a href="{{ url_for('logout') }}">Logout</a>
{% endif %}
</div>
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
表单能够发出 POST 请求给 /register 视图,是由于此动作 action="{{ url_for('register') }}"。url_for() 是 Flask 用来访问视图函数中定义的 URL 的方法。表单数据可通过输入组件的名称获取;比如,用户填在 username 文本框里的字符串就用 request.form['username']。