python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

昨日内容回顾

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
1. 简述权限管理的实现原理。
    粒度控制到按钮级别的权限控制
    - 用户登陆成功之后,将权限和菜单信息放入session
    - 每次请求时,在中间件中做权限校验
    - inclusion_tag实现的动态菜单
            
2. 表结构
    有6张表,分别是:
    菜单表,权限表,角色,用户表,用户角色关系表,角色权限关系表

3. 知识点
    - 中间件白名单:配置文件、中间件return None
    - 权限初始化:
        - left join 
        - 特殊字典的构造
            权限 = {
                权限别名:{id:'',title:'',url,pid:''},
                权限别名:{id:'',title:'',url,pid:''},
                权限别名:{id:'',title:'',url,pid:''},
            }
            
            菜单 = {
                菜单ID:{
                    title:'',
                    icon:'',
                    children:[
                        {id:'1',.....}
                    ]
                }
            }
        - key为数字的字典,在序列化时会变成字符串(*- 配置文件 
        
    - 中间件进行权限校验 
        - 权限校验
        - 导航路径
        - pid,访问无法成为菜单的权限时,默认展开的父级权限ID
        
    - 动态生成菜单
        - 通过inclusion_tag和两层for循环 + 中间件传来的pid
    
    - 粒度控制到按钮
        - 基于filter并通过 别名 进行权限的判断;
View Code

 

一、django ModelForm

什么是ModelForm

Django中Model负责操作数据库,并且具有简单的数据库验证功能;Form用于用户请求的验证,具有强悍的数据库验证功能;ModelForm是将二者合二为一,即可用于数据库操作(部分),也可用于用户请求的验证(部分)!

其实Django Admin就是利用ModelForm的功能实现的。

Form组件和ModelForm的区别

ModelForm是Django Model.py和Form组件的结合体,可以简单/快速使用 Form验证和数据库操作功能,但不如Form组件灵活,如果在使用Django做web开发过程中验证的数据和数据库字段相关(可以对表进行增、删、改操,注意 Many to many字段,也可以级联操作第3张关系表;),建议优先使用ModelForm,用起来更方便些,但是在使用ModelForm的时候慎用fields='__all__',获取数据库所有字段势必造成性能损耗;

ModelForm应用场景

我们来假设这样一个场景,假如,我们有一个类需要保存org的一些信息如下面这些字段。这个类我们叫做ChangOrgModel

course_num = models.IntegerField(default=0, verbose_name=u'课程数')
students_num = models.IntegerField(default=0, verbose_name=u'学习人数')
address = models.CharField(max_length=200, verbose_name=u'地址')

如果我们使用form我们需要怎么做呢。我们大概需要这样做。

class OrgModelForm(forms.Form):
    course_num = forms.IntegerField()
    students_num = forms.IntegerField()
    address = forms.CharField(max_length=200)

我们不难发现,我们的Form定义跟Model的定义基本没什么区别,应该加max_length的时候还是需要加,我们相当于是重写了一遍,其次,这里只有三个,并没有感觉,当有7、8甚至更多的时候,这样重复操作就很让人受不了了。所以这里我们可以使用ModelForm。

定义ModelForm类

from django import forms
from app01 import models
 
class UserModelForm(forms.ModelForm):
    class Meta:
        model = models.User     #关联的model类
        fields = "__all__"      #或('name','email','user_type')    #验证哪些字段,"__all__"表示所有字段
        exclude = None          #排除的字段
        labels = None           #提示信息
        help_texts = None       #帮助提示信息
        widgets = None          #自定义插件
        error_messages = None   #自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
        field_classes = None    #自定义字段类(也阔以自定义字段)
        localized_fields = ()   #本地化,根据settings中TIME_ZONE设置的不同时区显示时间

ModelForm验证执行的过程

Form所有的钩子ModelForm都有。

is_valid()-->self.errors-->full_clean()-->self._clean_fields() -->  clean_字段名(自定义方法)
                        self._clean_form() -->  clean(self) 
                        self._post_clean() (整体错误)

clean_字段名(自定义方法)

 

举例

新建一个项目untitled1,注意: django版本为1.11

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

修改models.py

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
from django.db import models

class Depart(models.Model):  # 部门
    caption = models.CharField(verbose_name='部门',max_length=32)

    def __str__(self):
        return self.caption
class Role(models.Model):  # 角色
    title = models.CharField(verbose_name='角色名',max_length=32)

    def __str__(self):
        return self.title

class User(models.Model):  # 用户

    name = models.CharField(verbose_name='姓名',max_length=32)
    depart = models.ForeignKey(verbose_name='部门',to='Depart',on_delete=models.CASCADE)

    gender_choices = (
        (1,''),
        (2,''),
    )
    gender = models.IntegerField(verbose_name='性别',choices=gender_choices,default=1)

    roles = models.ManyToManyField(verbose_name='角色',to='Role')
View Code

执行2个命令,生成表

python manage.py makemigrations
python manage.py migrate

 

修改admin.py,注册表

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
from django.contrib import admin
from app01 import models
# Register your models here.

admin.site.register(models.Depart)
admin.site.register(models.Role)
admin.site.register(models.User)
View Code

 

创建超级用户

python manage.py createsuperuser

登录admin后台,录入数据。这样做的目的是为了渲染页面时,不会出现空页面!

录入基本数据

新建部门

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

新建角色

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

 

 修改urls.py,增加路由

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^user/list/$', views.user_list),
]
View Code

修改views.py,增加视图

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
from django.shortcuts import render
from app01 import models
# Create your views here.
def user_list(request):
    user_queryset = models.User.objects.all()
    return render(request,'user_list.html',{'user_queryset':user_queryset})
View Code

 在templates目录下,创建文件user_list.html

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <a href="/user/add/" class="btn btn-primary">添加</a>
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>名称</th>
                    <th>性别</th>
                    <th>部门</th>
                    <th>角色</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for row in user_queryset %}
                    <tr>
                        <td>{{ row.name }}</td>
                        <td>{{ row.get_gender_display }}</td>
                        <td>{{ row.depart.caption }}</td>
                        <td>
                            {% for node in row.roles.all %}
                                <span>{{ node.title }}</span>
                            {% endfor %}
                        </td>
                        <td>
                            <a href="/user/edit/{{ row.id }}/">编辑</a>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</body>
</html>
View Code

访问页面:http://127.0.0.1:8000/user/list/

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

默认是空的。需要写添加页面

添加页面,怎么写呢?写4个input框?假如有10个表呢?累成狗!

使用ModelForm,根据models.py定义的字段,自动生成input框!

添加功能

修改urls.py,增加路由

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^user/list/$', views.user_list),
    url(r'^user/add/$', views.user_add),
]
View Code

 

修改views.py,增加添加页面,使用ModelForm

需要导入forms组件

from django import forms

完整代码如下:

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
from django.shortcuts import render,redirect
from app01 import models
from django import forms

# Create your views here.
def user_list(request):
    user_queryset = models.User.objects.all()
    return render(request,'user_list.html',{'user_queryset':user_queryset})

class UserForm(forms.ModelForm):  # 类名最好是表名+Form
    class Meta:
        model = models.User  # user表
        fields = '__all__'  # 所有字段
        # fields = ['name','depart']
        widgets = {
            # 定义name字段的输入框为text
            'name':forms.TextInput(attrs={'class':'form-control'}),
            # 定义depart字段的输入框为Select
            'depart':forms.Select(attrs={'class':'form-control'}),
            'gender':forms.Select(attrs={'class':'form-control'}),
            # 定义roles字段的输入框为多选框
            'roles':forms.SelectMultiple(attrs={'class':'form-control'}),
        }
        # 错误信息
        error_messages = {
            # name字段的错误信息
            'name':{
                # 英文的required转为中文提示
                'required':'用户名不能为空'
            }
        }

def user_add(request):
    if request.method == "GET":
        form = UserForm() # 实例化一个空的ModelForm对象
    else:
        form = UserForm(request.POST)  # 接收POST请求数据
        if form.is_valid():  # 进行验证
            print('通过验证')
            form.save()  # 将验证通过的数据插入到数据库中
            return redirect('/user/list/')  # 重定向页面
        
    return render(request,'user_add.html',{'form':form})
View Code

 在templates目录下,创建文件user_add.html

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h1>添加用户</h1>
        <form method="post" class="form-horizontal" novalidate>
            {% csrf_token %}
            {#for循环form对象#}
            {% for field in form %}
                <div class="form-group">
                    {#显示字段名#}
                    <label class="col-sm-2 control-label">{{ field.label }} </label>
                    <div class="col-sm-10">
                        {#显示字段的输入框以及错误信息,0表示取第一个错误#}
                      {{ field }} {{ field.errors.0 }}
                    </div>
                  </div>
            {% endfor %}
            <input type="submit" value="提交">
        </form>
    </div>
</body>
</html>
View Code

刷新页面,点击添加,效果如下:

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

直接提交空表单,会有错误提示

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

添加一个正常数据

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

添加成功后,页面会自动跳转

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

修改功能

修改urls.py,增加路由

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^user/list/$', views.user_list),
    url(r'^user/add/$', views.user_add),
    url(r'^user/edit/(?P<uid>\d+)/$', views.user_edit),
]
View Code

修改views.py,增加视图

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
from django.shortcuts import render,redirect
from app01 import models
from django import forms

# Create your views here.
def user_list(request):
    user_queryset = models.User.objects.all()
    return render(request,'user_list.html',{'user_queryset':user_queryset})

class UserForm(forms.ModelForm):  # 类名最好是表名+Form
    class Meta:
        model = models.User  # user表
        fields = '__all__'  # 所有字段
        # fields = ['name','depart']
        widgets = {
            # 定义name字段的输入框为text
            'name':forms.TextInput(attrs={'class':'form-control'}),
            # 定义depart字段的输入框为Select
            'depart':forms.Select(attrs={'class':'form-control'}),
            'gender':forms.Select(attrs={'class':'form-control'}),
            # 定义roles字段的输入框为多选框
            'roles':forms.SelectMultiple(attrs={'class':'form-control'}),
        }
        # 错误信息
        error_messages = {
            # name字段的错误信息
            'name':{
                # 英文的required转为中文提示
                'required':'用户名不能为空'
            }
        }

def user_add(request):
    if request.method == "GET":
        form = UserForm() # 实例化一个空的ModelForm对象
    else:
        form = UserForm(request.POST)  # 接收POST请求数据
        if form.is_valid():  # 进行验证
            print('通过验证')
            form.save()  # 将验证通过的数据插入到数据库中
            return redirect('/user/list/')  # 重定向页面

    return render(request,'user_add.html',{'form':form})

def user_edit(request,uid):
    # 查询指定id的数据
    obj = models.User.objects.filter(id=uid).first()
    if request.method =='GET':
        # 赋值instance可以使form表单是可以接受对象的数据
        form = UserForm(instance=obj)
        return render(request,'user_edit.html',{'form':form})
    else:
        # instance的参数,如果存在那么save()方法会更新这个实例,否则会创建一个新的实例
        # 由于这里是更新,如果不指定instance。那么它会新增一条数据
        # 我们这里不需要新增,必须要指定instance参数
        form = UserForm(data=request.POST,instance=obj)
        if form.is_valid():
            form.save()  # 更新一条数据
            return redirect('/user/list/')  # 重定向
        else:
            return render(request, 'user_edit.html', {'form': form})
View Code

 在templates目录下,创建文件user_edit.html

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
   <h1>编辑用户</h1>
    <form method="post" novalidate>
        {% csrf_token %}
        {% for field in form %}
            <div>{{ field.label }}{{ field }} {{ field.errors.0 }}</div>
        {% endfor %}
        <input type="submit" value="提交">
    </form>
</div>

</body>
</html>
View Code

刷新页面,点击第一条数据后面的编辑,效果如下:

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

上面会自动渲染数据,那么问题来了,它是如何渲染页面的?

看views中的一段代码

    if request.method =='GET':
        # 赋值instance可以使form表单是可以接受对象的数据
        form = UserForm(instance=obj)
        return render(request,'user_edit.html',{'form':form})

这里的obj就是,表里面的一条数据,也就是一个对象。那么执行render时,会渲染这个变量。

 

看前端页面代码

<div>{{ field.label }}{{ field }} {{ field.errors.0 }}</div>

这里的field,就是字段对象。注意:这个对象是有值的,forms组件会渲染它!

 

修改一下数据

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

点击提交,它会自动跳转

数据就更改了!

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

 

删除功能

修改views.py

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
from django.shortcuts import render,redirect,HttpResponse
from app01 import models
from django import forms

# Create your views here.
def user_list(request):
    user_queryset = models.User.objects.all()
    return render(request,'user_list.html',{'user_queryset':user_queryset})

class UserForm(forms.ModelForm):  # 类名最好是表名+Form
    class Meta:
        model = models.User  # user表
        fields = '__all__'  # 所有字段
        # fields = ['name','depart']
        widgets = {
            # 定义name字段的输入框为text
            'name':forms.TextInput(attrs={'class':'form-control'}),
            # 定义depart字段的输入框为Select
            'depart':forms.Select(attrs={'class':'form-control'}),
            'gender':forms.Select(attrs={'class':'form-control'}),
            # 定义roles字段的输入框为多选框
            'roles':forms.SelectMultiple(attrs={'class':'form-control'}),
        }
        # 错误信息
        error_messages = {
            # name字段的错误信息
            'name':{
                # 英文的required转为中文提示
                'required':'用户名不能为空'
            }
        }

def user_add(request):
    if request.method == "GET":
        form = UserForm() # 实例化一个空的ModelForm对象
    else:
        form = UserForm(request.POST)  # 接收POST请求数据
        if form.is_valid():  # 进行验证
            print('通过验证')
            form.save()  # 将验证通过的数据插入到数据库中
            return redirect('/user/list/')  # 重定向页面

    return render(request,'user_add.html',{'form':form})

def user_edit(request,uid):
    # 查询指定id的数据
    obj = models.User.objects.filter(id=uid).first()
    if request.method =='GET':
        # 赋值instance可以使form表单是可以接受对象的数据
        form = UserForm(instance=obj)
        return render(request,'user_edit.html',{'form':form})
    else:
        # instance的参数,如果存在那么save()方法会更新这个实例,否则会创建一个新的实例
        # 由于这里是更新,如果不指定instance。那么它会新增一条数据
        # 我们这里不需要新增,必须要指定instance参数
        form = UserForm(data=request.POST,instance=obj)
        if form.is_valid():
            form.save()  # 更新一条数据
            return redirect('/user/list/')  # 重定向
        else:
            return render(request, 'user_edit.html', {'form': form})


def user_del(request,uid):
    # 返回一个元组
    obj = models.User.objects.filter(id=uid).delete()
    if obj[0]:
        return redirect('/user/list/')
    else:
        return HttpResponse('删除失败')
View Code

修改user_list.html,增加删除按钮

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <a href="/user/add/" class="btn btn-primary">添加</a>
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>名称</th>
                    <th>性别</th>
                    <th>部门</th>
                    <th>角色</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for row in user_queryset %}
                    <tr>
                        <td>{{ row.name }}</td>
                        <td>{{ row.get_gender_display }}</td>
                        <td>{{ row.depart.caption }}</td>
                        <td>
                            {% for node in row.roles.all %}
                                <span>{{ node.title }}</span>
                            {% endfor %}
                        </td>
                        <td>
                            <a href="/user/edit/{{ row.id }}/">编辑</a>
                            <a href="/user/del/{{ row.id }}/">删除</a>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</body>
</html>
View Code

刷新页面,点击删除

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

删除成功后,页面刷新

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

 

二、客户管理之 编辑权限(一)

由于时间关系.略...

 

效果图

http://127.0.0.1:8000/rbac/menu/list/

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

如果是二级菜单,有淡蓝色的背景

权限编辑

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

菜单编辑

python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

完整代码如下:

 

链接:https://pan.baidu.com/s/1xYkyWFwmOZIFK4cqWWUizg 密码:zzs9

 

 

未完待续...

posted @ 2018-08-23 16:20 肖祥 阅读(...) 评论(...) 编辑 收藏