使用Python和Flask构建CRUD Web应用-第二部分

This is Part Two of a three-part tutorial to build an employee management web app, named Project Dream Team. In Part One of the tutorial, we set up a MySQL database using MySQL-Python and Flask-SQLAlchemy. We created models, migrated the database, and worked on the home and auth blueprints and templates. By the end of Part One, we had a working app that had a homepage, registration page, login page, and dashboard. We could register a new user, login, and logout.

这是一个分为三部分的教程的第二部分,该教程用于构建名为Project Dream Team的员工管理Web应用程序。 在本教程的第一部分中,我们使用MySQL-Python和Flask-SQLAlchemy建立了一个MySQL数据库。 我们创建了模型,迁移了数据库,并处理了home以及auth蓝图和模板。 在第一部分结束时,我们有了一个可以正常运行的应用程序,该应用程序具有一个主页,注册页面,登录页面和仪表板。 我们可以注册一个新用户,登录并注销。

In Part Two, we will work on:

在第二部分中,我们将致力于:

  1. Creating an admin user and admin dashboard

    创建管理员用户和管理员仪表板
  2. Creating, listing, editing and deleting departments

    创建,列出,编辑和删除部门
  3. Creating, listing, editing and deleting roles

    创建,列出,编辑和删除角色
  4. Assigning departments and roles to employees

    为员工分配部门和角色

管理员用户 ( Admin User )

We'll start by creating an admin user through the command line. Flask provides a handy command, flask shell, that allows us to use an interactive Python shell for use with Flask apps.

我们将从通过命令行创建管理员用户开始。 Flask提供了一个方便的命令, flask shell ,它使我们能够使用交互式Python shell与Flask应用程序一起使用。

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

We've just created a user with a username, admin, and a password, admin2016. Recall that we set the is_admin field to default to False in the Employee model. To create the admin user above, we override the default value of is_admin and set it to True.

我们刚刚创建了一个用户名,用户名admin和密码admin2016 。 回想一下,我们在Employee模型中将is_admin字段设置为默认值False 。 要在上方创建admin用户,我们将覆盖is_admin的默认值并将其设置为True

管理仪表盘 ( Admin Dashboard )

Now that we have an admin user, we need to add a view for an admin dashboard. We also need to ensure that once the admin user logs in, they are redirected to the admin dashboard and not the one for non-admin users. We will do this in the home blueprint.

现在我们有了管理员用户,我们需要为管理员仪表板添加一个视图。 我们还需要确保管理员用户登录后,他们将被重定向到管理员仪表板,而不是非管理员用户。 我们将在home蓝图中进行此操作。

# 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")
# 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')

Next we'll create the admin dashboard template. Create an admin_dashboard.html file in the templates/home directory, and then add the following code in it:

接下来,我们将创建管理仪表板模板。 在templates/home目录中创建一个admin_dashboard.html文件,然后在其中添加以下代码:

<!-- 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 %}

Now we need to edit the base template to show a different menu for the admin user.

现在我们需要编辑基本模板以为管理员用户显示不同的菜单。

<!-- 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>

In the menu above, we make use of the current_user proxy from Flask-Login to check whether the current user is an admin. If they are, we display the admin menu which will allow them to navigate to the Departments, Roles and Employees pages. Notice that we use # for the links in the admin menu. We will update this after we have created the respective views.

在上面的菜单中,我们使用Flask-Login中的current_user代理检查当前用户是否为管理员。 如果是这样,我们将显示管理菜单,使他们可以导航到“部门,角色和员工”页面。 请注意,我们在管理菜单中的链接上使用# 。 创建相应的视图后,我们将对其进行更新。

Now run the app and login as the admin user that we just created. You should see the admin dashboard:

现在运行该应用程序,并以我们刚刚创建的管理员用户身份登录。 您应该看到管理控制台:

Let's test the error we set in the home/views.py file to prevent non-admin users from accessing the admin dashboard. Log out and then log in as a regular user. In your browser's address bar, manually enter the following URL: http://127.0.0.1:5000/admin/dashboard. You should get a 403 Forbidden error. It looks pretty boring now, but don't worry, we'll create custom error pages in Part Three!

让我们测试一下我们在home/views.py文件中设置的错误,以防止非管理员用户访问管理控制台。 注销,然后以普通用户身份登录。 在浏览器的地址栏中,手动输入以下URL: http://127.0.0.1:5000/admin/dashboard : http://127.0.0.1:5000/admin/dashboard : http://127.0.0.1:5000/admin/dashboard 。 您应该收到403 Forbidden错误。 现在看起来很无聊,但是不用担心,我们将在第三部分中创建自定义错误页面!

部门 ( Departments )

Now we'll start working on the admin blueprint, which has the bulk of the functionality in the application. We'll begin by building out CRUD functionality for the departments.

现在,我们将开始研究admin蓝图,该蓝图具有应用程序中的大部分功能。 我们将从为部门建立CRUD功能开始。

形式 (Forms)

We'll start with the admin/forms.py file, where we'll create a form to add and edit departments.

我们将从admin/forms.py文件开始,在该文件中,我们将创建一个表单以添加和编辑部门。

# 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')

The form is pretty simple and has only two fields, name and department, both of which are required. We enforce this using the DataRequired() validator from WTForms. Note that we will use the same form for adding and editing departments.

表单非常简单,只有两个字段,即namedepartment ,这两个都是必填字段。 我们使用WTForms中的DataRequired()验证器强制执行此操作。 请注意,我们将使用相同的表格来添加和编辑部门。

观看次数 (Views)

Now, let's work on the views:

现在,让我们处理视图:

# 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")

We begin by creating a function, check_admin, which throws a 403 Forbidden error if a non-admin user attempts to access these views. We will call this function in every admin view.

我们首先创建一个函数check_admin ,如果非管理员用户尝试访问这些视图,该函数将引发403 Forbidden错误。 我们将在每个管理员视图中调用此函数。

The list_departments view queries the database for all departments and assigns them to the variable departments, which we will use to list them in the template.

list_departments视图在数据库中查询所有部门,并将它们分配给可变departments ,我们将使用这些变量在模板中列出它们。

The add_department view creates a new department object using the form data, and adds it to the database. If the department name already exists, an error message is displayed. This view redirects to the list_departments. This means that once the admin user creates a new department, they will be redirected to the Departments page.

add_department视图使用表单数据创建一个新的部门对象,并将其添加到数据库中。 如果部门名称已经存在,则会显示一条错误消息。 该视图重定向到list_departments 。 这意味着一旦管理员用户创建了新部门,他们将被重定向到“部门”页面。

The edit_department view takes one parameter: id . This is the department ID, and will be passed to the view in the template. The view queries the database for a department with the ID specified. If the department doesn't exist, a 404 Not Found error is thrown. If it does, it is updated with the form data.

edit_department视图采用一个参数: id 。 这是部门ID,将被传递到模板中的视图。 该视图在数据库中查询具有指定ID的部门。 如果该部门不存在,则会引发404 Not Found错误。 如果是这样,它将使用表单数据进行更新。

The delete_department view is similar to the edit_department one, in that it takes a department ID as a parameter and throws an error if the specified department doesn't exist. If it does, it is deleted from the database.

所述delete_department视图类似于edit_department一个,因为它需要一个部门ID作为参数,并如果指定部中不存在引发错误。 如果是这样,则将其从数据库中删除。

Note that we render the same template for adding and editing individual departments: department.html. This is why we have the add_department variable in the add_department view (where it is set to True), as well as in the edit_department view (where it is set to False). We'll use this variable in the department.html template to determine what wording to use for the title and heading.

请注意,我们渲染了用于添加和编辑各个部门的相同模板: department.html 。 这就是为什么我们在add_department视图(将其设置为True )和edit_department视图(将其设置为False )中都具有add_department变量的add_department 。 我们将在department.html模板中使用此变量来确定标题和标题使用的措词。

范本 (Templates)

Create an templates/admin directory, and in it, add a departments directory. Inside it, add the departments.html and department.html files:

创建一个templates/admin目录,并在其中添加一个departments目录。 在其中,添加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 %}

We've created a table in the template above, where we will display all the departments with their name, description, and number of employees. Take note of the count() function, which we use in this case to get the number of employees. Each department listed will have an edit and delete link. Notice how we pass the department.id value to the edit_department and delete_department views in the respective links.

我们在上面的模板中创建了一个表格,其中将显示所有部门及其名称,描述和员工人数。 注意count()函数,在本例中我们使用它来获取员工人数。 列出的每个部门都有一个编辑和删除链接。 注意我们如何将department.id值传递到相应链接中的edit_departmentdelete_department视图。

If there are no departments, the page will display "No departments have been added". There is also a button which can be clicked to add a new department.

如果没有部门,页面将显示“未添加部门”。 还有一个按钮可以单击以添加新部门。

Now let's work on the template for adding and editing departments:

现在,让我们来研究用于添加和编辑部门的模板:

<!-- 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 %}

Notice that we use the add_department variable which we initialized in the admin/views.py file, to determine whether the page title will be "Add Department" or "Edit Department".

请注意,我们使用在admin/views.py文件中初始化的add_department变量来确定页面标题是“添加部门”还是“编辑部门”。

Add the following lines to your style.css file:

将以下几行添加到您的style.css文件中:

/* 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;
}

The .middle, .inner, and .outer classes are to center the content in the middle of the page.

.middle.inner.outer类将内容置于页面中间。

Lastly, let's put the correct link to the Departments page in the admin menu:

最后,让我们在管理菜单中将正确的链接放入“部门”页面:

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

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

Re-start the flask server, and then log back in as the admin user and click on the Departments link. Because we have not added any departments, loading the page will display:

重新启动flask服务器,然后以管理员用户身份重新登录,然后单击“部门”链接。 由于我们尚未添加任何部门,因此加载页面将显示:

Let's try adding a department:

让我们尝试添加一个部门:

It worked! We get the success message we configured in the add_department view, and can now see the department displayed.

有效! 我们得到在add_department视图中配置的成功消息,现在可以看到显示的部门。

Now let's edit it:

现在让我们对其进行编辑:

Notice that the current department name and description are already pre-loaded in the form. Also, take note of the URL, which has the ID of the department we are editing.

请注意,当前部门名称和说明已经预先加载在表格中。 另外,记下URL,其中包含我们正在编辑的部门的ID。

Editing the department is successful as well. Clicking the Delete link deletes the department and redirects to the Departments page, where a confirmation message is displayed:

部门编辑也很成功。 单击删除链接将删除部门,然后重定向到“部门”页面,在该页面上显示确认消息:

的角色 ( Roles )

Now to work on the roles. This will be very similar to the departments code because the functionality for roles and departments is exactly the same.

现在开始处理角色。 这将与部门代码非常相似,因为角色和部门的功能完全相同。

形式 (Forms)

We'll start by creating the form to add and edit roles. Add the following code to the admin/forms.py file:

我们将从创建表单来添加和编辑角色开始。 将以下代码添加到admin/forms.py文件中:

# 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')

观看次数 (Views)

Next we'll write the views to add, list, edit, and delete roles. Add the following code to the admin/views.py 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")

These list, add, edit, and delete views are similar to the ones for departments that we created earlier.

这些列表,添加,编辑和删除视图与我们之前创建的部门的视图相似。

范本 (Templates)

Create a roles directory in the templates/admin directory. In it, create the roles.html and role.html files:

templates/admin目录中创建roles目录。 在其中,创建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 %}

Just like we did for the departments, we have created a table where we will display all the roles with their name, description, and number of employees. Each role listed will also have an edit and delete link. If there are no roles, a message of the same will be displayed. There is also a button which can be clicked to add a new role.

就像我们为部门所做的一样,我们创建了一个表,其中将显示所有角色及其名称,描述和员工人数。 列出的每个角色还将具有一个编辑和删除链接。 如果没有角色,将显示相同的消息。 还有一个按钮可以单击以添加新角色。

<!-- 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 %}

We use the add_role variable above the same way we used the add_department variable for the department.html template.

我们使用add_role我们用同样的方式上述变量add_department变量为department.html模板。

Once again, let's update the admin menu with the correct link:

再一次,让我们使用正确的链接更新管理菜单:

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

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

Re-start the server. You should now be able to access the Roles page, and add, edit and delete roles.

重新启动服务器。 现在,您应该能够访问“角色”页面,并添加,编辑和删除角色。

雇员 ( Employees )

Now to work on listing employees, as well as assigning them departments and roles.

现在开始列出员工,以及为他们分配部门和角色。

形式 (Forms)

We'll need a form to assign each employee a department and role. Add the following to the admin/forms.py 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')

We have imported a new field type, QuerySelectField, which we use for both the department and role fields. This will query the database for all departments and roles. The admin user will select one department and one role using the form on the front-end.

我们导入了一个新的字段类型QuerySelectField ,我们将其用于部门和角色字段。 这将查询数据库中的所有部门和角色。 管理员用户将使用前端上的表单选择一个部门和一个角色。

观看次数 ( Views )

Add the following code to the admin/views.py file:

将以下代码添加到admin/views.py文件中:

# 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')

The list_employees view queries the database for all employees and assigns them to the variable employees, which we will use to list them in the template.

list_employees视图在数据库中查询所有雇员,并将他们分配给变量employees ,我们将使用该变量在模板中列出他们。

The assign_employee view takes an employee ID. First, it checks whether the employee is an admin user; if it is, a 403 Forbidden error is thrown. If not, it updates the employee.department and employee.role with the selected data from the form, essentially assigning the employee a new department and role.

assign_employee视图采用员工ID。 首先,它检查员工是否是管理员用户。 如果是,则抛出403 Forbidden错误。 如果不是,它将使用从表单中选择的数据更新employee.departmentemployee.role ,从本质上为雇员分配一个新的部门和角色。

范本 ( Templates )

Create a employees directory in the templates/admin directory. In it, create the employees.html and employee.html files:

templates/admin目录中创建一个employees目录。 在其中,创建employees.htmlemployee.html文件:

<!-- 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 %}

The employees.html template shows a table of all employees. The table shows their full name, department and role, or displays a - in case no department and role has been assigned. Each employee has an assign link, which the admin user can click to assign them a department and role.

employees.html模板显示所有雇员的表。 该表显示其全名,部门和角色,或者在没有分配部门和角色的情况下显示- 。 每个员工都有一个分配链接,管理员用户可以单击该链接为他们分配部门和角色。

Because the admin user is an employee as well, they will be displayed in the table. However, we have formatted the table such that admin users stand out with a green background and white text.

由于管理员用户也是雇员,因此他们将显示在表中。 但是,我们对表格进行了格式化,以便管理员用户以绿色背景和白色文本突出显示。

<!-- 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 %}

We need to update the admin menu once more:

我们需要再次更新管理菜单:

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

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

Navigate to the Employees page now. If there are no users other than the admin, this is what you should see:

立即导航到“员工”页面。 如果除管理员外没有其他用户,这是您应该看到的内容:

When there is an employee registered, this is displayed:

当有员工注册时,将显示:

Feel free to add a variety of departments and roles so that you can start assigning them to employees.

随意添加各种部门和角色,以便您可以开始将其分配给员工。

You can re-assign departments and roles as well.

您也可以重新分配部门和角色。

结论 ( Conclusion )

We now have a completely functional CRUD web app! In Part Two of the tutorial, we've been able to create an admin user and an admin dashboard, as well as customise the menu for different types of users. We've also built out the core functionality of the app, and can now add, list, edit, and delete departments and roles, as well as assign them to employees. We have also taken security into consideration by protecting certain views from unauthorized access.

我们现在拥有功能齐全的CRUD Web应用程序! 在本教程的第二部分中,我们已经能够创建一个管理员用户和一个管理员仪表板,以及为不同类型的用户自定义菜单。 我们还构建了应用程序的核心功能,现在可以添加,列出,编辑和删除部门和角色,以及将其分配给员工。 我们还通过保护某些视图免受未经授权的访问来考虑安全性。

In Part Three, we will create custom error pages, write tests, and deploy the app to PythonAnywhere.

在第三部分中,我们将创建自定义错误页面,编写测试并将该应用程序部署到PythonAnywhere

Update: here's Part Three.

更新:这是第三部分

翻译自: https://scotch.io/tutorials/build-a-crud-web-app-with-python-and-flask-part-two

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值