利用Flask搭建微电影视频网站

利用Flask搭建微电影视频网站(十二):角色和管理员管理

2018-09-15  本文已影响7人  啃饼小白

关于博主

努力与运动兼备~~~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!

                  微信公众号:  啃饼思录
                   QQ: 2810706745(啃饼小白)

写在前面

本篇笔记,我们介绍角色管理,管理员管理以及访问权限控制这是三个功能。角色管理,管理员管理这个和前面介绍的标签,电影,预告管理也是差不多的,所以我就加快速度,话不多说,开始学习吧。

本篇笔记对应上传的仓库为:https://github.com/licheetools/movie对应第十二篇。

角色管理

角色管理和上面的权限管理是非常相似的,所以有些操作就是复制粘贴和套用了!

使用到的内容

我们将使用到的内容有:
模型:Role
表单: RoleForm
请求方法: GET ,POST
访问控制: @admin_login_req

1、添加表单验证字段

打开forms.py,我们定义Form字段,注意因为是权限管理模块,所以对应的html页面就是role_add.html,而且我们在forms.py里面定义的字段必须与role_add.html里面的保持一致!(如果觉得不是很容易看出来,可以运行项目,直接访问role_add.html页面,这样明显多了!)

from wtforms import  SelectMultipleField
from app.models import  Auth

# 添加角色
class RoleFrom(FlaskForm):
    name = StringField(
        label="角色名称",
        validators=[
            DataRequired("角色名称不能为空")
        ],
        description="角色名称",
        render_kw={
            "class": "form-control",
            "id": "input_name",
            "placeholder": "请输入角色名称!"
        }
    )
    auths = SelectMultipleField(
        label="权限列表",
        validators=[
            DataRequired("权限列表不能为空")
        ],
        coerce=int,
        choices=[(v.id, v.name) for v in Auth.query.all()],   # 数据动态填充选择,采用列表生成式
        description="权限列表",
        render_kw={
            "class": "form-control",
            "id": "input_url",
            "placeholder": "请选择角色列表!"
        }
    )
    submit = SubmitField(
        '修改',
        render_kw={
            "class": "btn btn-primary",
        }
    )

2、准备页面渲染字段

打开admin/views.py文件,我们修改role_add函数:

from app.admin.forms import RoleForm
from app.models import Role

# 添加权限
@admin.route('/role/add', methods=["GET", "POST"])
@admin_login_req
def role_add():
    form = RoleForm()
    return render_template("admin/role_add.html", form=form)

3、后台页面传值显示以及操作信息提示

可以仿照前面的auth_add.html来修改role_add.html:

记得在submit的上面添加{{ form.csrf_token }},还有form提交的方法:

  <form role="form" method="post" >

4、后台页面错误信息提示

打开role_add.html页面,我们同样可以仿照auth_add.html来进行修改:(里面的title需要按照我们数据库里字段的要求进行修改)

{% for err in form.title.errors %}
<div class="col-md-12">
<p style="color:red">{{ err }}</p>
</div>
{% endfor %}

5、修改角色添加函数

# 添加角色
@admin.route('/role/add', methods=["GET", "POST"])
@admin_login_req
def role_add():
    form = RoleFrom()
    if form.validate_on_submit():
        data = form.data
        role = Role(
            name=data["name"],
            auths=','.join(map(str, data["auths"]))  # 采用高阶函数map来生成一个迭代器,然后用''.join()来序列为一个字符串对象
        )
        db.session.add(role)
        db.session.commit()
        flash("添加角色成功!", "ok")
    return render_template("admin/role_add.html", form=form)

然后运行一下我们的manage.py文件,就可以去后台添加我们的角色了:

查询一下数据库,发现这样的信息就说明权限添加成功了:

mysql> select * from role;
+----+-------------+-------+---------------------+
| id | name        | auths | addtime             |
+----+-------------+-------+---------------------+
|  1 | 超级管理员 | NULL  | 2018-08-17 16:11:06 |
|  2 | 管理员      | 1235  | 2018-08-12 16:24:18 |
|  3 | 管理员1     | 12    | 2018-08-12 16:24:26 |
|  4 | 管理员2     | 123   | 2018-08-12 16:24:33 |
+----+-------------+-------+---------------------+
4 rows in set (0.00 sec)

mysql>

角色列表的配置

打开我们的views.py文件,我们参考之前的权限列表的配置,对角色列表进行配置:

# 角色列表
@admin.route('/role/list/<int:page>', methods=["GET"])
@admin_login_req
def role_list(page=None):
    if page is None:
        page = 1
# 查询的时候关联标签,采用join来加进去,多表关联用filter,过滤用filter_by
    page_data = Role.query.order_by(
        Role.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template("admin/role_list.html", page_data=page_data)

接着去我们的grid.html页面添加page参数(page=1),记住所以的列表都要有一个初始页,也就是说肯定有一个页面去显示,就算没有数据也要有页面,否则就是404嘛,这既然不是404,就得有个页面,空的页面也是可以的:

然后我们打开role_list.html页面,进行for循环的填充:

然后就是分页功能了,我们参考之前在标签列表页面的配置,打开auth_list.html,删除那个<ul class="pagination pagination-sm no-margin pull-right"> ....... </ul>标签里的内容,我们在开头导入刚才的admin_page.html文件,然后开始调用:

{% import "ui/admin_page.html" as pg %}

将下面的代码填充你刚才删除的那段的位置:

{{ pg.page(page_data, "admin.role_list") }}

至此,关于角色列表的介绍就到此为止了!接下来是角色的删除!

角色的删除

打开views.py文件,我们新定义role_del函数:(仿照权限删除的函数)

# 角色删除
@admin.route('/role/del/<int:id>', methods=["GET"])
@admin_login_req
def role_del(id=None):
    role = Role.query.filter_by(id=id).first_or_404()
    db.session.delete(role)
    db.session.commit()
    flash("角色删除成功!", "ok")
    return redirect(url_for("admin.role_list", page=1))

然后打开role_list.html页面,我们添加删除成功的flash提示(同样可以复制我们的auth_list.html页面的那一部分):

提示:有小伙伴们问我为啥需要添加这个操作成功的信息提示,那是因为我们的删除操作是在我们当前的列表页面进行的,所以自然我们的页面操作提示信息就应该放在列表页!!!

最后别忘了在role_list.html页面添加删除跳转链接:

<a href="{{ url_for('admin.role_del', id=v.id) }}" class="label label-danger">删除</a>

然后去测试一下我们的项目,看一看角色删除功能是否已经实现了呢,接下来进行角色的编辑功能的实现!

角色的编辑

打开views.py文件,我们在角色列表的下面新增以下代码:

# 角色编辑
@admin.route('/role/edit/<int:id>', methods=["GET", "POST"])
@admin_login_req
def role_edit(id=None):
    form = RoleForm()   # 实例化一个TagForm,然后将form传递到前端页面去。
    role = Role.query.get_or_404(id)
    if request.method == "GET":
        form.auths.data = list(map(int, role.auths.split(",")))  # form.auths.data为整形数组,而role.auths为一个可变字符串
    if form.validate_on_submit():
        data = form.data
        role.auths = ','.join(map(str, data["auths"]))
        role.name = data["name"]
        db.session.add(role)
        db.session.commit()
        flash("修改角色成功!", "ok")
        return redirect(url_for("admin.role_edit", id=id))
    return render_template("admin/role_edit.html", form=form, role=role)

接着在admin下面新建role_edit.html页面,将role_add.html页面全部添加进去,然后开始修改:将所有的添加字段改为编辑。并且给那些需要编辑的字段赋初值:

记住那个{{ form.auths }}是无法直接给它付初始值的,我们采用GET方法来请求!

然后在role_list里面修改页面的跳转:

 <a href="{{ url_for('admin.role_edit', id =v.id) }}" class="label label-success">编辑</a>

刷新一下,看看关于权限这一功能我们的项目是不是都能使用了!

错误修正

近期有小伙伴反映页面跳转到

http://127.0.0.1:5000/admin/role/edit/<int:id>

就出下面的错误:

builtins.AttributeError
AttributeError: 'NoneType' object has no attribute 'split'

然后我通过单点测试,发现原来这个form.auths.data是个空值:

但是呢,这个choices却不是,它是一个list,里面又有tuple。我们可以考虑取出每个tuple的第一个元素,它是Int类型,然后构造一个新的int类型的list:

list(map(lambda x: x[0], form.auths.choices))

而且我们知道,我们这个role.auths它其实是一个varchar类型,所以我们也可以把它变为一个int类型的list:

list(map(int, auths.split(",")))

然后就是这样:

list(map(lambda x: x[0], form.auths.choices))==list(map(int, auths.split(",")))

把之前的:

 form.auths.data = list(map(int, role.auths.split(",")))  # form.auths.data为整形数组,而role.auths为一个可变字符串

替换掉!!!记住我们不可以修改超级管理员的角色名称和权限,因为超级管理员权力最大,如果某个权限连它都没有的话,那就没有人有了!!!

管理员管理

使用到的内容

我们将使用到的内容有:
模型:Admin
表单: AdminForm
请求方法: GET ,POST
访问控制: @admin_login_req

1、添加表单验证字段

打开forms.py,我们定义Form字段,注意因为是权限管理模块,所以对应的html页面就是admin_add.html,而且我们在forms.py里面定义的字段必须与admin_add.html里面的保持一致!(如果觉得不是很容易看出来,可以运行项目,直接访问admin_add.html页面,这样明显多了!)

from app.models import Role
from wtforms.validators import  EqualTo  # EqualTo 用于比对两次密码是否一致! 


# 添加管理员
class AdminForm(FlaskForm):
    name = StringField(
        label="管理员名称",
        validators=[
            DataRequired("管理员名称不能为空!")
        ],
        description="管理员名称",
        render_kw={
            "class": "form-control",
            "placeholder": "请输入管理员名称!",
            # "required": "required"   # 注释此处显示forms报错errors信息
        }
    )

    pwd = PasswordField(
        label="管理员密码",
        validators=[
            DataRequired("管理员密码不能为空!")
        ],
        description="管理员密码",
        render_kw={
            "class": "form-control",
            "placeholder": "请输入管理员密码!",
            # "required": "required"   # 注释此处显示forms报错errors信息
        }
    )
    repwd = PasswordField(
        label="管理员重复密码",
        validators=[
            DataRequired("管理员重复密码不能为空!"),
            EqualTo('pwd', message="两次密码不一致!")
        ],
        description="管理员重复密码",
        render_kw={
            "class": "form-control",
            "placeholder": "请输入管理员重复密码!",
            # "required": "required"   # 注释此处显示forms报错errors信息
        }
    )
    role_id = SelectField(
        label="所属角色",
        validators=[
            DataRequired("请选择所属角色!")
        ],
        # 所属角色是整数型
        coerce=int,
        # 采用下拉选择的方式进行所属角色的选择
        choices=[(v.id, v.name) for v in Role.query.all()],
        description="所属角色",
        render_kw={
            "class": "form-control",
        }
    )

    submit = SubmitField(
        '编辑',
        render_kw={
            "class": "btn btn-primary btn-block btn-flat",
        }
    )

2、准备页面渲染字段

打开admin/views.py文件,我们修改admin_add函数:

from app.admin.forms import AdminForm

# 添加管理员
@admin.route('/admin/add', methods=["GET", "POST"])
@admin_login_req
def admin_add():
    form = AdminForm()
    from werkzeug.security import generate_password_hash
    if form.validate_on_submit():
        data = form.data
        admin = Admin(
            name=data["name"],
            pwd=generate_password_hash(data["pwd"]),
            role_id=data["role_id"],
            is_super=1  # 普通管理员为1
        )
        db.session.add(admin)
        db.session.commit()
        flash("添加管理员成功!", "ok")
    return render_template("admin/admin_add.html", form=form)

3、后台页面传值显示以及操作信息提示

可以仿照前面的role_add.html来修改admin_add.html:

记得在submit的上面添加{{ form.csrf_token }},还有form提交的方法:

  <form role="form" method="post" >

4、后台页面错误信息提示

打开admin_add.html页面,我们同样可以仿照admin_add.html来进行修改:(里面的title需要按照我们数据库里字段的要求进行修改)

{% for err in form.title.errors %}
<div class="col-md-12">
<p style="color:red">{{ err }}</p>
</div>
{% endfor %}

然后运行一下我们的manage.py文件,就可以去后台添加我们的管理员了:

管理员列表的配置

打开我们的views.py文件,我们参考之前的角色列表的配置,对管理员列表进行配置:

# 管理员列表
@admin.route('/admin/list/<int:page>', methods=["GET"])
@admin_login_req
def admin_list(page=None):
    if page is None:
        page = 1
    page_data = Admin.query.join(
        Role
    ).filter(
        Role.id == Admin.role_id
    ).order_by(
        Admin.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template("admin/admin_list.html", page_data=page_data)

接着去我们的grid.html页面添加page参数(page=1),记住所以的列表都要有一个初始页,也就是说肯定有一个页面去显示,就算没有数据也要有页面,否则就是404嘛,这既然不是404,就得有个页面,空的页面也是可以的:

然后我们打开admin_list.html页面,进行for循环的填充:

然后就是分页功能了,我们参考之前在标签列表页面的配置,打开admin_list.html,删除那个<ul class="pagination pagination-sm no-margin pull-right"> ....... </ul>标签里的内容,我们在开头导入刚才的admin_page.html文件,然后开始调用:

{% import "ui/admin_page.html" as pg %}

将下面的代码填充你刚才删除的那段的位置:

{{ pg.page(page_data, "admin.admin_list") }}

错误提示:最近有小伙伴告诉我,一点击管理员列表就会出现这样的错误:

jinja2.exceptions.UndefinedError: 'app.models.Admin object' has no attribute 'role'

那么你需要检查我们的models.py文件,查看我们的Role函数里面是否有这个字段:

admins = db.relationship("Admin", backref='role')  # 管理员外键关系关联

如果没有请添加,这样就应该不会出错了!

至此,关于管理员列表的介绍就到此为止了,下面介绍访问权限控制的配置!

访问权限控制的配置

打开views.py文件,我们新定义一个访问权限控制装饰器:

from flask import abort

# 访问权限控制装饰器
def admin_auth(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        admin = Admin.query.join(
            Role
        ).filter(
            Role.id == Admin.role_id,
            Admin.id == session["admin_id"]
        ).first()
        auths = admin.role.auths
        auths = list(map(int, auths.split(",")))
        auth_list = Auth.query.all()
        urls = [v.url for v in auth_list for val in auths if val == v.id]
        rule = request.url_rule
        if str(rule) not in urls:
            abort(404)
        return f(*args, **kwargs)
    return decorated_function

接下来给除了登录和后台首页以外所有的函数都加上访问权限控制装饰器,然后运行manage.py测试一下我们的项目是否可以使用。

不过这样在代码里面添加访问权限控制器挺累的,等有时间准备做一个后台权限分配页面,这样可视化操作就比较人性化了。

至此,本篇关于角色管理,管理员管理以及访问权限控制的介绍就到此为止了。也就是说所有后台的管理配置都介绍完了,下一篇我们就开始进行前台页面的配置,感谢你的赏阅。

本篇笔记对应上传的仓库为:https://github.com/licheetools/movie对应第十二篇。

上一篇 下一篇

猜你喜欢

热点阅读