The Whole Point
I am attempting to make a fairly basic website with Flask (noob!) and am running into trouble with the user login system. I've decided that I want to use Flask-Login, Flask-BrowserID (Mozilla Persona) and SQLAlchemy. I am going to have Persona be the part that takes care of storing user passwords and such, I am going to use Flask-Login once the user has been authenticated to keep track of their sessions and I am going to use SQLAlchemy to store everything in an sqlite3 db. I've done a lot of bouncing around and I think I have almost finished these features, but I cannot seem to get back a specific error.
Update 1
Based on the comment by davidism, I had to add db.Model to the User class. Unfortunately, that solved the first error, but now there is a new one to deal with. Traceback found below.
The Question
What gives? I am obviously missing something, but I cannot seem to find what that is.
Resources I have been working with
- Flask-BrowserID: https://github.com/garbados/flask-browserid/wiki
- Flask-Login: http://flask-login.readthedocs.org/en/latest/
- Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application
- Is it possible to store Python class objects in SQLite?
- SQLAlchemy Docs: http://docs.sqlalchemy.org/en/rel_0_9/orm/tutorial.html
- WORKING Flask-BrowserID: https://github.com/garbados/flask-browserid/blob/master/tests/init.py
Additional Information
Here is my main.py and index.html I am using with Flask and the Traceback I am getting:
MAIN.py
import sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
from contextlib import closing
import time
from flask.ext.login import LoginManager, UserMixin
from flaskext.browserid import BrowserID
from flask.ext.sqlalchemy import SQLAlchemy
## SETUP
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/flaskr.db'
db = SQLAlchemy(app)
app.config.from_object(__name__)
app.config['BROWSERID_LOGIN_URL'] = "/login"
app.config['BROWSERID_LOGOUT_URL'] = "/logout"
app.config['SECRET_KEY'] = "deterministic"
app.config['TESTING'] = True
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.UnicodeText, unique=True)
firstname = db.Column(db.Unicode(40))
lastname = db.Column(db.Unicode(40))
date_register = db.Column(db.Integer)
bio = db.Column(db.Text)
facebook = db.Column(db.Unicode(1000))
twitter = db.Column(db.Unicode(1000))
website = db.Column(db.Unicode(1000))
image = db.Column(db.LargeBinary)
def __init__(self, email, firstname=None, lastname=None, date_register=None, bio=None, facebook=None, twitter=None,
website=None, image=None):
self.email = email
self.firstname = firstname
self.lastname = lastname
self.date_register = time.time()
self.bio = bio
self.facebook = facebook
self.twitter = twitter
self.website = website
self.image = image
self.email = email
def __repr__(self):
return '<User %r>' % self.email
### Login Functions ###
def get_user_by_id(id):
"""
Given a unicode ID, returns the user that matches it.
"""
for row in db.session.query(User).filter(User.id == id):
if row is not None:
return row.User
return None
def create_browserid_user(kwargs):
"""
Takes browserid response and creates a user.
"""
if kwargs['status'] == 'okay':
user = User(kwargs['email'])
db.session.add(user)
db.session.commit()
return user
else:
return None
def get_user(kwargs):
"""
Given the response from BrowserID, finds or creates a user.
If a user can neither be found nor created, returns None.
"""
import pdb; pdb.set_trace()
# try to find the user
for row in db.session.query(User).filter(User.email == kwargs.get('email')):
if row is not None:
return row.User
for row in db.session.query(User).filter(User.id == kwargs.get('id')):
if row is not None:
return row.User
# try to create the user
return create_browserid_user(kwargs)
login_manager = LoginManager()
login_manager.user_loader(get_user_by_id)
login_manager.init_app(app)
browserid = BrowserID()
browserid.user_loader(get_user)
browserid.init_app(app)
### Routing ###
@app.route('/')
def home():
return render_template('index.html')
if __name__ == '__main__':
app.run()
INDEX.html
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://login.persona.org/include.js" type="text/javascript"></script>
<script type="text/javascript">{{ auth_script|safe }}</script>
</head>
<body>
{% if current_user.is_authenticated() %}
<button id="browserid-logout">Logout</button>
{% else %}
<button id="browserid-login">Login</button>
{% endif %}
</body>
</html>
Traceback
Traceback (most recent call last):
File "/Library/Python/2.7/site-packages/flask/app.py", line 1701, in __call__
return self.wsgi_app(environ, start_response)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1689, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Library/Python/2.7/site-packages/flask/app.py", line 1687, in wsgi_app
response = self.full_dispatch_request()
File "/Library/Python/2.7/site-packages/flask/app.py", line 1360, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1358, in full_dispatch_request
rv = self.dispatch_request()
File "/Library/Python/2.7/site-packages/flask/app.py", line 1344, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/jzeller/Classes/CS494/main.py", line 106, in home
return render_template('test.html')
File "/Library/Python/2.7/site-packages/flask/templating.py", line 123, in render_template
ctx.app.update_template_context(context)
File "/Library/Python/2.7/site-packages/flask/app.py", line 692, in update_template_context
context.update(func())
File "/Library/Python/2.7/site-packages/flask_login.py", line 799, in _user_context_processor
return dict(current_user=_get_user())
File "/Library/Python/2.7/site-packages/flask_login.py", line 768, in _get_user
current_app.login_manager._load_user()
File "/Library/Python/2.7/site-packages/flask_login.py", line 348, in _load_user
return self.reload_user()
File "/Library/Python/2.7/site-packages/flask_login.py", line 312, in reload_user
user = self.user_callback(user_id)
File "/Users/jzeller/Classes/CS494/main.py", line 60, in get_user_by_id
print "get_user_by_id - " + str(type(row.User)) + " - " + str(row.User)
AttributeError: 'User' object has no attribute 'User'
Answer to original question
The
User
model needs to be a subclass ofdb.Model
(or a mapped class) to work with SQLAlchemy.Answer to first update
You don't seem to understand what is getting returned by session.query. If you query a single model, the "rows" that are returned are instances of the model. The rows will never be None. When you're checking for existence, you should just use
.first()
if you are applying filters, or.get(primary_key)
if you are filtering by primary key. This gets the first result only since the users are unique anywayThis is what your
get_user_by_id
should look like:This is what your
get_user
should look like: