-Flask开发轻博客(五):用户登录

作者:chen_h
微信号 & QQ:862251340
微信公众号:coderpai


目录

Flask开发轻博客(一):欢迎来到 Flask 的世界

Flask开发轻博客(二):Flask 模板

Flask开发轻博客(三):Flask 的 Web 表单

Flask开发轻博客(四):数据库

Flask开发轻博客(五):用户登录

Flask开发轻博客(六):用户首页和发布博客

Flask开发轻博客(七):分页


上节回顾

上一章中,我们已经创建了数据库以及学会了使用它来存储用户以及 blog,但是我们并没有把它融入我们的应用程序中。在两章以前,我们已经看到如何创建表单并且留下了一个完全实现的登录表单。

在本章中我们将会建立 web 表单和数据库的联系,并且编写我们的登录系统。在本章结尾的时候,我们这个小型的应用程序将能够让用户能够登入和登出。

我们接下来讲述的正是我们上一章离开的地方,所以你可能要确保应用程序 micblog 正确地安装和工作。

一. 配置

像以前章节一样,我们从配置将会使用到的 Flask 扩展开始入手。对于登录系统,我们将会使用到扩展:Flask-Login。配置情况如下(文件app/__init__.py ):

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager


# 初始化flask应用
app = Flask(__name__)
app.config.from_object('config')

# 初始化数据库
db = SQLAlchemy(app)

# 初始化flask-Login
lm = LoginManager()
lm.setup_app(app)

from app import views, models

二. 重构用户模型

Flask-Login 扩展需要在我们的 User 类中实现一些特定的方法。但是类如何去实现这些方法却没有什么要求。

下面就是我们为 Flask-Login 实现的 User 类(修改文件 app/models.py):

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(15), index=True, unique=True)
    email = db.Column(db.String(128), index=True, unique=True)
    role = db.Column(db.SmallInteger, default=ROLE_USER)
    posts = db.relationship('Post', backref='author', lazy='dynamic')

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return unicode(self.id)

    @classmethod
    def login_check(cls, user_name):
        user = cls.query.filter(db.or_(
            User.nickname == user_name, User.email == user_name)).first()

        if not user:
            return None

        return user

    def __repr__(self):
        return '<User %r>' % (self.nickname)

is_authenticated 方法有一个具有迷惑性的名称。一般而言,这个方法应该只返回 True,除非表示用户的对象因为某些原因不允许被认证。

is_active 方法应该返回 True,除非是用户是无效的,比如因为他们的账号被禁止。

is_anonymous 方法应该返回 True,除非是伪造的用户不允许登录系统。

最后,get_id 方法应该返回一个用户唯一的标识符,以 unicode 格式返回我们使用数据库生成的唯一的id。

三. user_loader 回调

现在我们已经准备好用 Flask-Login 扩展来开始实现登录系统。

首先,我们必须编写一个函数用于从数据库加载用户。这个函数将会被 Flask-Login 使用(文件 app/views.py )

@lm.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

请注意在 Flask-Login 中的用户的 id 永远是 unicode 字符串,因此在我们把id 发送给 Flask-SQLAlchemy 之前,需要把id转成整型是必须的,否则会报错!至于原因是什么就需要大家研究一下了,其实很简单的~

四. 登录视图函数

接下来我们需要更新我们的登录视图函数(文件 app/views.py )

import datetime

from flask import render_template, flash, redirect, session, url_for, request, g 
from flask.ext.login import login_user, logout_user, current_user, login_required 

from models import User, Post, ROLE_USER, ROLE_ADMIN
from app import app, db, lm


@lm.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

... # 这里省略的是我们的index函数


@app.route('/login', methods=['GET', 'POST'])
def login():
    # 验证用户是否被验证
    if current_user.is_authenticated:
        return redirect('index')
    # 注册验证
    form = LoginForm()
    if form.validate_on_submit():
        user = User.login_check(request.form.get('user_name'))
        if user:
            login_user(user)
            user.last_seen = datetime.datetime.now()

            try:
                db.session.add(user)
                db.session.commit()
            except:
                flash("The Database error!")
                return redirect('/login')

            flash('Your name: ' + request.form.get('user_name'))
            flash('remember me? ' + str(request.form.get('remember_me')))
            return redirect(url_for("users", user_id=current_user.id))
        else:
            flash('Login failed, Your name is not exist!')
            return redirect('/login')

    return render_template(
        "login.html",
        title="Sign In",
        form=form)

注意我们这里导入了不少新的模块,一些模块我们将会在不久后使用到。

整个流程就是,验证用户,验证用户是否已经注册,如果注册则从数据库中加载用户并转到用户页面。

如果要让这些都起作用的话,Flask-Login 需要知道哪个视图允许用户登录。我们在应用程序模块初始化中配置(文件 app/*init.py*)

# 初始化flask-login
lm = LoginManager()
lm.setup_app(app)

五. 首页视图

在前面的章节中,我们的 index 视图函数使用了伪造的对象,因为那时候我们并没有用户或者 blog。好了,现在我们有用户了,让我们使用它:

@app.route('/')
@app.route('/index')
def index():
    user = 'Man'
    posts = [
        {
            'author': {'nickname': 'John'},

            'body': 'Beautiful day in Portland!'
        },

        {
            'author': {'nickname': 'Susan'},

            'body': 'The Avengers movie was so cool!'
        }
    ]
    return render_template(
        "index.html",
        title="Home",
        user=user,
        posts=posts)

这是运行应用程序最好的时候了!

六. 登出

我们已经实现了登录,现在是时候增加登出的功能。

登出的视图函数是相当地简单(文件 app/views.py )

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))

注意,这里的 login_required 是为了验证用户必须是登陆的前提,才会有登出。

七. 注册

在注册前,我们需要修改app/forms.py文件以绑定数据库:

from flask_wtf import Form
from wtforms import TextField, BooleanField, SubmitField, TextAreaField
from wtforms.validators import Required, Email, Length


class LoginForm(Form):
    user_name = TextField('user name', validators=[
        Required(), Length(max=15)])
    remember_me = BooleanField('remember me', default=False)
    submit = SubmitField('Log in')

class SignUpForm(Form):
    user_name = TextField('user name', validators=[
        Required(), Length(max=15)])
    user_email = TextField('user email', validators=[
        Email(), Required(), Length(max=128)])
    submit = SubmitField('Sign up')

这里添加了类 SignUpForm,用户用户名和邮件的注册提交。

接下来,实现用户注册视图(文件 app/views.py ):

from forms import LoginForm, SignUpForm


@app.route('/sign-up', methods=['GET', 'POST'])
def sign_up():
    form = SignUpForm()
    user = User()
    if form.validate_on_submit():
        user_name = request.form.get('user_name')
        user_email = request.form.get('user_email')

        register_check = User.query.filter(db.or_(
            User.nickname == user_name, User.email == user_email)).first()
        if register_check:
            flash("error: The user's name or email already exists!")
            return redirect('/sign-up')

        if len(user_name) and len(user_email):
            user.nickname = user_name
            user.email = user_email
            user.role = ROLE_USER
            try:
                db.session.add(user)
                db.session.commit()
            except:
                flash("The Database error!")
                return redirect('/sign-up')

            flash("Sign up successful!")
            return redirect('/index')

    return render_template(
        "sign_up.html",
        form=form)

在提交注册信息的时候验证数据库中是否已经注册该用户信息,如果没有注册则在数据库中提交该信息,并显示注册成功,转到首页。

首先,修改主页(index.html):


{% extends "base.html" %}

{% block content %}

{% if not current_user.is_authenticated %}
<h1>Hi, Guys!</h1>
{% else %}
<h1>Welcome back, {{ current_user.nickname }}!</h1>
{% endif %}

{% for post in posts %}
    <p>{{ post.author.nickname }} says: <b>{{ post.body }}</b></p>
{% endfor %}
{% endblock %}

接下来,修改登录模版(login.html):


{% extends "base.html" %}

{% block content %}
<h1>Sign In</h1>
<form action="/login" method="post" name="login">
   {{ form.hidden_tag() }}
   <p>Please enter your name: {{ form.user_name }}</p>
   {% for error in form.errors.user_name %}
   <p style="color:red;">[-] {{ error }}</p>
   {% endfor %}

   <p>记住我? {{ form.remember_me }}</p>
   <p>{{ form.submit }}</p>

</form>
{% endblock %}

当然,你现在如果运行程序的话,肯定会说用户名不存在,因为我们还需要建立一个注册模版(sign_up.html):

{% extends "base.html" %}

{% block content %}

<form action="/sign-up" method="POST" name="sign_up">
    {{ form.hidden_tag() }}
    <p>Nick name: {{ form.user_name }}</p>
    {% for error in form.errors.user_name %}
    <p style="color:red;">[-] {{ error }}</p>
    {% endfor %}

    <p>E-mail: {{ form.user_email }}</p>
    {% for error in form.errors.user_email %}
    <p style="color:red;">[-] {{ error }}</p>
    {% endfor %}

    <p>{{ form.submit }}</p>
</form>

{% endblock %}

但是我们还没有在模版中添加登出和注册的链接。我们将要把这个链接放在基础层中的导航栏里(文件 app/templates/base.html )

<html>
  <head>
    {% if title %}
    <title>{{title}} - microblog</title>
    {% else %}
    <title>microblog</title>
    {% endif %}
  </head>
  <body>
    <div>Microblog: 
        <a href="{{ url_for('index') }}">Home</a>
        {% if not current_user.is_authenticated %}
        | <a href="{{ url_for('login') }}">Log in</a>
        or <a href="{{ url_for('sign_up') }}">Sign up</a>
        {% else %}
        | <a href="{{ url_for('logout') }}">Logout</a>
        {% endif %}
    </div>
    <hr />
    {% with messages = get_flashed_messages() %}
    {% if messages %}
    <ul>
        {% for message in messages %}
        <li>{{ message }}</li>
        {% endfor %}
    </ul>
    {% endif %}
    {% endwith %}
    {% block content %}{% endblock %}
  </body>

  {% block js %}{% endblock %}
</html>

实现起来是不是很简单?我们已经添加了登出链接。我们也正好利用这个机会在模版中使用 url_for

接下来,大家试试我们的注册与登陆函数吧,效果如下:

Home界面

Login界面

Sign Up界面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值