-
Notifications
You must be signed in to change notification settings - Fork 8
Commit v0.24
kwmccabe edited this page Apr 17, 2018
·
9 revisions
v0.24 - Flask-Login: update UserModel, add /login and /profile, decorate routes with @login_required
- +14 -0 [M] web/app/init.py
- +1 -1 [M] web/app/item/templates/item_create.html
- +2 -2 [M] web/app/item/templates/item_edit.html
- +2 -2 [M] web/app/item/templates/item_view.html
- +7 -0 [M] web/app/item/views.py
- +7 -0 [M] web/app/main/templates/base.html
- +2 -2 [M] web/app/main/templates/navbar_main.html
- +12 -0 [M] web/app/user/forms.py
- +21 -1 [M] web/app/user/models.py
- +1 -1 [M] web/app/user/templates/user_create.html
- +2 -2 [M] web/app/user/templates/user_edit.html
- +1 -1 [M] web/app/user/templates/user_index.html
- +55 -0 [A] web/app/user/templates/user_login.html
- +58 -0 [A] web/app/user/templates/user_profile.html
- +2 -2 [M] web/app/user/templates/user_view.html
- +42 -2 [M] web/app/user/views.py
- +4 -0 [M] web/requirements.txt
- Import and init
LoginManager
. - Create function
init_login_manager()
to collect configuration options.
+from flask_login import LoginManager
...
+login_manager = LoginManager()
...
+ init_login_manager(app)
...
+def init_login_manager(app):
+ login_manager.init_app(app)
+ login_manager.session_protection = 'strong'
+ login_manager.login_view = 'user.user_login'
+ login_manager.login_message = u"Please log in to access this page."
+ login_manager.login_message_category = "warning"
+ login_manager.refresh_view = 'user.user_login'
+ login_manager.needs_refresh_message = (u"Please confirm your credentials to access this page.")
+ login_manager.needs_refresh_message_category = "warning"
- <h3 class="condensed">Create Item</h3>
+ <h3 class="condensed">Item</h3>
<!-- BLOCK: title -->
-{% block title %}{{super()}} - Admin - Edit '{{ form.item.keyname }}'{% endblock %}
+{% block title %}{{super()}} - Admin - Edit Item '{{ form.item.keyname }}'{% endblock %}
...
- <h3 class="condensed">Edit Item</h3>
+ <h3 class="condensed">Item '{{ form.item.keyname }}'</h3>
<!-- BLOCK: title -->
-{% block title %}{{super()}} - Admin - View '{{ item.keyname }}'{% endblock %}
+{% block title %}{{super()}} - Admin - Item '{{ item.keyname }}'{% endblock %}
...
- <h3 class="condensed">View Item</h3>
+ <h3 class="condensed">Item '{{ item.keyname }}'</h3>
- Restrict access to admin routes with login decorator
@login_required
.
+from flask_login import login_required
...
@item.route('/admin/item/action', methods=['POST'])
+@login_required
def item_action():
...
@item.route('/admin/item/delete/<int:id>', methods=['GET','POST'])
+@login_required
def item_delete( id ):
...
@item.route('/admin/item/create', methods=['GET','POST'])
+@login_required
def item_create():
...
@item.route('/admin/item/edit/<int:id>', methods=['GET','POST'])
+@login_required
def item_edit( id ):
...
@item.route('/admin/item/view/<int:id>')
+@login_required
def item_view( id ):
...
@item.route('/admin/item/list')
@get_list_opts('item_list_opts')
+@login_required
def item_list():
- Display
current_user.user_email
and Profile link if logged in. - Display Login link if not logged in.
<div id="header_right" class="col-md-4 text-right">
{% block header_right %}{% endblock %}
+ {% if current_user.user_email %}
+ <a href="{{ url_for('user.user_profile') }}">{{ current_user.user_email }}</a>
+ {% else %}
+ <a href="{{ url_for('user.user_login') }}">Login</a>
+ {% endif %}
</div>
...
<div id="debuginfo"><pre>
DEBUG:
+{% if current_user.user_email %}LOGGED IN: {{ current_user.user_email }}{% else %}LOGGED OUT{% endif %}
TEMPLATES: {% block templates %}base.html{% endblock %}
REQUEST.ARGS: {{ request.args }}
+REQUEST.COOKIES: {{ request.cookies }}
SESSION: {{ session }}
</pre></div>
<div class="collapse navbar-collapse" id="navbar-main-collapse">
<ul class="nav navbar-nav">
<!-- li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li -->
- <li><a href="{{ url_for('item.item_page') }}">Items</a></li>
- <li><a href="{{ url_for('user.user_page') }}">Users</a></li>
+ <li><a href="{{ url_for('item.item_page', page='index') }}">Items</a></li>
+ <li><a href="{{ url_for('user.user_page', page='index') }}">Users</a></li>
<li class="dropdown">
- Add
LoginForm
for use by route/login
.
+class LoginForm(FlaskForm):
+ next = HiddenField('next')
+ user_email = StringField('Email', validators=[InputRequired(),Length(1,255),Email()])
+ password = PasswordField('Password', validators=[InputRequired()])
+ remember = BooleanField('Keep me logged in')
+ submit = SubmitField('Login')
+
+ def __init__(self, user, *args, **kwargs):
+ super(LoginForm, self).__init__(*args, **kwargs)
+ self.user = user
- Import
login_manager
. - Create function for
@login_manager.user_loader
. - Create class methods
is_authenticated()
,is_anonymous()
,is_active()
, andget_id()
.
-from .. import db
+from .. import db, login_manager
+#see https://flask-login.readthedocs.io/
+#@login_manager.request_loader
+#def load_user_from_request(request):
+
+@login_manager.user_loader
+def load_user(user_id):
+ logging.info( "load_user(%s)" % user_id)
+ return UserModel.query.filter_by(user_email=user_id).first()
class UserModel(db.Model):
...
+ def is_authenticated(self):
+ return self.authenticated
+
+ def is_anonymous(self):
+ return self.anonymous
+
+ def is_active(self):
+ return self.active
+
+ def get_id(self):
+ return self.user_email
+
def to_json(self):
- <h3 class="condensed">Create User</h3>
+ <h3 class="condensed">User</h3>
<!-- BLOCK: title -->
-{% block title %}{{super()}} - Admin - Edit '{{ form.user.keyname }}'{% endblock %}
+{% block title %}{{super()}} - Admin - Edit User '{{ form.user.keyname }}'{% endblock %}
...
- <h3 class="condensed">Edit User</h3>
+ <h3 class="condensed">User - '{{ form.user.keyname }}'</h3>
-<p>This is public landing page for the User Module.</p>
+<p>This is the public landing page for the User Module.</p>
+{% extends "base.html" %}
+
+
+<!-- BLOCK: title -->
+{% block title %}{{super()}} - Login{% endblock %}
+
+
+<!-- BLOCK: breadcrumb -->
+{% block breadcrumb %}
+{{super()}}
+<li><a href="{{ url_for('.user_login') }}">Login</a></li>
+{% endblock %}
+
+
+<!-- BLOCK: content -->
+{% block content %}
+<div id="item_edit_panel" class="panel panel-info">
+ <div class="panel-heading">
+<p style="float: right;">
+[ <a href="{{ url_for('main.main_home') }}">Cancel</a> ]
+</p>
+ <h3 class="condensed">User Login</h3>
+ </div>
+ <div class="panel-body">
+
+<form class="form-horizontal" action="{{ url_for('.user_login') }}" method="post">
+ {{ form.csrf_token }}
+ {{ form.next }}
+ <div class="form-group">
+ *{{ form.user_email.label }}
+ {{ form.user_email(class_='form-control',placeholder='[email protected]') }}
+ </div>
+ <div class="form-group">
+ *{{ form.password.label }}
+ {{ form.password(class_='form-control') }}
+ </div>
+ <div class="form-group">
+ {{ form.remember.label }}
+ {{ form.remember(class_='form-control') }}
+ </div>
+ {{ form.submit(class_='btn btn-primary') }}
+ <span class="form-submit-buttons">
+ <a class="btn btn-default" href="{{ url_for('main.main_home') }}">Cancel</a>
+ </span>
+</form>
+
+ </div> <!-- end class="panel-body" -->
+</div> <!-- end class="panel" -->
+{% endblock %}
+
+
+<!-- BLOCK: templates -->
+{% block templates %}{{super()}} - user_login.html{% endblock %}
+
+{# end web/app/user/templates/user_login.html #}
+{% extends "base.html" %}
+
+
+<!-- BLOCK: title -->
+{% block title %}{{super()}} - Profile{% endblock %}
+
+
+<!-- BLOCK: breadcrumb -->
+{% block breadcrumb %}
+{{super()}}
+<li><a href="{{ url_for('.user_profile') }}">User Profile</a></li>
+{% endblock %}
+
+
+<!-- BLOCK: content_left -->
+{% block content_left %}
+{{super()}} <b>user_view</b>
+{% endblock %}
+
+
+<!-- BLOCK: content -->
+{% block content %}
+<div id="item_list_panel" class="panel panel-info">
+ <div class="panel-heading">
+<p style="float: right;">
+{# [ <a href="{{ url_for('.user_edit', id=user.id) }}">Update Profile</a> ] #}
+[ <a href="{{ url_for('.user_logout') }}">Logout</a> ]
+</p>
+ <h3 class="condensed">Profile '{{ user.keyname }}'</h3>
+ </div>
+ <div class="panel-body">
+
+<div class="table-responsive">
+<table class="table table-condensed table-hover">
+<tr>
+ <th>column</th>
+ <th>value</th>
+</tr>
+
+{% for col in cols %}
+ <tr>
+ <td>{{ col }}</td>
+ <td>{{ user[col] }}</td>
+ </tr>
+{% endfor %}
+
+</table>
+</div>
+
+ </div> <!-- end class="panel-body" -->
+</div> <!-- end class="panel" -->
+{% endblock %}
+
+
+<!-- BLOCK: templates -->
+{% block templates %}{{super()}} - user_profile.html{% endblock %}
+
+{# end web/app/user/templates/user_profile.html #}
<!-- BLOCK: title -->
-{% block title %}{{super()}} - Admin - View '{{ user.keyname }}'{% endblock %}
+{% block title %}{{super()}} - Admin - User '{{ user.keyname }}'{% endblock %}
...
- <h3 class="condensed">View User</h3>
+ <h3 class="condensed">User '{{ user.keyname }}'</h3>
- Import
flask_login
functions. - Create routes
/login
,/logout
, and/profile
. - Restrict access to admin routes with login decorator
@login_required
.
-from flask import flash, redirect, render_template, request, session, url_for
+from flask import abort, flash, redirect, render_template, request, session, url_for
+from flask_login import current_user, login_required, login_user, logout_user
...
-from .forms import CreatUserForm, EditUserForm
+from .forms import CreatUserForm, EditUserForm, LoginForm
...
+@user.route('/login', methods=['GET','POST'])
+def user_login():
+ user = UserModel()
+ form = LoginForm(user)
+ if form.validate_on_submit():
+ user = UserModel.query.filter_by(user_email=form.user_email.data).first()
+ if user is not None and user.verify_password(form.password.data):
+ login_user(user, form.remember.data)
+ return redirect(form.next.data or url_for('main.main_home'))
+ flash('Invalid username or password')
+ else:
+ flash_errors(form)
+ form.next.data = request.args.get('next') or url_for('main.main_home')
+ return render_template('user_login.html', form=form)
+
+
+@user.route('/logout')
+#@login_required
+def user_logout():
+ logout_user()
+ flash('You have been logged out.')
+ return redirect(url_for('main.main_page', page='index'))
+
+
+@user.route('/profile')
+@login_required
+def user_profile():
+ cols = UserModel.__table__.columns.keys()
+ cols_filtered = list(filter(lambda x: x not in ['id','user_pass'], cols))
+ user = UserModel.query.get_or_404( current_user.id )
+ return render_template('user_profile.html', cols=cols_filtered, user=user)
...
@user.route('/admin/user/action', methods=['POST'])
+@login_required
def user_action():
...
@user.route('/admin/user/delete/<int:id>', methods=['GET','POST'])
+@login_required
def user_delete( id ):
...
@user.route('/admin/user/create', methods=['GET','POST'])
+@login_required
def user_create():
...
@user.route('/admin/user/edit/<int:id>', methods=['GET','POST'])
+@login_required
def user_edit( id ):
...
@user.route('/admin/user/view/<int:id>')
+@login_required
def user_view( id ):
...
@user.route('/admin/user/list', methods=['GET','POST'])
@get_list_opts('user_list_opts')
+@login_required
def user_list():
- Add
Flask-Login
package. - Provide explicit version numbers to some implicit packages.
Flask==0.12.2
Flask-Bootstrap==3.3.7.1
+Flask-Login==0.4.1
Flask-SQLAlchemy==2.2.0
Flask-WTF==0.14.2
gunicorn==19.7.1
Jinja2==2.9.6
MySQL-Connector-Python==8.0.6
+SQLAlchemy==1.2.5
+Werkzeug==0.14.1
+WTForms==2.1
Commit-v0.23 | Commit-v0.24 | Commit-v0.25
- FlaskApp Tutorial
- Table of Contents
- About
- Application Setup
- Modules, Templates, and Layouts
- Database Items, Forms, and CRUD
- List Filter, Sort, and Paginate
- Users and Login
- Database Relationships
- API Module, HTTPAuth and JSON
- Refactoring User Roles and Item Status
- AJAX and Public Pages