كيفية تشغيل Flask-Login وFlask-BrowserID وFlask-SQLAlchemy في تناغم؟
-
21-12-2019 - |
سؤال
بيت القصيد
أحاول إنشاء موقع ويب أساسي إلى حد ما باستخدام Flask (noob!) وأواجه مشكلة مع نظام تسجيل دخول المستخدم.لقد قررت أنني أريد استخدام Flask-Login وFlask-BrowserID (Mozilla Persona) وSQLAlchemy.سأجعل Persona هو الجزء الذي يعتني بتخزين كلمات مرور المستخدم، وهكذا، سأستخدم Flask-Login بمجرد مصادقة المستخدم لتتبع جلساته وسأستخدم SQLAlchemy لتخزين كل شيء فيه sqlite3 ديسيبل.لقد قمت بالكثير من التقلبات وأعتقد أنني قد انتهيت تقريبًا من هذه الميزات، لكن يبدو أنني لا أستطيع استعادة خطأ محدد.
تحديث 1
بناءً على تعليق دافيدية، اضطررت إلى إضافة db.Model إلى فئة المستخدم.لسوء الحظ، أدى ذلك إلى حل الخطأ الأول، ولكن الآن هناك خطأ جديد يجب التعامل معه.تم العثور على التتبع أدناه.
السؤال
ما يعطي؟من الواضح أنني أفتقد شيئًا ما، لكن يبدو أنني لا أستطيع العثور على ما هو.
الموارد التي عملت معها
- معرف متصفح القارورة: https://github.com/garbados/flask-browserid/wiki
- قارورة الدخول: http://flask-login.readthedocs.org/en/latest/
- قارورة SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application
- هل من الممكن تخزين كائنات فئة بايثون في SQLite؟
- مستندات SQLAlchemy: http://docs.sqlalchemy.org/en/rel_0_9/orm/tutorial.html
- معرف متصفح القارورة العامل: https://github.com/garbados/flask-browserid/blob/master/tests/فيه.السنة التحضيرية
معلومات إضافية
إليك ملفي main.py وindex.html الذي أستخدمه مع Flask وTraceback الذي أحصل عليه:
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 (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'
المحلول
الإجابة على السؤال الأصلي
ال User
يجب أن يكون النموذج فئة فرعية من db.Model
(أو فئة معينة) للعمل مع SQLAlchemy.
class User(UserMixin, db.Model):
...
الجواب على التحديث الأول
يبدو أنك لا تفهم ما يتم إرجاعه بواسطة session.query.إذا قمت بالاستعلام عن نموذج واحد، فإن "الصفوف" التي يتم إرجاعها هي مثيلات للنموذج.لن تكون الصفوف بلا شيء أبدًا.عند التحقق من وجودها، يجب عليك فقط استخدام .first()
إذا كنت تقوم بتطبيق المرشحات، أو .get(primary_key)
إذا كنت تقوم بالتصفية حسب المفتاح الأساسي.يحصل هذا على النتيجة الأولى فقط نظرًا لأن المستخدمين فريدون على أي حال
هذا هو ما الخاص بك get_user_by_id
يجب أن تبدو:
def get_user_by_id(id):
return User.query.get(id)
هذا هو ما الخاص بك get_user
يجب أن تبدو:
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