+4

Xây dựng ứng dụng web CRUD với Python và Flask - Phần hai

Chào mọi người. Tiếp theo phần một: xây dựng ứng dụng web CRUD với Python và Flask. Đây là phần hai của hướng dẫn để xây dựng một ứng dụng web quản lý nhân viên. Trong phần một trước, mình đã thiết lập cơ sở dữ liệu MySQL bằng MySQL-PythonFlask-SQLAlchemy. Mình tạo ra models, migrated database, và làm việc trên folder homeauth của blueprints và các templates. Đến cuối phần một, mình đã có một ứng dụng có trang chủ, đăng ký, đăng nhập và dashboard. Mình đã có thể đăng ký một người dùng mới, đăng nhập và đăng xuất.

Trong phần hai này, mình sẽ làm việc về:

  • Create user admin và trang dashboard của admin
  • Create, list, edit và delete các phòng ban
  • Create, list, edit và delete các vai trò
  • Phân công các phòng ban và vai trò cho nhân viên

1. Create Admin User

Mình sẽ bắt đầu bằng cách tạo người dùng quản trị thông qua dòng lệnh. Flask có cung cấp một lệnh rất tiện dụng, flask shell cho phép chúng ta sử dụng một interactive Python shell để sử dụng với các ứng dụng Flask.

$ flask shell
>>> from app.models import Employee
>>> from app import db
>>> admin = Employee(email="admin@admin.com",username="admin",password="admin2021",is_admin=True)
>>> db.session.add(admin)
>>> db.session.commit()

Mình vừa tạo một user admin và mật khẩu admin2021 và trường is_admin = true vì hiện tại database đang để mặc định trường này là false.

2. Dashboard dành cho admin

Bây giờ mình đã có user admin, mình cần thêm chế độ xem cho trang tổng quan quản trị. Mình cần đảm bảo rằng sau khi user admin login, họ sẽ được chuyển hướng đến dashboard dành cho admin chứ không phải trang dashboard dành cho user thường. Mình sẽ làm trong folder home.

  • File app/home/views.py
# update imports
from flask import abort, render_template
from flask_login import current_user, login_required

# add admin dashboard view

@home.route('/admin/dashboard')
@login_required
def admin_dashboard():
    # prevent non-admins from accessing the page
    if not current_user.is_admin:
        abort(403)

    return render_template('home/admin_dashboard.html', title="Dashboard")
  • File app/auth/views.py
# Edit the login view to redirect to the admin dashboard if employee is an admin

@auth.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():

        # check whether employee exists in the database and whether
        # the password entered matches the password in the database
        employee = Employee.query.filter_by(email=form.email.data).first()
        if employee is not None and employee.verify_password(
                form.password.data):
            # log employee in
            login_user(employee)

            # redirect to the appropriate dashboard page
            if employee.is_admin:
                return redirect(url_for('home.admin_dashboard'))
            else:
                return redirect(url_for('home.dashboard'))

        # when login details are incorrect
        else:
            flash('Invalid email or password.')

    # load login template
    return render_template('auth/login.html', form=form, title='Login')

Tiếp theo, mình sẽ tạo template dashboard dành cho admin. Tạo một file admin_dashboard.html trong thư mục templates/home, sau đó thêm code sau:


<!-- app/templates/home/admin_dashboard.html -->

{% extends "base.html" %}
{% block title %}Admin Dashboard{% endblock %}
{% block body %}
<div class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-12">
                <div class="intro-message">
                    <h1>Admin Dashboard</h1>
                    <h3>For administrators only!</h3>
                    <hr class="intro-divider">
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

Bây giờ mình cần chỉnh sửa template base để hiển thị một menu khác cho user admin.


<!-- app/templates/base.html -->

<!-- Modify nav bar menu -->
<ul class="nav navbar-nav navbar-right">
    {% if current_user.is_authenticated %}
      {% if current_user.is_admin %}
        <li><a href="{{ url_for('home.admin_dashboard') }}">Dashboard</a></li>
        <li><a href="#">Departments</a></li>
        <li><a href="#">Roles</a></li>
        <li><a href="#">Employees</a></li>
      {% else %}
        <li><a href="{{ url_for('home.dashboard') }}">Dashboard</a></li>
      {% endif %}
      <li><a href="{{ url_for('auth.logout') }}">Logout</a></li>
      <li><a><i class="fa fa-user"></i>  Hi, {{ current_user.username }}!</a></li>
    {% else %}
      <li><a href="{{ url_for('home.homepage') }}">Home</a></li>
      <li><a href="{{ url_for('auth.register') }}">Register</a></li>
      <li><a href="{{ url_for('auth.login') }}">Login</a></li>
    {% endif %}
</ul>

Trong menu ở trên, mình sử dụng current_userproxy từ Flask-Login để kiểm tra xem user hiện tại có phải là admin hay không. Nếu đúng, mình hiển thị menu admin cho phép user redirect đến các trang Phòng ban, Vai trò và Nhân viên. Lưu ý rằng mình sử dụng # cho các link trong menu admin. Mình sẽ cập nhật điều này sau khi tạo các views tương ứng.

Bây giờ mình sẽ chạy ứng dụng và đăng nhập với tư cách là user admin mà mình vừa tạo. Bạn sẽ thấy dashboard của admin: Admin dashboard

Hãy kiểm tra lỗi mà mình đã đặt trong file home/views.py để ngăn người dùng không phải admin truy cập vào dashboard admin. Đăng xuất và sau đó đăng nhập với một user thường. Nhập địa chỉ sau trên URL: http://127.0.0.1:5000/admin/dashboard. Bạn sẽ thấy xuất hiện lỗi 403 Forbidden.

403 Forbidden

3. Phòng ban

Bây giờ mình sẽ bắt đầu làm việc trên admin blueprint, có phần lớn chức năng trong ứng dụng. Mình sẽ bắt đầu bằng cách xây dựng chức năng CRUD cho các phòng ban.

3.1 Form

Mình sẽ bắt đầu với file admin/forms.py, ở đây mình sẽ tạo 1 form để thêm và chỉnh sửa các phòng ban.

  • File app/admin/forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired


class DepartmentForm(FlaskForm):
    """
    Form for admin to add or edit a department
    """
    name = StringField('Name', validators=[DataRequired()])
    description = StringField('Description', validators=[DataRequired()])
    submit = SubmitField('Submit')

Form này khá đơn giản và chỉ có hai trường namedepartment cả hai trường đều là bắt buộc. Mình thực thi điều này bằng cách sử dụng DataRequired() validator từ WTForms. Lưu ý là mình sẽ sử dụng cùng một biểu mẫu để thêm và chỉnh sửa các phòng ban.

3.2 Views

Bây giờ, chúng ta hãy làm việc trên các file views:

  • File app/admin/views.py
from flask import abort, flash, redirect, render_template, url_for
from flask_login import current_user, login_required

from . import admin
from forms import DepartmentForm
from .. import db
from ..models import Department


def check_admin():
    """
    Prevent non-admins from accessing the page
    """
    if not current_user.is_admin:
        abort(403)

# Department Views
@admin.route('/departments', methods=['GET', 'POST'])
@login_required
def list_departments():
    """
    List all departments
    """
    check_admin()

    departments = Department.query.all()

    return render_template('admin/departments/departments.html',
                           departments=departments, title="Departments")


@admin.route('/departments/add', methods=['GET', 'POST'])
@login_required
def add_department():
    """
    Add a department to the database
    """
    check_admin()

    add_department = True

    form = DepartmentForm()
    if form.validate_on_submit():
        department = Department(name=form.name.data,
                                description=form.description.data)
        try:
            # add department to the database
            db.session.add(department)
            db.session.commit()
            flash('You have successfully added a new department.')
        except:
            # in case department name already exists
            flash('Error: department name already exists.')

        # redirect to departments page
        return redirect(url_for('admin.list_departments'))

    # load department template
    return render_template('admin/departments/department.html', action="Add",
                           add_department=add_department, form=form,
                           title="Add Department")


@admin.route('/departments/edit/<int:id>', methods=['GET', 'POST'])
@login_required
def edit_department(id):
    """
    Edit a department
    """
    check_admin()

    add_department = False

    department = Department.query.get_or_404(id)
    form = DepartmentForm(obj=department)
    if form.validate_on_submit():
        department.name = form.name.data
        department.description = form.description.data
        db.session.commit()
        flash('You have successfully edited the department.')

        # redirect to the departments page
        return redirect(url_for('admin.list_departments'))

    form.description.data = department.description
    form.name.data = department.name
    return render_template('admin/departments/department.html', action="Edit",
                           add_department=add_department, form=form,
                           department=department, title="Edit Department")


@admin.route('/departments/delete/<int:id>', methods=['GET', 'POST'])
@login_required
def delete_department(id):
    """
    Delete a department from the database
    """
    check_admin()

    department = Department.query.get_or_404(id)
    db.session.delete(department)
    db.session.commit()
    flash('You have successfully deleted the department.')

    # redirect to the departments page
    return redirect(url_for('admin.list_departments'))

    return render_template(title="Delete Department")

Mình bắt đầu bằng cách tạo một function, function check_admin này sẽ redirect đến page 403 Forbidden nếu user không phải admin cố gắng truy cập các chế độ xem này. Mình sẽ gọi chức năng này trong mọi chế độ xem quản trị.

Trên đây có các function list_departments để get all phòng ban, add_department thêm phòng ban, edit_department sửa phòng ban, delete_department xoá phòng ban.

Lưu ý: Mình làm cùng một template để create và edit ở file department.html. Đây là lý do tại sao mình có biến add_department = true trong view add_department, cũng như add_department = false trong view edit_department.

3.3 Templates

Tạo một thư mục templates/admin và trong đó, thêm một thư mục departments. Bên trong nó, thêm 2 file departments.htmldepartment.html:

<!-- app/templates/admin/departments/departments.html -->

{% import "bootstrap/utils.html" as utils %}
{% extends "base.html" %}
{% block title %}Departments{% endblock %}
{% block body %}
<div class="content-section">
  <div class="outer">
    <div class="middle">
      <div class="inner">
        <br/>
        {{ utils.flashed_messages() }}
        <br/>
        <h1 style="text-align:center;">Departments</h1>
        {% if departments %}
          <hr class="intro-divider">
          <div class="center">
            <table class="table table-striped table-bordered">
              <thead>
                <tr>
                  <th width="15%"> Name </th>
                  <th width="40%"> Description </th>
                  <th width="15%"> Employee Count </th>
                  <th width="15%"> Edit </th>
                  <th width="15%"> Delete </th>
                </tr>
              </thead>
              <tbody>
              {% for department in departments %}
                <tr>
                  <td> {{ department.name }} </td>
                  <td> {{ department.description }} </td>
                  <td>
                    {% if department.employees %}
                      {{ department.employees.count() }}
                    {% else %}
                      0
                    {% endif %}
                  </td>
                  <td>
                    <a href="{{ url_for('admin.edit_department', id=department.id) }}">
                      <i class="fa fa-pencil"></i> Edit 
                    </a>
                  </td>
                  <td>
                    <a href="{{ url_for('admin.delete_department', id=department.id) }}">
                      <i class="fa fa-trash"></i> Delete 
                    </a>
                  </td>
                </tr>
              {% endfor %}
              </tbody>
            </table>
          </div>
          <div style="text-align: center">
        {% else %}
          <div style="text-align: center">
            <h3> No departments have been added. </h3>
            <hr class="intro-divider">
        {% endif %}
          <a href="{{ url_for('admin.add_department') }}" class="btn btn-default btn-lg">
            <i class="fa fa-plus"></i>
            Add Department
          </a>
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Mình đã tạo một bảng trong template ở trên. Hãy lưu ý đến hàm count() mà mình sử dụng trong trường hợp này để lấy số lượng nhân viên. Mỗi bộ phận được liệt kê sẽ có một link để sửa và xóa.

Nếu không có phòng ban, trang sẽ hiển thị "Chưa có phòng ban nào được thêm vào". Ngoài ra còn có một nút có thể được nhấp để thêm một bộ phận mới.

Bây giờ mình sẽ làm việc trên template để add và edit các phòng ban:


<!-- app/templates/admin/departments/department.html -->

{% import "bootstrap/wtf.html" as wtf %}
{% extends "base.html" %}
{% block title %}
    {% if add_department %}
        Add Department
    {% else %}
        Edit Department
    {% endif %}
{% endblock %}
{% block body %}
<div class="content-section">
 <div class="outer">
    <div class="middle">
      <div class="inner">
        <div class="center">
            {% if add_department %}
                <h1>Add Department</h1>
            {% else %}
                <h1>Edit Department</h1>
            {% endif %}
            <br/>
            {{ wtf.quick_form(form) }}
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Mình sử dụng biên add_departmentđể xác định xem title sẽ là "Thêm Bộ phận" hay "Chỉnh sửa Bộ phận".

Dưới đây là 1 chút css ở file app/static/css/style.css:

.outer {
    display: table;
    position: absolute;
    height: 70%;
    width: 100%;
}

.middle {
    display: table-cell;
    vertical-align: middle;
}

.inner {
    margin-left: auto;
    margin-right: auto;
}

Cuối cùng, mình sẽ thêm link ở menu admin:


<!-- app/templates/base.html -->

<!-- Modify nav bar menu -->
<li><a href="{{ url_for('admin.list_departments') }}">Departments</a></li>

Khởi động lại server, sau đó đăng nhập lại với user admin và nhấp vào link Phòng ban, đây là màn hình khi chưa có bộ phận nào:

List departments

Thêm một bộ phận:

Add department List departments

Okey, It worked!

Bây giờ mình sẽ sửa nó: Edit department

Chỉnh sửa bộ phận thành công. Tiếp theo test thử chức năng delete:

Delete success

4. Role

Bây giờ để làm việc đến role. Cái này sẽ rất giống với mã phòng ban vì các chức năng của role và phòng ban hoàn toàn giống nhau.

4.1 Form

Mình sẽ bắt đầu bằng cách tạo form để add và edit role. Thêm code sau vào file admin/forms.py:

  • File app/admin/forms.py
# existing code remains

class RoleForm(FlaskForm):
    """
    Form for admin to add or edit a role
    """
    name = StringField('Name', validators=[DataRequired()])
    description = StringField('Description', validators=[DataRequired()])
    submit = SubmitField('Submit')

4.2 Views

Tiếp theo, mình sẽ thêm các function add, list, edit và delete role. Thêm code sau vào file admin/views.py:

# app/admin/views.py

# update imports

from forms import DepartmentForm, RoleForm
from ..models import Department, Role

# existing code remains

# Role Views


@admin.route('/roles')
@login_required
def list_roles():
    check_admin()
    """
    List all roles
    """
    roles = Role.query.all()
    return render_template('admin/roles/roles.html',
                           roles=roles, title='Roles')


@admin.route('/roles/add', methods=['GET', 'POST'])
@login_required
def add_role():
    """
    Add a role to the database
    """
    check_admin()

    add_role = True

    form = RoleForm()
    if form.validate_on_submit():
        role = Role(name=form.name.data,
                    description=form.description.data)

        try:
            # add role to the database
            db.session.add(role)
            db.session.commit()
            flash('You have successfully added a new role.')
        except:
            # in case role name already exists
            flash('Error: role name already exists.')

        # redirect to the roles page
        return redirect(url_for('admin.list_roles'))

    # load role template
    return render_template('admin/roles/role.html', add_role=add_role,
                           form=form, title='Add Role')


@admin.route('/roles/edit/<int:id>', methods=['GET', 'POST'])
@login_required
def edit_role(id):
    """
    Edit a role
    """
    check_admin()

    add_role = False

    role = Role.query.get_or_404(id)
    form = RoleForm(obj=role)
    if form.validate_on_submit():
        role.name = form.name.data
        role.description = form.description.data
        db.session.add(role)
        db.session.commit()
        flash('You have successfully edited the role.')

        # redirect to the roles page
        return redirect(url_for('admin.list_roles'))

    form.description.data = role.description
    form.name.data = role.name
    return render_template('admin/roles/role.html', add_role=add_role,
                           form=form, title="Edit Role")


@admin.route('/roles/delete/<int:id>', methods=['GET', 'POST'])
@login_required
def delete_role(id):
    """
    Delete a role from the database
    """
    check_admin()

    role = Role.query.get_or_404(id)
    db.session.delete(role)
    db.session.commit()
    flash('You have successfully deleted the role.')

    # redirect to the roles page
    return redirect(url_for('admin.list_roles'))

    return render_template(title="Delete Role")

4.3 Templates

Tạo một folder roles trong folder templates/admin. Trong đó, tạo các file roles.htmlrole.html:


<!-- app/templates/admin/roles/roles.html -->

{% import "bootstrap/utils.html" as utils %}
{% extends "base.html" %}
{% block title %}Roles{% endblock %}
{% block body %}
<div class="content-section">
  <div class="outer">
    <div class="middle">
      <div class="inner">
        <br/>
        {{ utils.flashed_messages() }}
        <br/>
        <h1 style="text-align:center;">Roles</h1>
        {% if roles %}
          <hr class="intro-divider">
          <div class="center">
            <table class="table table-striped table-bordered">
              <thead>
                <tr>
                  <th width="15%"> Name </th>
                  <th width="40%"> Description </th>
                  <th width="15%"> Employee Count </th>
                  <th width="15%"> Edit </th>
                  <th width="15%"> Delete </th>
                </tr>
              </thead>
              <tbody>
              {% for role in roles %}
                <tr>
                  <td> {{ role.name }} </td>
                  <td> {{ role.description }} </td>
                  <td>
                    {% if role.employees %}
                      {{ role.employees.count() }}
                    {% else %}
                      0
                    {% endif %}
                  </td>
                  <td>
                    <a href="{{ url_for('admin.edit_role', id=role.id) }}">
                      <i class="fa fa-pencil"></i> Edit 
                    </a>
                  </td>
                  <td>
                    <a href="{{ url_for('admin.delete_role', id=role.id) }}">
                      <i class="fa fa-trash"></i> Delete 
                    </a>
                  </td>
                </tr>
              {% endfor %}
              </tbody>
            </table>
          </div>
          <div style="text-align: center">
        {% else %}
          <div style="text-align: center">
            <h3> No roles have been added. </h3>
            <hr class="intro-divider">
        {% endif %}
          <a href="{{ url_for('admin.add_role') }}" class="btn btn-default btn-lg">
            <i class="fa fa-plus"></i>
            Add Role
          </a>
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Giống như các phòng ban, Mình đã tạo một bảng để hiển thị tất cả các role với tên, description và số lượng nhân viên. Mỗi role được liệt kê cũng sẽ có một link để edit và delete. Nếu không có role nào, sẽ hiển thị một thông báo. Và thêm 1 mút để thêm role mowis.


<!-- app/templates/admin/roles/role.html -->

{% import "bootstrap/wtf.html" as wtf %}
{% extends "base.html" %}
{% block title %}
    {% if add_department %}
        Add Role
    {% else %}
        Edit Role
    {% endif %}
{% endblock %}
{% block body %}
<div class="content-section">
 <div class="outer">
    <div class="middle">
      <div class="inner">
        <div class="center">
            {% if add_role %}
                <h1>Add Role</h1>
            {% else %}
                <h1>Edit Role</h1>
            {% endif %}
            <br/>
            {{ wtf.quick_form(form) }}
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Mình cũng sử dụng biên add_role ở trên giống như biến add_department cho template department.html.

Okey, giờ mình sẽ update lại menu admin:


<!-- app/templates/base.html -->

<!-- Modify nav bar menu -->
<li><a href="{{ url_for('admin.list_roles') }}">Roles</a></li>

Restart lại server, bây giờ bạn có thể truy cập trang roles và thêm, edit, delete role.

List Roles

Add role

Add success

Edit role

Delete role

5. Nhân viên

Tiếp theo mình sẽ làm việc với danh sách nhân viên, cũng như phân công các phòng ban và role.

5.1 Form

Mình sẽ cần một biểu mẫu để chỉ định mỗi nhân viên một bộ phận và vai trò. Thêm code sau vào file admin/forms.py:

# app/admin/forms.py

# update imports
from wtforms.ext.sqlalchemy.fields import QuerySelectField

from ..models import Department, Role

# existing code remains

class EmployeeAssignForm(FlaskForm):
    """
    Form for admin to assign departments and roles to employees
    """
    department = QuerySelectField(query_factory=lambda: Department.query.all(),
                                  get_label="name")
    role = QuerySelectField(query_factory=lambda: Role.query.all(),
                            get_label="name")
    submit = SubmitField('Submit')

Mình đã thêm một loại trường mới, trường QuerySelectField này mình sử dụng cho cả trường phòng ban và trường role. Admin sẽ chọn một bộ phận và một role bằng cách sử dụng template trên giao diện người dùng.

5.2 Views

Thêm code sau vào file admin/views.py:

  • File app/admin/views.py
# update imports

from forms import DepartmentForm, EmployeeAssignForm, RoleForm
from ..models import Department, Employee, Role

# existing code remains
# Employee Views

@admin.route('/employees')
@login_required
def list_employees():
    """
    List all employees
    """
    check_admin()

    employees = Employee.query.all()
    return render_template('admin/employees/employees.html',
                           employees=employees, title='Employees')


@admin.route('/employees/assign/<int:id>', methods=['GET', 'POST'])
@login_required
def assign_employee(id):
    """
    Assign a department and a role to an employee
    """
    check_admin()

    employee = Employee.query.get_or_404(id)

    # prevent admin from being assigned a department or role
    if employee.is_admin:
        abort(403)

    form = EmployeeAssignForm(obj=employee)
    if form.validate_on_submit():
        employee.department = form.department.data
        employee.role = form.role.data
        db.session.add(employee)
        db.session.commit()
        flash('You have successfully assigned a department and role.')

        # redirect to the roles page
        return redirect(url_for('admin.list_employees'))

    return render_template('admin/employees/employee.html',
                           employee=employee, form=form,
                           title='Assign Employee')

Chức năng list_employees sẽ query dữ liệu trong database và được gán vào biến employees, sau đó dữ liệu sẽ được đổ ra view.

Đầu tiên, kiểm tra xem nhân viên có phải là user admin hay không; nếu không sẽ redirect đến trang 403 Forbidden. Nếu đúng, sẽ cập nhật employee.department và employee.role với dữ liệu đã chọn từ form, về cơ bản sẽ gán cho nhân viên một bộ phận và vai trò mới.

5.3 Templates

Tạo một thư mục employees trong thư mục templates/admin. Trong đó, tạo tiếp file employees.htmlemployee.html:

  • File app/templates/admin/employees/employees.html
{% import "bootstrap/utils.html" as utils %}
{% extends "base.html" %}
{% block title %}Employees{% endblock %}
{% block body %}
<div class="content-section">
  <div class="outer">
    <div class="middle">
      <div class="inner">
        <br/>
        {{ utils.flashed_messages() }}
        <br/>
        <h1 style="text-align:center;">Employees</h1>
        {% if employees %}
          <hr class="intro-divider">
          <div class="center">
            <table class="table table-striped table-bordered">
              <thead>
                <tr>
                  <th width="15%"> Name </th>
                  <th width="30%"> Department </th>
                  <th width="30%"> Role </th>
                  <th width="15%"> Assign </th>
                </tr>
              </thead>
              <tbody>
              {% for employee in employees %}
                {% if employee.is_admin %}
                    <tr style="background-color: #aec251; color: white;">
                        <td> <i class="fa fa-key"></i> Admin </td>
                        <td> N/A </td>
                        <td> N/A </td>
                        <td> N/A </td>
                    </tr>
                {% else %}
                    <tr>
                      <td> {{ employee.first_name }} {{ employee.last_name }} </td>
                      <td>
                        {% if employee.department %}
                          {{ employee.department.name }}
                        {% else %}
                          -
                        {% endif %}
                      </td>
                      <td>
                        {% if employee.role %}
                          {{ employee.role.name }}
                        {% else %}
                          -
                        {% endif %}
                      </td>
                      <td>
                        <a href="{{ url_for('admin.assign_employee', id=employee.id) }}">
                          <i class="fa fa-user-plus"></i> Assign
                        </a>
                      </td>
                    </tr>
                {% endif %}
              {% endfor %}
              </tbody>
            </table>
          </div>
        {% endif %}
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Template employees.html sẽ list ra bảng tất cả nhân viên. Bảng hiển thị tên đầy đủ, bộ phận và vai trò hoặc hiển thị - nếu không có bộ phận và vai trò nào.

Vì user admin cũng là nhân viên, nên user admin sẽ được hiển thị trong bảng. Nhưng, user admin sẽ được nổi bật lên với nền màu xanh lá cây và text màu trắng.

  • File app/templates/admin/employees/employee.html
{% import "bootstrap/wtf.html" as wtf %}
{% extends "base.html" %}
{% block title %}Assign Employee{% endblock %}
{% block body %}
<div class="content-section">
 <div class="outer">
    <div class="middle">
      <div class="inner">
        <div class="center">
            <h1> Assign Departments and Roles </h1>
            <br/>
            <p>
                Select a department and role to assign to
                <span style="color: #aec251;">
                    {{ employee.first_name }} {{ employee.last_name }}
                </span>
            </p>
            <br/>
            {{ wtf.quick_form(form) }}
        </div>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Okey, mình sé update lại menu admin:


<!-- app/templates/base.html -->

<!-- Modify nav bar menu -->
<li><a href="{{ url_for('admin.list_employees') }}">Employees</a></li>

Redirect đến trang Nhân viên. Nếu không có người dùng nào khác ngoài quản trị viên, sẽ hiển thị thế này:

List employees

Khi có một nhân viên đăng ký, sẽ được hiển thị:

List all employees

Thêm phòng ban và role để gán cho nhân viên. Add department and role

Assign employee

6. Kết luận

Okey, như vậy mình đã có một ứng dụng web CRUD full chức năng! Trong Phần hai này, mình đã có thể tạo user admin và trang dashboard admin, cũng như tùy chỉnh menu cho các loại user khác nhau. Mình đã xây dựng chức năng cốt lõi của ứng dụng và hiện có thể add, list, edit, delete các phòng ban và role cũng như gán vào với nhân viên. Mình cũng đã thêm page 403 Forbidden để giới hạn quyền cho user admin mới được xem.

Tài liệu tham khảo

Build a CRUD Web App With Python and Flask - Part Two


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.