¿Cómo ejecutar Flask-Login, Flask-BrowserID y Flask-SQLAlchemy en armonía?
-
21-12-2019 - |
Pregunta
El punto completo
Estoy intentando crear un sitio web bastante básico con Flask (¡novato!) y tengo problemas con el sistema de inicio de sesión del usuario.Decidí que quiero usar Flask-Login, Flask-BrowserID (Mozilla Persona) y SQLAlchemy.Voy a hacer que Persona sea la parte que se encargue de almacenar las contraseñas de los usuarios y demás, voy a usar Flask-Login una vez que el usuario haya sido autenticado para realizar un seguimiento de sus sesiones y voy a usar SQLAlchemy para almacenar todo en una base de datos sqlite3.He estado dando muchas vueltas y creo que casi he terminado estas funciones, pero parece que no puedo recuperar un error específico.
Actualización 1
Según el comentario de davidismo, tuve que agregar db.Model a la clase Usuario.Desafortunadamente, eso solucionó el primer error, pero ahora hay uno nuevo que solucionar.El rastreo se encuentra a continuación.
La pregunta
¿Lo que da?Obviamente me falta algo, pero parece que no puedo encontrar qué es.
Recursos con los que he estado trabajando
- Flask-BrowserID: https://github.com/garbados/flask-browserid/wiki
- Inicio de sesión en matraz: http://flask-login.readthedocs.org/en/latest/
- Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application
- ¿Es posible almacenar objetos de clase Python en SQLite?
- Documentos de SQLAlchemy: http://docs.sqlalchemy.org/en/rel_0_9/orm/tutorial.html
- TRABAJANDO Flask-BrowserID: https://github.com/garbados/flask-browserid/blob/master/tests/en eso.py
información adicional
Aquí están mis main.py e index.html que estoy usando con Flask y el Traceback que estoy obteniendo:
PRINCIPAL.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()
ÍNDICE.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>
Rastrear
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'
Solución
Respuesta a la pregunta original
El User
El modelo debe ser una subclase de db.Model
(o una clase asignada) para trabajar con SQLAlchemy.
class User(UserMixin, db.Model):
...
Respuesta a la primera actualización
Parece que no comprende qué devuelve session.query.Si consulta un solo modelo, las "filas" que se devuelven son instancias del modelo.Las filas nunca serán Ninguna.Cuando estés comprobando la existencia, deberías usar .first()
si está aplicando filtros, o .get(primary_key)
si está filtrando por clave principal.Esto obtiene el primer resultado solo porque los usuarios son únicos de todos modos.
Esto es lo que tu get_user_by_id
debería verse así:
def get_user_by_id(id):
return User.query.get(id)
Esto es lo que tu get_user
debería verse así:
def get_user(kwargs):
u = User.query.filter(db.or_(
User.id == kwargs.get('id'),
User.email == kwargs.get('email')
)).first()
if u is None: # user didn't exist in db
return create_browserid_user(kwargs)
return u