Flask基础学习-flask-login篇

由fliter引发的思考

对于Python内置的一些函数例如:filter,map,reduce这些在代码中使用较少。这些高效的函数有时可以简化很多代码,以后注意要多使用。

关于SQLAlchemy的一个基类问题

我们可以创建一个基类,里面定义一些需要所有模型都要有的一些字段

1
2
3
4
5
class Base(db.Model):
# 将该类设置为抽象基类 才不会被创建为表
__abstract__ = True
create_time = Column('create_time', Integer)
status = Column(SmallInteger, default=1)

这样其他的模型创建只要继承该基类就好了。‘

一个由字典创建对象的快速方法。

1
2
3
for key, value in attrs.items():
if hasattr(self, key) and key != 'id':
setattr(self, key, value)

这里要求字典的key和模型类的属性是相同的

1. Python属性描述符实现getter和setter

通过属性描述符我们可以对模型的属性进行读写操作,以及读写操作前的预处理。

例如我们在数据库中保存用户密码的时候,并不是希望保存用户输入的明文密码,而是希望加密后的密码,这样我们可以使用属性描述符操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User(UserMixin, Base):
__tablename__ = 'user'

_password = Column('password', String(100))


@property
def password(self):
return self._password

@password.setter
def password(self, raw):
# 保存的时候加密
self._password = generate_password_hash(raw)

如上面代码中,我们将用户的原始输出密码raw加密后保存到用户模型中。

利用属性描述符我们可以实现属性的预处理,将属性设置为只读或者只写等。

2. WTForms的自定义验证器

我们之前学过WTForms,但是没写一个自定义字段验证器,下面就是一个自定义的验证器

1
2
3
4
5
6
7
8
9
10
class RegisterForm(Form):
nickname = StringField('昵称', validators=[
DataRequired(), Length(2, 10, message='昵称至少需要两个字符,最多10个字符')])

password = PasswordField('密码', validators=[
DataRequired(), Length(6, 20)])

def validate_email(self, field):
if User.query.filter_by(email=field.data).first():
raise ValidationError('电子邮件已被注册')

自定义验证器以validate开头,传入的参数就是对应的属性,使用field.data获得具体的值。自定义验证器无需传入到validators中,会自动生效。

3. 在视图函数之间使用redirect进行路由跳转

我们在不同视图函数之间想要实现路由跳转,需要使用redirect。

比如我们在注册成功之后跳转到登录页面。

1
redirect(url_for('web.login'))
4. 什么是cookie

我们的服务器会生成很多键值对的值,这些值包含在response中返回到浏览器,浏览器会存储起来。被浏览器存储起来的这些键值对就是cookie.我们可以给cookie设置有效期等特性。

cookie失效一般有两种原因:有效期过期,浏览器关闭。

flask中如何在响应中加入cookie呢?

1
2
response = make_response('Hello')
response.set_cookie('name', 'value', 100)

既然浏览器可以保存服务器返回的一些数据,我们可以在服务器生成一些票据,返回给浏览器,这样浏览器就能在票据没有过期的情况下,免登陆访问服务器。

在flask中我们可以使用check_password_hash比较加密的密码和原生密码。

1
2
check_password_hash(数据库密码,用户输入密码)
# 返回的结果是True或False
5. 使用Flask-Login插件管理登录

在flask中如果我们手动去管理cookie会很麻烦,我们可以使用flask-login实现登录管理

如何使用呢?

1. 实例化

如同其他插件一样,我们新的插件都要进行实例化操作

1
2
3
4
5
6
7
8
9
10
from flask_login import LoginManager


login_manager = LoginManager()

def create_app(config=None):
app = Flask(__name__)

# 注册login模块
login_manager.init_app(app)
2. 使用login_user登录

登录的实际是生成令牌保存到cookie中,我们使用login_user是先登录

1
2
3
4
from flask_login import login_user

# 通过login_user 间接将用户票据写入到cookie中 通过remember设置不过期
login_user(user, remember=True)

这里我们传入的参数是我们定义的用户模型的实例化,实际保存到cookie中做用户表示的是用户的id,那flask-login是如何知道哪个字段属性代表id呢?

这就需要我们在用户模型中创建一个函数,在这个函数中我们需要返回表示用户身份的字段

1
2
def get_id(self):
return self.id

除了这一个方法,flask-login还需要我们定义其他几个方法,我们可以通过用户模型继承UserMixin来简化继承,这样我们就可以不用再定义这些方法了。

1
2
3
4
5
6
from flask_login import UserMixin


# 用户模型
class User(UserMixin, db.Model):
__tablename__ = 'user'

注意:当标识用户身份的主键不是id字段的时候,同样需要重写get_id方法,然后返回唯一标识

完成上面配置后,再登录的话就会生成令牌键值对(用户的id)保存到浏览器的cookie中。

3. 访问权限控制

我们上面已经完成了flask-login的配置,对于一些需要登录才能访问的页面,我们可以使用login_required装饰器来完成。

在使用login_required之前我们还需要为插件编写一个函数

在用户模型模块

1
2
3
4
5
6
7
8
from . import login_manager

# 这个函数是被flask-logind
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))

# login_manager 就是我们实例化的flask-login记得导入

上面的函数是在我们调用current_user的时候,确保是当前用户模型的实例化。

1
2
3
4
from flask_login import current_user


current_user_id = current_user.id

完成后直接是视图函数上加上装饰器

1
2
@login_required
def somedef():

我们可以告诉flask-login插件我们的登录页面是哪个,可以在用户未登录的情况下,直接重定向到登录页面。

我们在实例化flask-login插件的时候加入下面的配置。

1
2
3
4
5
6
# 注册login模块
login_manager.init_app(app)

# 当用户访问一个未授权需要登录的页面的时候 我们制定login_view进行指定登录页面 login_message指定提示信息
login_manager.login_view = 'web.login'
login_manager.login_message = '请先登录或注册'

当用户在某个页面未登录跳转到登录跳转到登录页面,然后登录成功后,可以使用下面代码返回到之前的页面。

1
2
3
4
5
6
7
# flask_login插件会记录为登录页面的url 登录后直接重定向到之前的页面
# url保存是以args参数next=url
next = request.args.get('next')
# 为了防止重定向攻击,这里加入判断startswith('/')
if not next or not next.startswith('/'):
next = url_for('web.index')
return redirect(next)

判断一个用户是否登录

1
2
current_user.is_authenticated
# 返回值是一个布尔值

登出

1
2
from flask_login import logout_user
logout_user()
知识就是财富
如果您觉得文章对您有帮助, 欢迎请我喝杯水!