Merge base templates. Add database support. Add blueprint for main.

This commit is contained in:
Patrick Jentsch
2019-07-05 14:47:35 +02:00
parent f6b2dd3282
commit b6a67fcd4d
20 changed files with 414 additions and 73 deletions

View File

@ -1,5 +1,13 @@
from config import config
from flask import Flask, render_template
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
def create_app(config_name):
@ -7,11 +15,13 @@ def create_app(config_name):
app.config.from_object(config[config_name])
config[config_name].init_app(app)
@app.route('/')
def index():
return render_template('base.html.j2')
db.init_app(app)
login_manager.init_app(app)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app

View File

@ -0,0 +1,11 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length, Email
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Length(1, 64),
Email()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Keep me logged in')
submit = SubmitField('Log In')

View File

@ -1,10 +1,31 @@
from flask import render_template
from flask import flash, redirect, render_template, request, url_for
from flask_login import login_required, login_user, logout_user
from . import auth
from .forms import LoginForm
from ..models import User
@auth.route('/login', methods=['GET', 'POST'])
def login():
return render_template('auth/login.html.j2', title='Log in')
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user, form.remember_me.data)
next = request.args.get('next')
if next is None or not next.startswith('/'):
next = url_for('main.index')
return redirect(next)
flash('Invalid username or password.')
return render_template('auth/login.html.j2', form=form, title='Log in')
@auth.route('/logout')
@login_required
def logout():
logout_user()
flash('You have been logged out.')
return redirect(url_for('main.index'))
@auth.route('/register', methods=['GET', 'POST'])

View File

@ -0,0 +1,5 @@
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views

View File

@ -0,0 +1,7 @@
from flask import render_template
from . import main
@main.route('/')
def index():
return render_template('main/index.html.j2')

43
app/models.py Normal file
View File

@ -0,0 +1,43 @@
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from . import db
from . import login_manager
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
def __repr__(self):
return '<Role %r>' % self.name
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __repr__(self):
return '<User %r>' % self.username
password_hash = db.Column(db.String(128))
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))

View File

@ -16,6 +16,7 @@
</div>
</div>
</div>
<div class="col s12 m6">
<div class="card medium">
<div class="card-content">

View File

@ -14,7 +14,35 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
{% include 'header.html.j2' %}
<header>
<div id="nav-notifications-dropdown" class="dropdown-content">
<p>Notifications</p>
</div>
<div id="nav-settings-dropdown" class="dropdown-content">
<p>Settings</p>
</div>
<nav>
<div class="nav-wrapper">
<a href="#!" class="brand-logo">
{% if title %}{{ title }}{% else %}Opaque{% endif %}
</a>
<a href="#" data-target="slide-out" class="sidenav-trigger"><i class="material-icons">menu</i></a>
<ul class="right hide-on-med-and-down">
<li><a id="nav-notifications" class="dropdown-trigger" href="#!" data-target="nav-notifications-dropdown"><i class="material-icons">notifications</i></a></li>
<li><a id="nav-settings" class="dropdown-trigger" href="#!" data-target="nav-settings-dropdown"><i class="material-icons">settings</i></a></li>
</ul>
</div>
</nav>
<ul id="slide-out" class="sidenav sidenav-fixed">
<li><a href="{{ url_for('main.index') }}">Opaque</a></li>
{% if current_user.is_authenticated %}
<li><a href="{{ url_for('auth.logout') }}">Log out</a></li>
{% else %}
<li><a href="{{ url_for('auth.login') }}">Log in</a></li>
{% endif %}
</ul>
</header>
<main class="grey lighten-5">
<div class="container">
@ -25,7 +53,45 @@
</div>
</main>
{% include 'footer.html.j2' %}
<footer class="page-footer">
<div class="container">
<div class="row">
<div class="col s12 l3">
<img src="{{ url_for('static', filename='images/logo_sfb_1288.png') }}" class="responsive-img" style="max-height: 140px;">
</div>
<div class="col s12 l3">
<h5 class="white-text">About</h5>
<ul>
<li><a class="grey-text text-lighten-3" href="#!">Link 1</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 2</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 3</a></li>
</ul>
</div>
<div class="col s12 l3">
<h5 class="white-text">Connect</h5>
<ul>
<li><a class="grey-text text-lighten-3" href="#!">Link 1</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 2</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 3</a></li>
</ul>
</div>
<div class="col s12 l3">
<h5 class="white-text">Contact</h5>
<ul>
<li><a class="grey-text text-lighten-3" href="#!">Link 1</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 2</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 3</a></li>
</ul>
</div>
</div>
</div>
<div class="footer-copyright">
<div class="container">
© 2019 Bielefeld University
<a class="grey-text text-lighten-4 right" href="#!">Impress</a>
</div>
</div>
</footer>
<!--JavaScript at end of body for optimized loading-->
<script type="text/javascript" src="{{ url_for('static', filename='js/materialize.min.js') }}"></script>

View File

@ -1,39 +0,0 @@
<footer class="page-footer">
<div class="container">
<div class="row">
<div class="col s12 l3">
<img src="{{ url_for('static', filename='images/logo_sfb_1288.png') }}" class="responsive-img" style="max-height: 140px;">
</div>
<div class="col s12 l3">
<h5 class="white-text">About</h5>
<ul>
<li><a class="grey-text text-lighten-3" href="#!">Link 1</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 2</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 3</a></li>
</ul>
</div>
<div class="col s12 l3">
<h5 class="white-text">Connect</h5>
<ul>
<li><a class="grey-text text-lighten-3" href="#!">Link 1</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 2</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 3</a></li>
</ul>
</div>
<div class="col s12 l3">
<h5 class="white-text">Contact</h5>
<ul>
<li><a class="grey-text text-lighten-3" href="#!">Link 1</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 2</a></li>
<li><a class="grey-text text-lighten-3" href="#!">Link 3</a></li>
</ul>
</div>
</div>
</div>
<div class="footer-copyright">
<div class="container">
© 2019 Bielefeld University
<a class="grey-text text-lighten-4 right" href="#!">Impress</a>
</div>
</div>
</footer>

View File

@ -1,25 +0,0 @@
<header>
<div id="nav-notifications-dropdown" class="dropdown-content">
<p>Notifications</p>
</div>
<div id="nav-settings-dropdown" class="dropdown-content">
<p>Settings</p>
</div>
<nav>
<div class="nav-wrapper">
<a href="#!" class="brand-logo">
{% if title %}{{ title }}{% else %}Opaque{% endif %}
</a>
<a href="#" data-target="slide-out" class="sidenav-trigger"><i class="material-icons">menu</i></a>
<ul class="right hide-on-med-and-down">
<li><a id="nav-notifications" class="dropdown-trigger" href="#!" data-target="nav-notifications-dropdown"><i class="material-icons">notifications</i></a></li>
<li><a id="nav-settings" class="dropdown-trigger" href="#!" data-target="nav-settings-dropdown"><i class="material-icons">settings</i></a></li>
</ul>
</div>
</nav>
<ul id="slide-out" class="sidenav sidenav-fixed">
<li><a href="{{ url_for('index') }}">Opaque</a></li>
<li><a href="{{ url_for('auth.login') }}">Login</a></li>
</ul>
</header>

View File

@ -2,6 +2,19 @@
{% block page_content %}
<div class="col s12">
<div class="card">
<div class="card-content">
<span class="card-title">
Hello,
{% if current_user.is_authenticated %}
{{ current_user.username }}
{% else %}
Stranger
{% endif %}!
</span>
</div>
</div>
<h2>Services</h2>
<div class="row">