Python Flask framework to implement simple login function tutorial, pythonflask
Review
In the previous series, we created a database and learned to fill it with users and emails, but we have not yet been able to implant it into our program. Before the two chapters, we have seen how to create a network form and left a complete login form.
In this article, we will build and implement our own user logon system based on the network forms and databases we have learned. At the end of the tutorial, our applet will implement the new user registration, login and exit functions.
In order to keep up with this chapter, you need the Weibo program that we leave in the last part of the previous chapter. Make sure that your program has been correctly installed and run.
In the previous section, we began to configure the Flask extension we will use. To log on to the system, we will use two extensions: Flask-Login and Flask-OpenID. The configuration is as follows (fileapp \ __init _. py ):
import osfrom flaskext.login import LoginManagerfrom flaskext.openid import OpenIDfrom config import basedir lm = LoginManager()lm.setup_app(app)oid = OpenID(app, os.path.join(basedir, 'tmp'))
The Flask-OpenID extension requires a Temporary Folder path to store temporary files. To this end, we provide its location.
Revisit our user model
The Flask-Login Extension must implement some methods in our User class. In addition to these methods, classes are not required to implement other methods.
The following is our User class (fileapp/models. py ):
class User(db.Model): id = db.Column(db.Integer, primary_key = True) nickname = db.Column(db.String(64), unique = True) email = db.Column(db.String(120), unique = True) role = db.Column(db.SmallInteger, default = ROLE_USER) posts = db.relationship('Post', backref = 'author', lazy = 'dynamic') def is_authenticated(self): return True def is_active(self): return True def is_anonymous(self): return False def get_id(self): return unicode(self.id) def __repr__(self): return '<User %r>' % (self.name)
The is_authenticated method is a misleading name method. Generally, this method should return True unless the object represents a user that has not been authenticated for some reason.
The is_active method should return True for the user unless the user is not activated. For example, they have been disabled.
The is_anonymous method should return True for users not permitted to log on.
Finally, the get_id method returns a unique unicode identifier. We use the database layer to generate a unique id.
User load callback
Now we use the Flask-Login and Flask-OpenID extensions to log on to the system.
First, we need to write a method to load data from the database to a user. This method will be used by Flask-Login (fileapp/views. py ):
@lm.user_loaderdef load_user(id): return User.query.get(int(id))
Remember that the user id in Flask-Login has always been of the unicode type. Therefore, when we pass the id to Flask-SQLAlchemy, it is necessary to convert it to the integer type.
Logon view Functions
Next we will update the logon view function (fileapp/views. py ):
from flask import render_template, flash, redirect, session, url_for, request, gfrom flaskext.login import login_user, logout_user, current_user, login_requiredfrom app import app, db, lm, oidfrom forms import LoginFormfrom models import User, ROLE_USER, ROLE_ADMIN @app.route('/login', methods = ['GET', 'POST'])@oid.loginhandlerdef login(): if g.user is not None and g.user.is_authenticated(): return redirect(url_for('index')) form = LoginForm() if form.validate_on_submit(): session['remember_me'] = form.remember_me.data return oid.try_login(form.openid.data, ask_for = ['nickname', 'email']) return render_template('login.html', title = 'Sign In', form = form, providers = app.config['OPENID_PROVIDERS'])
Note that we have imported some new modules, some of which will be used later.
The last version has changed little. We added a new decorator oid. loginhandler to the view function. It tells Flask-OpenID that this is our login view function.
At the beginning of the method body, we check whether the user has been authenticated. If so, we will redirect to the index page. The idea here is that if a user has logged on, we will not have it perform Secondary Logon.
The global variable g is set by Flask and is used to store and share data in a request lifecycle. So I guess you have already thought of it. We will put the logged-on user into the g variable.
The url_for () method we use when calling redirect () is defined by Flask to get the url from the given view method. If you want to redirect to the index page, you h may use redirect ('/Index'), but we have a good reason to let Flask construct a url for you.
When we get the returned data from the login form, the code to be run is also newly written. Here we will do two things. First, we save the Boolean value of remember_me to the Flask session. Do not confuse it with the db. session of Flask-SQLAlchemy. We already know that Flask g objects are used to save and share data in the lifecycle of a request. Flask sessions along this line provide more and more complex services. Once the data is stored in the session, the request initiated by the same client and the subsequent request will survive and will not die. The data will remain in the session until it is explicitly removed. To achieve this, Flask creates a session for each client.
The following oid. try_login uses Flask-OpenID to perform user authentication. This method has two parameters: openid provided by web forms and list data items provided by OpenID provider. Because we have defined the User class that contains both nickname and email, we need to find the nick name and email items.
OpenID-based authentication is asynchronous. If the authentication succeeds, Flask-OpenID will call the method registered by oid. after_login. If the authentication fails, the user will be redirected to the login page.
Flask-OpenID login callback
This is our after_login method (app/views. py)
@oid.after_logindef after_login(resp): if resp.email is None or resp.email == "": flash('Invalid login. Please try again.') redirect(url_for('login')) user = User.query.filter_by(email = resp.email).first() if user is None: nickname = resp.nickname if nickname is None or nickname == "": nickname = resp.email.split('@')[0] user = User(nickname = nickname, email = resp.email, role = ROLE_USER) db.session.add(user) db.session.commit() remember_me = False if 'remember_me' in session: remember_me = session['remember_me'] session.pop('remember_me', None) login_user(user, remember = remember_me) return redirect(request.args.get('next') or url_for('index'))
The resp parameter passed to the after_login method contains some information returned by OpenID provider.
The first if statement is only for verification. We need a valid email, so we cannot log on to an email that is not provided.
Next, we will find the database by email. If the email is not found, we think it is a new user, so we will add a new user to the database, just as we learned from the previous chapter. Note that we have not processed nickname, because some OpenID providers do not contain this information.
After doing this, we will get the remember_me value from the Flask session. If it exists, it is a boolean value that we saved to the session in the login view method.
Then we call the login_user method of Flask-Login to register this valid logon.
Finally, we will redirect to the next page in the last line, or if the next page is not provided in the request, we will redirect to the index page.
The concept of jump to the next page is very simple. For example, You need to log on to a page, but you are not logged on now. In Flask-Login, you can use the login_required modifier to limit Unlogged users. If a user wants to connect to a limited url, the user will be automatically redirected to the login page. Flask-Login will save the original url as the next page. Once the Login is complete, we will jump to this page.
To do this, Flask-Login needs to know the page on which the user is currently located. We can configure it in the app initialization component (app/_ init _. py ):
lm = LoginManager()lm.setup_app(app)lm.login_view = 'login'
Global variable g. user
If you are very focused, you should remember to check g. user in the login view method to determine whether a user has logged on. To achieve this, we will use the before_request event provided by Flask. Any method decorated by the before_request modifier will be executed with the view method in advance when each request is received. So here we will set our g. user variable (app/views. py ):
@app.before_requestdef before_request(): g.user = current_user
This is all it has to do. The current_user global variable is set by Flask-Login, so we just need to copy it to the g variable that is easier to access. In this way, all requests can access the login user, or even internal templates.
Index View
In the previous chapter, we used false code to leave our index view, because at that time there were no users or blog articles in our system. Now we have users, so let's do it:
@app.route('/')@app.route('/index')@login_requireddef index(): user = g.user posts = [ { 'author': { 'nickname': 'John' }, 'body': 'Beautiful day in Portland!' }, { 'author': { 'nickname': 'Susan' }, 'body': 'The Avengers movie was so cool!' } ] return render_template('index.html', title = 'Home', user = user, posts = posts)
There are only two changes in this method. First, we added the login_required modifier. This indicates that only login users can access this page.
Another change is to pass g. user to the template and replace the false objects between them.
Now we can run our application.
When we connect to http: // localhost: 5000, you will see the login page. Remember that if you log on through OpenID, you must use the OpenID URL provided by your provider. You can use any OpenID provider in the URL below to generate a correct URL for you.
As part of the login process, you will be redirected to the website of the OpenID provider, where you will authenticate and authorize some information you share with our application (we only need email and nickname, rest assured that no password or other personal information will be exposed ).
Once the logon is complete, you will be taken to the index page as the logged-on user.
Check the remember_me check box. With this option, you are logged on when you close the application in the browser and re-open the application.
Log out
We have enabled logon. Now it is time to log out.
The method for logging out is usually quite simple (file app/views. py ):
@app.route('/logout')def logout(): logout_user() return redirect(url_for('index'))
However, we do not have a logon Logout link in the template. We will add this link in the top navigation bar of base.html (file app/templates/base.html ):
How simple it is. We only need to check whether there is a valid user in g. user. If so, add the Logout link. In our template, we once again used the url_for method.
Last words
We now have a full-featured User Logon system. In the next chapter, we will create a user's profile page and display the user's profile picture.
During this period, here is the updated application code, including all the changes in this article:
Download the microblog-0.5.zip.