使用 Flask-Security 登录时遇到问题:'User' 没有属性 'get_user'

Trouble logging in with Flask-Security: 'User' has no attribute 'get_user'

当我使用 Flask-Security 的登录表单时,出现以下错误。它告诉我我没有 get_user 方法(这是我的 user_datastore 的一部分)。

Traceback (most recent call last):
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/app.py", line 1994, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask_security/decorators.py", line 225, in wrapper
    return f(*args, **kwargs)
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask_security/views.py", line 75, in login
    if form.validate_on_submit():
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask_wtf/form.py", line 101, in validate_on_submit
    return self.is_submitted() and self.validate()
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/flask_security/forms.py", line 230, in validate
    self.user = _datastore.get_user(self.email.data)
  File "/home/ubuntu/workspace/my_app/venv/lib/python3.6/site-packages/werkzeug/local.py", line 343, in __getattr__
    return getattr(self._get_current_object(), name)
AttributeError: type object 'User' has no attribute 'get_user'

当我深入研究 Flask-Security 的 datasource.py 时,我看到了以下方法:

def get_user(self, id_or_email):
    """Returns a user matching the specified ID or email address."""
    raise NotImplementedError

这似乎是一个抽象方法,必须在我的 User 模型中实现。如果那是真的,为什么 Flask-Security examples 的 none 实现这个方法?如果我尝试将该方法添加到我的 User 模型中,事情也不会变得更好。这是我使用实验覆盖方法的模型:

from extensions import db
from blog.models import Post
from sentence.models import Sentence
from roster.models import Roster
from datetime import datetime
import datetime
from flask_security import UserMixin, RoleMixin

# Helper table for a many-to-many relationship
roles_users = db.Table('roles_users',
        db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
        db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))

class Role(db.Model, RoleMixin):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(80), unique=True)
    description = db.Column(db.String(255))

class User(db.Model, UserMixin):
    # general variables
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(155))
    last_name = db.Column(db.String(155))
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    active = db.Column(db.Boolean(), default=True)
    confirmed_at = db.Column(db.DateTime())

    # app-specific variables
    paid_until = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    lifetime = db.Column(db.Boolean, default=False)

    # relations
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))
    posts = db.relationship('Post', backref='user', lazy='dynamic')
    sentences = db.relationship('Sentence', backref='user', lazy='dynamic')

    def __init__(self, **kwargs):
        self.password = kwargs['password']
        self.email = kwargs['email']
        if kwargs.get('first_name', False):
            self.first_name = kwargs['first_name']
            self.last_name = kwargs['last_name']

        # create a roster
        roster = Roster("default", self.email)
        db.session.add(roster)
        db.session.commit()

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

    # __str__ is required by Flask-Admin (not using?), so we can have human-readable values for the Role when editing a User.
    # If we were using Python 2.7, this would be __unicode__ instead.
    def __str__(self):
        return self.name

    # __hash__ is required to avoid the exception TypeError: unhashable type: 'Role' when saving a User
    def __hash__(self):
        return hash(self.name)

    def get_user(self, *args):
        # I had to use *args or I'd always get missing 1 required positional argument
        for arg in args:
            user = User.query.filter_by(email=arg).first()
            if user:
                return user
'''    
    def find_user(self, *args, **kwargs):
        """Returns a user matching the provided parameters."""
        raise NotImplementedError

    def find_role(self, *args, **kwargs):
        """Returns a role matching the provided name."""
        raise NotImplementedError
'''

如果我不使用 *args,上述方法会导致 missing 1 required positional argument。正如上面所写的模型,应用程序不会崩溃,但会显示 "Specified user does not exist",但事实并非如此,因为我可以在数据库 CLI 中看到用户。那么,我是否需要实施 get_user 方法

问题出在 Flask 应用程序的 __init__ 中。正如所怀疑的那样,Flask-Security 尝试使用用户模型调用 get_user 表明在调用 security.init_app(app, user_datastore) 时传递了错误的对象。来自 user_datastore 实例化的相同参数被重用于 security.init_app(),这就是错误访问 User 模型的原因。