title: Trouble logging in with Flask-Security: 'User' has no attribute 'get_user'
body: <p>When I use Flask-Security's login form, I get the following error. It's telling me that I don't have a <code>get_user</code> method, (which is a part of my <code>user_datastore</code>).</p>
  
  <pre><code>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'
  </code></pre>
  
  <p>When I dug into Flask-Security's <code>datasource.py</code>, I see the following method: </p>
  
  <pre><code>def get_user(self, id_or_email):
      """Returns a user matching the specified ID or email address."""
      raise NotImplementedError
  </code></pre>
  
  <p>This seems like an abstract method and must be implemented within my <code>User</code> model. If that's true, why do none of the <a href="https://pythonhosted.org/Flask-Security/quickstart.html#id1" rel="nofollow noreferrer">Flask-Security examples</a> implement this method? If I try to add the method to my <code>User</code> model, things don't get much better. Here's my model with the experimental overridden method:</p>
  
  <pre><code>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 '&lt;User %r&gt;' % 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
  '''
  </code></pre>
  
  <p>The above approach results either in <code>missing 1 required positional argument</code> if I don't use <code>*args</code>. As the model is written above, the app doesn't crash but says "Specified user does not exist", which is not the case as I can see the user in the database CLI. So, <em>do I need to implement the <code>get_user</code> method</em>? </p>