├── .gitignore ├── flask-crud-part-two.jpg ├── app ├── static │ ├── img │ │ ├── favicon.ico │ │ └── intro-bg.jpg │ └── css │ │ └── style.css ├── admin │ ├── __init__.py │ ├── forms.py │ └── views.py ├── auth │ ├── __init__.py │ ├── forms.py │ └── views.py ├── home │ ├── __init__.py │ └── views.py ├── templates │ ├── auth │ │ ├── register.html │ │ └── login.html │ ├── home │ │ ├── dashboard.html │ │ ├── index.html │ │ └── admin_dashboard.html │ ├── admin │ │ ├── roles │ │ │ ├── role.html │ │ │ └── roles.html │ │ ├── departments │ │ │ ├── department.html │ │ │ └── departments.html │ │ └── employees │ │ │ ├── employee.html │ │ │ └── employees.html │ └── base.html ├── __init__.py └── models.py ├── run.py ├── README.md ├── requirements.txt └── config.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | instance/ 4 | migrations/ 5 | -------------------------------------------------------------------------------- /flask-crud-part-two.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbithenzomo/project-dream-team-two/HEAD/flask-crud-part-two.jpg -------------------------------------------------------------------------------- /app/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbithenzomo/project-dream-team-two/HEAD/app/static/img/favicon.ico -------------------------------------------------------------------------------- /app/admin/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | admin = Blueprint('admin', __name__) 4 | 5 | from . import views 6 | -------------------------------------------------------------------------------- /app/auth/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | auth = Blueprint('auth', __name__) 4 | 5 | from . import views 6 | -------------------------------------------------------------------------------- /app/home/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | home = Blueprint('home', __name__) 4 | 5 | from . import views 6 | -------------------------------------------------------------------------------- /app/static/img/intro-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbithenzomo/project-dream-team-two/HEAD/app/static/img/intro-bg.jpg -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from app import create_app 4 | 5 | config_name = os.getenv('FLASK_CONFIG') 6 | app = create_app(config_name) 7 | 8 | if __name__ == '__main__': 9 | app.run() 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![featured-image](https://raw.githubusercontent.com/andela-mnzomo/project-dream-team-two/master/flask-crud-part-two.jpg) 2 | 3 | The code for Part Two of my three-part tutorial, *Build a CRUD Web App With Python and Flask*. 4 | -------------------------------------------------------------------------------- /app/templates/auth/register.html: -------------------------------------------------------------------------------- 1 | {% import "bootstrap/utils.html" as utils %} 2 | {% import "bootstrap/wtf.html" as wtf %} 3 | {% extends "base.html" %} 4 | {% block title %}Register{% endblock %} 5 | {% block body %} 6 |
7 |
8 |

Register for an account

9 |
10 | {{ wtf.quick_form(form) }} 11 |
12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alembic==0.8.9 2 | click==6.6 3 | dominate==2.3.1 4 | Flask==0.11.1 5 | Flask-Bootstrap==3.3.7.0 6 | Flask-Login==0.4.0 7 | Flask-Migrate==2.0.1 8 | Flask-Script==2.0.5 9 | Flask-SQLAlchemy==2.1 10 | Flask-WTF==0.13.1 11 | itsdangerous==0.24 12 | Jinja2==2.8 13 | Mako==1.0.6 14 | MarkupSafe==0.23 15 | MySQL-python==1.2.5 16 | python-editor==1.0.3 17 | SQLAlchemy==1.1.4 18 | visitor==0.1.3 19 | Werkzeug==0.11.11 20 | WTForms==2.1 21 | -------------------------------------------------------------------------------- /app/templates/auth/login.html: -------------------------------------------------------------------------------- 1 | {% import "bootstrap/utils.html" as utils %} 2 | {% import "bootstrap/wtf.html" as wtf %} 3 | {% extends "base.html" %} 4 | {% block title %}Login{% endblock %} 5 | {% block body %} 6 |
7 |
8 | {{ utils.flashed_messages() }} 9 |
10 |
11 |

Login to your account

12 |
13 | {{ wtf.quick_form(form) }} 14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | """ 3 | Common configurations 4 | """ 5 | 6 | # Put any configurations here that are common across all environments 7 | 8 | 9 | class DevelopmentConfig(Config): 10 | """ 11 | Development configurations 12 | """ 13 | 14 | DEBUG = True 15 | SQLALCHEMY_ECHO = True 16 | 17 | 18 | class ProductionConfig(Config): 19 | """ 20 | Production configurations 21 | """ 22 | 23 | DEBUG = False 24 | 25 | app_config = { 26 | 'development': DevelopmentConfig, 27 | 'production': ProductionConfig 28 | } 29 | -------------------------------------------------------------------------------- /app/templates/home/dashboard.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Dashboard{% endblock %} 3 | {% block body %} 4 |
5 |
6 |
7 |
8 |
9 |

The Dashboard

10 |

We made it here!

11 |
12 | 13 |
14 |
15 |
16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /app/templates/home/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Home{% endblock %} 3 | {% block body %} 4 |
5 |
6 |
7 |
8 |
9 |

Project Dream Team

10 |

The best company in the world!

11 |
12 | 13 |
14 |
15 |
16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /app/templates/home/admin_dashboard.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Admin Dashboard{% endblock %} 3 | {% block body %} 4 |
5 |
6 |
7 |
8 |
9 |

Admin Dashboard

10 |

For administrators only!

11 |
12 | 13 |
14 |
15 |
16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /app/templates/admin/roles/role.html: -------------------------------------------------------------------------------- 1 | {% import "bootstrap/wtf.html" as wtf %} 2 | {% extends "base.html" %} 3 | {% block title %} 4 | {% if add_department %} 5 | Add Role 6 | {% else %} 7 | Edit Role 8 | {% endif %} 9 | {% endblock %} 10 | {% block body %} 11 |
12 |
13 |
14 |
15 |
16 | {% if add_role %} 17 |

Add Role

18 | {% else %} 19 |

Edit Role

20 | {% endif %} 21 |
22 | {{ wtf.quick_form(form) }} 23 |
24 |
25 |
26 |
27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /app/templates/admin/departments/department.html: -------------------------------------------------------------------------------- 1 | {% import "bootstrap/wtf.html" as wtf %} 2 | {% extends "base.html" %} 3 | {% block title %} 4 | {% if add_department %} 5 | Add Department 6 | {% else %} 7 | Edit Department 8 | {% endif %} 9 | {% endblock %} 10 | {% block body %} 11 |
12 |
13 |
14 |
15 |
16 | {% if add_department %} 17 |

Add Department

18 | {% else %} 19 |

Edit Department

20 | {% endif %} 21 |
22 | {{ wtf.quick_form(form) }} 23 |
24 |
25 |
26 |
27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /app/templates/admin/employees/employee.html: -------------------------------------------------------------------------------- 1 | {% import "bootstrap/wtf.html" as wtf %} 2 | {% extends "base.html" %} 3 | {% block title %}Assign Employee{% endblock %} 4 | {% block body %} 5 |
6 |
7 |
8 |
9 |
10 |

Assign Departments and Roles

11 |
12 |

13 | Select a department and role to assign to 14 | 15 | {{ employee.first_name }} {{ employee.last_name }} 16 | 17 |

18 |
19 | {{ wtf.quick_form(form) }} 20 |
21 |
22 |
23 |
24 |
25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /app/home/views.py: -------------------------------------------------------------------------------- 1 | from flask import abort, render_template 2 | from flask_login import current_user, login_required 3 | 4 | from . import home 5 | 6 | 7 | @home.route('/') 8 | def homepage(): 9 | """ 10 | Render the homepage template on the / route 11 | """ 12 | return render_template('home/index.html', title="Welcome") 13 | 14 | 15 | @home.route('/dashboard') 16 | @login_required 17 | def dashboard(): 18 | """ 19 | Render the dashboard template on the /dashboard route 20 | """ 21 | return render_template('home/dashboard.html', title="Dashboard") 22 | 23 | 24 | @home.route('/admin/dashboard') 25 | @login_required 26 | def admin_dashboard(): 27 | # prevent non-admins from accessing the page 28 | if not current_user.is_admin: 29 | abort(403) 30 | 31 | return render_template('home/admin_dashboard.html', title="Dashboard") 32 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | # third-party imports 2 | from flask import Flask 3 | from flask_bootstrap import Bootstrap 4 | from flask_login import LoginManager 5 | from flask_migrate import Migrate 6 | from flask_sqlalchemy import SQLAlchemy 7 | 8 | # local imports 9 | from config import app_config 10 | 11 | db = SQLAlchemy() 12 | login_manager = LoginManager() 13 | 14 | 15 | def create_app(config_name): 16 | app = Flask(__name__, instance_relative_config=True) 17 | app.config.from_object(app_config[config_name]) 18 | app.config.from_pyfile('config.py') 19 | 20 | Bootstrap(app) 21 | db.init_app(app) 22 | login_manager.init_app(app) 23 | login_manager.login_message = "You must be logged in to access this page." 24 | login_manager.login_view = "auth.login" 25 | migrate = Migrate(app, db) 26 | 27 | from app import models 28 | 29 | from .admin import admin as admin_blueprint 30 | app.register_blueprint(admin_blueprint, url_prefix='/admin') 31 | 32 | from .auth import auth as auth_blueprint 33 | app.register_blueprint(auth_blueprint) 34 | 35 | from .home import home as home_blueprint 36 | app.register_blueprint(home_blueprint) 37 | 38 | return app 39 | -------------------------------------------------------------------------------- /app/admin/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField, SubmitField 3 | from wtforms.ext.sqlalchemy.fields import QuerySelectField 4 | from wtforms.validators import DataRequired 5 | 6 | from ..models import Department, Role 7 | 8 | 9 | class DepartmentForm(FlaskForm): 10 | """ 11 | Form for admin to add or edit a department 12 | """ 13 | name = StringField('Name', validators=[DataRequired()]) 14 | description = StringField('Description', validators=[DataRequired()]) 15 | submit = SubmitField('Submit') 16 | 17 | 18 | class RoleForm(FlaskForm): 19 | """ 20 | Form for admin to add or edit a role 21 | """ 22 | name = StringField('Name', validators=[DataRequired()]) 23 | description = StringField('Description', validators=[DataRequired()]) 24 | submit = SubmitField('Submit') 25 | 26 | 27 | class EmployeeAssignForm(FlaskForm): 28 | """ 29 | Form for admin to assign departments and roles to employees 30 | """ 31 | department = QuerySelectField(query_factory=lambda: Department.query.all(), 32 | get_label="name") 33 | role = QuerySelectField(query_factory=lambda: Role.query.all(), 34 | get_label="name") 35 | submit = SubmitField('Submit') 36 | -------------------------------------------------------------------------------- /app/auth/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import PasswordField, StringField, SubmitField, ValidationError 3 | from wtforms.validators import DataRequired, Email, EqualTo 4 | 5 | from ..models import Employee 6 | 7 | 8 | class RegistrationForm(FlaskForm): 9 | """ 10 | Form for users to create new account 11 | """ 12 | email = StringField('Email', validators=[DataRequired(), Email()]) 13 | username = StringField('Username', validators=[DataRequired()]) 14 | first_name = StringField('First Name', validators=[DataRequired()]) 15 | last_name = StringField('Last Name', validators=[DataRequired()]) 16 | password = PasswordField('Password', validators=[ 17 | DataRequired(), 18 | EqualTo('confirm_password') 19 | ]) 20 | confirm_password = PasswordField('Confirm Password') 21 | submit = SubmitField('Register') 22 | 23 | def validate_email(self, field): 24 | if Employee.query.filter_by(email=field.data).first(): 25 | raise ValidationError('Email is already in use.') 26 | 27 | def validate_username(self, field): 28 | if Employee.query.filter_by(username=field.data).first(): 29 | raise ValidationError('Username is already in use.') 30 | 31 | 32 | class LoginForm(FlaskForm): 33 | """ 34 | Form for users to login 35 | """ 36 | email = StringField('Email', validators=[DataRequired(), Email()]) 37 | password = PasswordField('Password', validators=[DataRequired()]) 38 | submit = SubmitField('Login') 39 | -------------------------------------------------------------------------------- /app/auth/views.py: -------------------------------------------------------------------------------- 1 | from flask import flash, redirect, render_template, url_for 2 | from flask_login import login_required, login_user, logout_user 3 | 4 | from . import auth 5 | from forms import LoginForm, RegistrationForm 6 | from .. import db 7 | from ..models import Employee 8 | 9 | 10 | @auth.route('/register', methods=['GET', 'POST']) 11 | def register(): 12 | form = RegistrationForm() 13 | if form.validate_on_submit(): 14 | employee = Employee(email=form.email.data, 15 | username=form.username.data, 16 | first_name=form.first_name.data, 17 | last_name=form.last_name.data, 18 | password=form.password.data) 19 | 20 | # add employee to the database 21 | db.session.add(employee) 22 | db.session.commit() 23 | flash('You have successfully registered! You may now login.') 24 | 25 | # redirect to the login page 26 | return redirect(url_for('auth.login')) 27 | 28 | # load registration template 29 | return render_template('auth/register.html', form=form, title='Register') 30 | 31 | 32 | @auth.route('/login', methods=['GET', 'POST']) 33 | def login(): 34 | form = LoginForm() 35 | if form.validate_on_submit(): 36 | 37 | # check whether employee exists in the database and whether 38 | # the password entered matches the password in the database 39 | employee = Employee.query.filter_by(email=form.email.data).first() 40 | if employee is not None and employee.verify_password( 41 | form.password.data): 42 | # log employee in 43 | login_user(employee) 44 | 45 | # redirect to the appropriate dashboard page 46 | if employee.is_admin: 47 | return redirect(url_for('home.admin_dashboard')) 48 | else: 49 | return redirect(url_for('home.dashboard')) 50 | 51 | # when login details are incorrect 52 | else: 53 | flash('Invalid email or password.') 54 | 55 | # load login template 56 | return render_template('auth/login.html', form=form, title='Login') 57 | 58 | 59 | @auth.route('/logout') 60 | @login_required 61 | def logout(): 62 | logout_user() 63 | flash('You have successfully been logged out.') 64 | 65 | # redirect to the login page 66 | return redirect(url_for('auth.login')) 67 | -------------------------------------------------------------------------------- /app/templates/admin/roles/roles.html: -------------------------------------------------------------------------------- 1 | {% import "bootstrap/utils.html" as utils %} 2 | {% extends "base.html" %} 3 | {% block title %}Roles{% endblock %} 4 | {% block body %} 5 |
6 |
7 |
8 |
9 |
10 | {{ utils.flashed_messages() }} 11 |
12 |

Roles

13 | {% if roles %} 14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% for role in roles %} 28 | 29 | 30 | 31 | 38 | 43 | 48 | 49 | {% endfor %} 50 | 51 |
Name Description Employee Count Edit Delete
{{ role.name }} {{ role.description }} 32 | {% if role.employees %} 33 | {{ role.employees.count() }} 34 | {% else %} 35 | 0 36 | {% endif %} 37 | 39 | 40 | Edit 41 | 42 | 44 | 45 | Delete 46 | 47 |
52 |
53 |
54 | {% else %} 55 |
56 |

No roles have been added.

57 |
58 | {% endif %} 59 | 60 | 61 | Add Role 62 | 63 |
64 |
65 |
66 |
67 |
68 | {% endblock %} 69 | -------------------------------------------------------------------------------- /app/templates/admin/departments/departments.html: -------------------------------------------------------------------------------- 1 | {% import "bootstrap/utils.html" as utils %} 2 | {% extends "base.html" %} 3 | {% block title %}Departments{% endblock %} 4 | {% block body %} 5 |
6 |
7 |
8 |
9 |
10 | {{ utils.flashed_messages() }} 11 |
12 |

Departments

13 | {% if departments %} 14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% for department in departments %} 28 | 29 | 30 | 31 | 38 | 43 | 48 | 49 | {% endfor %} 50 | 51 |
Name Description Employee Count Edit Delete
{{ department.name }} {{ department.description }} 32 | {% if department.employees %} 33 | {{ department.employees.count() }} 34 | {% else %} 35 | 0 36 | {% endif %} 37 | 39 | 40 | Edit 41 | 42 | 44 | 45 | Delete 46 | 47 |
52 |
53 |
54 | {% else %} 55 |
56 |

No departments have been added.

57 |
58 | {% endif %} 59 | 60 | 61 | Add Department 62 | 63 |
64 |
65 |
66 |
67 |
68 | {% endblock %} 69 | -------------------------------------------------------------------------------- /app/static/css/style.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | body, h1, h2, h3 { 7 | font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; 8 | font-weight: 700; 9 | } 10 | 11 | a, .navbar-default .navbar-brand, .navbar-default .navbar-nav>li>a { 12 | color: #aec251; 13 | } 14 | 15 | a:hover, .navbar-default .navbar-brand:hover, .navbar-default .navbar-nav>li>a:hover { 16 | color: #687430; 17 | } 18 | 19 | footer { 20 | padding: 50px 0; 21 | background-color: #f8f8f8; 22 | } 23 | 24 | p.copyright { 25 | margin: 15px 0 0; 26 | } 27 | 28 | .alert-info { 29 | width: 50%; 30 | margin: auto; 31 | color: #687430; 32 | background-color: #e6ecca; 33 | border-color: #aec251; 34 | } 35 | 36 | .btn-default { 37 | border-color: #aec251; 38 | color: #aec251; 39 | } 40 | 41 | .btn-default:hover { 42 | background-color: #aec251; 43 | } 44 | 45 | .center { 46 | margin: auto; 47 | width: 50%; 48 | padding: 10px; 49 | } 50 | 51 | .content-section { 52 | padding: 50px 0; 53 | border-top: 1px solid #e7e7e7; 54 | } 55 | 56 | .footer, .push { 57 | clear: both; 58 | height: 4em; 59 | } 60 | 61 | .intro-divider { 62 | width: 400px; 63 | border-top: 1px solid #f8f8f8; 64 | border-bottom: 1px solid rgba(0,0,0,0.2); 65 | } 66 | 67 | .intro-header { 68 | padding-top: 50px; 69 | padding-bottom: 50px; 70 | text-align: center; 71 | color: #f8f8f8; 72 | background: url(../img/intro-bg.jpg) no-repeat center center; 73 | background-size: cover; 74 | height: 100%; 75 | } 76 | 77 | .intro-message { 78 | position: relative; 79 | padding-top: 20%; 80 | padding-bottom: 20%; 81 | } 82 | 83 | .intro-message > h1 { 84 | margin: 0; 85 | text-shadow: 2px 2px 3px rgba(0,0,0,0.6); 86 | font-size: 5em; 87 | } 88 | 89 | .intro-message > h3 { 90 | text-shadow: 2px 2px 3px rgba(0,0,0,0.6); 91 | } 92 | 93 | .lead { 94 | font-size: 18px; 95 | font-weight: 400; 96 | } 97 | 98 | .topnav { 99 | font-size: 14px; 100 | } 101 | 102 | .wrapper { 103 | min-height: 100%; 104 | height: auto !important; 105 | height: 100%; 106 | margin: 0 auto -4em; 107 | } 108 | 109 | .outer { 110 | display: table; 111 | position: absolute; 112 | height: 70%; 113 | width: 100%; 114 | } 115 | 116 | .middle { 117 | display: table-cell; 118 | vertical-align: middle; 119 | } 120 | 121 | .inner { 122 | margin-left: auto; 123 | margin-right: auto; 124 | } 125 | -------------------------------------------------------------------------------- /app/templates/admin/employees/employees.html: -------------------------------------------------------------------------------- 1 | {% import "bootstrap/utils.html" as utils %} 2 | {% extends "base.html" %} 3 | {% block title %}Employees{% endblock %} 4 | {% block body %} 5 |
6 |
7 |
8 |
9 |
10 | {{ utils.flashed_messages() }} 11 |
12 |

Employees

13 | {% if employees %} 14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% for employee in employees %} 27 | {% if employee.is_admin %} 28 | 29 | 30 | 31 | 32 | 33 | 34 | {% else %} 35 | 36 | 37 | 44 | 51 | 56 | 57 | {% endif %} 58 | {% endfor %} 59 | 60 |
Name Department Role Assign
Admin N/A N/A N/A
{{ employee.first_name }} {{ employee.last_name }} 38 | {% if employee.department %} 39 | {{ employee.department.name }} 40 | {% else %} 41 | - 42 | {% endif %} 43 | 45 | {% if employee.role %} 46 | {{ employee.role.name }} 47 | {% else %} 48 | - 49 | {% endif %} 50 | 52 | 53 | Assign 54 | 55 |
61 |
62 | {% endif %} 63 |
64 |
65 |
66 |
67 |
68 | {% endblock %} 69 | -------------------------------------------------------------------------------- /app/models.py: -------------------------------------------------------------------------------- 1 | from flask_login import UserMixin 2 | from werkzeug.security import generate_password_hash, check_password_hash 3 | 4 | from app import db, login_manager 5 | 6 | 7 | class Employee(UserMixin, db.Model): 8 | """ 9 | Create an Employee table 10 | """ 11 | 12 | # Ensures table will be named in plural and not in singular 13 | # as is the name of the model 14 | __tablename__ = 'employees' 15 | 16 | id = db.Column(db.Integer, primary_key=True) 17 | email = db.Column(db.String(60), index=True, unique=True) 18 | username = db.Column(db.String(60), index=True, unique=True) 19 | first_name = db.Column(db.String(60), index=True) 20 | last_name = db.Column(db.String(60), index=True) 21 | password_hash = db.Column(db.String(128)) 22 | department_id = db.Column(db.Integer, db.ForeignKey('departments.id')) 23 | role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) 24 | is_admin = db.Column(db.Boolean, default=False) 25 | 26 | @property 27 | def password(self): 28 | """ 29 | Prevent pasword from being accessed 30 | """ 31 | raise AttributeError('password is not a readable attribute.') 32 | 33 | @password.setter 34 | def password(self, password): 35 | """ 36 | Set password to a hashed password 37 | """ 38 | self.password_hash = generate_password_hash(password) 39 | 40 | def verify_password(self, password): 41 | """ 42 | Check if hashed password matches actual password 43 | """ 44 | return check_password_hash(self.password_hash, password) 45 | 46 | def __repr__(self): 47 | return ''.format(self.username) 48 | 49 | 50 | # Set up user_loader 51 | @login_manager.user_loader 52 | def load_user(user_id): 53 | return Employee.query.get(int(user_id)) 54 | 55 | 56 | class Department(db.Model): 57 | """ 58 | Create a Department table 59 | """ 60 | 61 | __tablename__ = 'departments' 62 | 63 | id = db.Column(db.Integer, primary_key=True) 64 | name = db.Column(db.String(60), unique=True) 65 | description = db.Column(db.String(200)) 66 | employees = db.relationship('Employee', backref='department', 67 | lazy='dynamic') 68 | 69 | def __repr__(self): 70 | return ''.format(self.name) 71 | 72 | 73 | class Role(db.Model): 74 | """ 75 | Create a Role table 76 | """ 77 | 78 | __tablename__ = 'roles' 79 | 80 | id = db.Column(db.Integer, primary_key=True) 81 | name = db.Column(db.String(60), unique=True) 82 | description = db.Column(db.String(200)) 83 | employees = db.relationship('Employee', backref='role', 84 | lazy='dynamic') 85 | 86 | def __repr__(self): 87 | return ''.format(self.name) 88 | -------------------------------------------------------------------------------- /app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ title }} | Project Dream Team 5 | 6 | 7 | 8 | 9 | 10 | 11 | 44 |
45 | {% block body %} 46 | {% endblock %} 47 |
48 |
49 |
50 |
51 |
52 |
53 |
    54 |
  • Home
  • 55 | 56 | {% if current_user.is_authenticated %} 57 |
  • Logout
  • 58 | {% else %} 59 |
  • Register
  • 60 | 61 |
  • Login
  • 62 | {% endif %} 63 |
64 | 65 |
66 |
67 |
68 |
69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /app/admin/views.py: -------------------------------------------------------------------------------- 1 | from flask import abort, flash, redirect, render_template, url_for 2 | from flask_login import current_user, login_required 3 | 4 | from . import admin 5 | from forms import DepartmentForm, EmployeeAssignForm, RoleForm 6 | from .. import db 7 | from ..models import Department, Employee, Role 8 | 9 | 10 | def check_admin(): 11 | # prevent non-admins from accessing the page 12 | if not current_user.is_admin: 13 | abort(403) 14 | 15 | 16 | # Department Views 17 | 18 | 19 | @admin.route('/departments', methods=['GET', 'POST']) 20 | @login_required 21 | def list_departments(): 22 | """ 23 | List all departments 24 | """ 25 | check_admin() 26 | 27 | departments = Department.query.all() 28 | 29 | return render_template('admin/departments/departments.html', 30 | departments=departments, title="Departments") 31 | 32 | 33 | @admin.route('/departments/add', methods=['GET', 'POST']) 34 | @login_required 35 | def add_department(): 36 | """ 37 | Add a department to the database 38 | """ 39 | check_admin() 40 | 41 | add_department = True 42 | 43 | form = DepartmentForm() 44 | if form.validate_on_submit(): 45 | department = Department(name=form.name.data, 46 | description=form.description.data) 47 | try: 48 | # add department to the database 49 | db.session.add(department) 50 | db.session.commit() 51 | flash('You have successfully added a new department.') 52 | except: 53 | # in case department name already exists 54 | flash('Error: department name already exists.') 55 | 56 | # redirect to departments page 57 | return redirect(url_for('admin.list_departments')) 58 | 59 | # load department template 60 | return render_template('admin/departments/department.html', action="Add", 61 | add_department=add_department, form=form, 62 | title="Add Department") 63 | 64 | 65 | @admin.route('/departments/edit/', methods=['GET', 'POST']) 66 | @login_required 67 | def edit_department(id): 68 | """ 69 | Edit a department 70 | """ 71 | check_admin() 72 | 73 | add_department = False 74 | 75 | department = Department.query.get_or_404(id) 76 | form = DepartmentForm(obj=department) 77 | if form.validate_on_submit(): 78 | department.name = form.name.data 79 | department.description = form.description.data 80 | db.session.commit() 81 | flash('You have successfully edited the department.') 82 | 83 | # redirect to the departments page 84 | return redirect(url_for('admin.list_departments')) 85 | 86 | form.description.data = department.description 87 | form.name.data = department.name 88 | return render_template('admin/departments/department.html', action="Edit", 89 | add_department=add_department, form=form, 90 | department=department, title="Edit Department") 91 | 92 | 93 | @admin.route('/departments/delete/', methods=['GET', 'POST']) 94 | @login_required 95 | def delete_department(id): 96 | """ 97 | Delete a department from the database 98 | """ 99 | check_admin() 100 | 101 | department = Department.query.get_or_404(id) 102 | db.session.delete(department) 103 | db.session.commit() 104 | flash('You have successfully deleted the department.') 105 | 106 | # redirect to the departments page 107 | return redirect(url_for('admin.list_departments')) 108 | 109 | return render_template(title="Delete Department") 110 | 111 | 112 | # Role Views 113 | 114 | 115 | @admin.route('/roles') 116 | @login_required 117 | def list_roles(): 118 | check_admin() 119 | """ 120 | List all roles 121 | """ 122 | roles = Role.query.all() 123 | return render_template('admin/roles/roles.html', 124 | roles=roles, title='Roles') 125 | 126 | 127 | @admin.route('/roles/add', methods=['GET', 'POST']) 128 | @login_required 129 | def add_role(): 130 | """ 131 | Add a role to the database 132 | """ 133 | check_admin() 134 | 135 | add_role = True 136 | 137 | form = RoleForm() 138 | if form.validate_on_submit(): 139 | role = Role(name=form.name.data, 140 | description=form.description.data) 141 | 142 | try: 143 | # add role to the database 144 | db.session.add(role) 145 | db.session.commit() 146 | flash('You have successfully added a new role.') 147 | except: 148 | # in case role name already exists 149 | flash('Error: role name already exists.') 150 | 151 | # redirect to the roles page 152 | return redirect(url_for('admin.list_roles')) 153 | 154 | # load role template 155 | return render_template('admin/roles/role.html', add_role=add_role, 156 | form=form, title='Add Role') 157 | 158 | 159 | @admin.route('/roles/edit/', methods=['GET', 'POST']) 160 | @login_required 161 | def edit_role(id): 162 | """ 163 | Edit a role 164 | """ 165 | check_admin() 166 | 167 | add_role = False 168 | 169 | role = Role.query.get_or_404(id) 170 | form = RoleForm(obj=role) 171 | if form.validate_on_submit(): 172 | role.name = form.name.data 173 | role.description = form.description.data 174 | db.session.add(role) 175 | db.session.commit() 176 | flash('You have successfully edited the role.') 177 | 178 | # redirect to the roles page 179 | return redirect(url_for('admin.list_roles')) 180 | 181 | form.description.data = role.description 182 | form.name.data = role.name 183 | return render_template('admin/roles/role.html', add_role=add_role, 184 | form=form, title="Edit Role") 185 | 186 | 187 | @admin.route('/roles/delete/', methods=['GET', 'POST']) 188 | @login_required 189 | def delete_role(id): 190 | """ 191 | Delete a role from the database 192 | """ 193 | check_admin() 194 | 195 | role = Role.query.get_or_404(id) 196 | db.session.delete(role) 197 | db.session.commit() 198 | flash('You have successfully deleted the role.') 199 | 200 | # redirect to the roles page 201 | return redirect(url_for('admin.list_roles')) 202 | 203 | return render_template(title="Delete Role") 204 | 205 | 206 | # Employee Views 207 | 208 | @admin.route('/employees') 209 | @login_required 210 | def list_employees(): 211 | """ 212 | List all employees 213 | """ 214 | check_admin() 215 | 216 | employees = Employee.query.all() 217 | return render_template('admin/employees/employees.html', 218 | employees=employees, title='Employees') 219 | 220 | 221 | @admin.route('/employees/assign/', methods=['GET', 'POST']) 222 | @login_required 223 | def assign_employee(id): 224 | """ 225 | Assign a department and a role to an employee 226 | """ 227 | check_admin() 228 | 229 | employee = Employee.query.get_or_404(id) 230 | 231 | # prevent admin from being assigned a department or role 232 | if employee.is_admin: 233 | abort(403) 234 | 235 | form = EmployeeAssignForm(obj=employee) 236 | if form.validate_on_submit(): 237 | employee.department = form.department.data 238 | employee.role = form.role.data 239 | db.session.add(employee) 240 | db.session.commit() 241 | flash('You have successfully assigned a department and role.') 242 | 243 | # redirect to the roles page 244 | return redirect(url_for('admin.list_employees')) 245 | 246 | return render_template('admin/employees/employee.html', 247 | employee=employee, form=form, 248 | title='Assign Employee') 249 | --------------------------------------------------------------------------------