DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • BBS

  • 订单平台

    • 单点知识
    • 表结构
    • 用户名登陆
    • 短信登陆
    • 菜单和权限
    • 级别管理
      • 级别列表
      • 新建级别
        • 使用Form
        • 使用ModelForm
        • 自定义widgets插件
      • 编辑级别
      • 删除级别
      • 小节
    • 客户管理
    • 分页和搜索
    • 价格策略
    • 交易中心
    • message组件
    • 我的交易列表
    • worker
    • 部署之代码同步
    • 部署之线上运行
    • 张sir的部署
  • CRM

  • flask+layui

  • django+layui

  • 供应链

  • 实战
  • 订单平台
DC
2023-06-08
目录

级别管理

完成级别管理的增删改查

# 级别列表

# -- 动态菜单
NB_MENU = {
    "ADMIN": [
        {
            "text": "用户信息1",
            "icon": "fa-calendar-plus-o",
            "children": [
                {"text": "级别管理1", "url": "/level/list/", "name": "level_list"},
            ]
        },
        {...},
    ],
    "CUSTOMER": [...]
}

# -- 权限校验
NB_PERMISSION_PUBLIC = {
    "home": {"text": "主页", 'parent': None},
    "logout": {"text": "注销", 'parent': None},
}

NB_PERMISSION = {
    "ADMIN": {
        "level_list": {"text": "级别列表", "parent": None},
        "level_add": {"text": "新建级别", "parent": "level_list"},
    },
    "CUSTOMER": {...}
}



urlpatterns = [
    path('login/', account.login, name='login'),
    path('sms/login/', account.sms_login, name='sms_login'),
    path('sms/send/', account.sms_send, name='sms_send'),
    path('logout/', account.logout, name='logout'),
    path('home/', account.home, name='home'),

    path('level/list/', level.level_list, name='level_list'),
    path('level/add/', level.level_add, name='level_add'),
]


[进行级别列表的展示]
在level_list视图函数中查询Level表中的数据,传递给level_list.html模版.
(level_list.html继承了layout.html母版) 在level_list.html中加入表格,使用模版语法-循环加载出每一条数据.
>注意:
   记得在settings配置文件中加上对应的权限!!配置后,路径导航也会自动生成!
   ★ 不仅是级别列表,编辑级别、删除级别等都要记得在配置文件中加权限.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

image-20230404112403288


# 新建级别

1> 我们通常会使用 ModelForm做表结构的CURD;
2>若只是单独的对用户请求进行校验, 或者校验后有做复杂的sql操作, 再或者Form中的字段跟数据库中的不一致,还需手动写,
通常使用Form!

image-20230404152615198

# 使用Form

简单回顾下,Form的使用!

关键代码如下:

from django import forms
from django.shortcuts import render, redirect
from django.urls import reverse

from web import models


class LevelForm(forms.Form):
    title = forms.CharField(
        label="标题",
        required=True,
        widget=forms.TextInput(
            attrs={
                "class": "form-control",
                "placeholder": "请输入标题!",
                "style": "width:10%;"
            },
        )
    )
    percent = forms.CharField(
        label="折扣",
        required=True,
        widget=forms.TextInput(
            attrs={
                "class": "form-control",
                "placeholder": "请输入折扣!",
                "style": "width:25%;"
            },
        ),
        help_text="填入0-100整数表示百分比,例如:90表示90%"
    )


def level_list(request):
    queryset = models.Level.objects.filter(active=1)
    return render(request, "level_list.html", {"queryset": queryset})


def level_add(request):
    if request.method == "GET":
        form = LevelForm()
        return render(request, "form.html", {"form": form})
    form = LevelForm(data=request.POST)
    if not form.is_valid():
        return render(request, "form.html", {"form": form})

    models.Level.objects.create(**form.cleaned_data)

    return redirect(reverse("level_list"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

代码分析如下:

在LevelForm(forms.Form)里,定义字段.
在视图函数level_add里,对LevelForm类进行实例化,传递给模版form.html 那么在模版里可以对该实例化对象进行循环展示定义的字段.
form.html中注意几点:
  1> 使用子绝父相,可以避免有错误信息,显示时影响布局.
  2> 注意label标签跟input标签的关系!!要想实现点击label标签,对应的input标签获取光标.
     需要在label标签里写上for属性,其值为input标签中id属性的值.
     <label for="{{ field.id_for_label }}"> ★★★★★★★★★★★★★★★★★★★★★★
  3> 可以加入提示信息 但我们一般会加个判断.
  4> 点击`保存`按钮,以post请求提交.若不写action,会提交到当前显示的页面所在的url!
  5> 不希望浏览器帮我们进行一些表单校验,加上novalidate.
  6> 以表单的形式提交,解决csrf的错误,加上 {% csrf_token %}
    {% extends 'layout.html' %}
    {% block content %}
        <form method="post" novalidate>
            {% for field in form %}
                {% csrf_token %}
                <div class="form-group" style="position: relative;margin-bottom: 25px">
                    <label for="{{ field.id_for_label }}">
                        {{ field.label }}
                        {% if field.help_text %}
                            <span style="font-weight: 300;color: lightgray;">
                                ({{ field.help_text }})
                            </span>
                        {% endif %}
                    </label>
                    {{ field }}
                    <span style="color: lightcoral;position: absolute;">{{ field.errors.0 }}</span>
                </div>
            {% endfor %}
    
            <button type="submit" class="btn btn-primary">保 存</button>
        </form>
    {% endblock %}
  

提交表单数据后,在视图函数level_add中注意:
  表单校验通过后,存入数据库, 若是这样写: models.Level.objects.create(**form.cleaned_data)
  得保证,LevelForm类里定义的字段名要与Level模型类里的字段名一样!且字段的个数也要相同.否则,添加失败!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 使用ModelForm

相较于Form. 使用ModelForm可以不用写字段,直接使用model里的!! 保存操作也很方便.

注意:
  基于models.Level表在LevelModelForm中自动生成字段
  1> models.Level字段的类型在forms.ModelForm中是有一一对应的!
  2> forms.ModelForm字段的属性 <--> models.Level字段的属性 
     - label 对应 verbose_name ★★★★★★★★★★★★★★★★★★★★★★★
     - required默认为True/不为空,若需要其为False/想为空,设置 null=True,blank=True  ★★★★★★★★★★★★★★★★
     - help_text 对应 help_text
  3> 当往后做添加更新等操作时,无需自己拿到用户输入的值,然后手动操作对应的数据库.
     直接执行语句, form.save()即可.
    
required  是否必填
label         注释信息
error_messages  报错信息        
initial        默认值
widget        控制标签属性和样


class LevelModelForm(forms.ModelForm):
    class Meta:
        model = models.Level
        fields = ['title', 'percent']  # fields = "__all__"
        # -- 使用widgets插件
        widgets = {
            "title": forms.TextInput(attrs={
                "class": "form-control",
                "placeholder": "请输入标题!",
                "style": "width:10%;"
            }),
            "percent": forms.TextInput(attrs={
                "class": "form-control",
                "placeholder": "请输入折扣!",
                "style": "width:25%;"
            }),
        }

def level_add(request):
    if request.method == "GET":
        # form = LevelForm()
        form = LevelModelForm()
        return render(request, "form.html", {"form": form})
    # form = LevelForm(data=request.POST)
    form = LevelModelForm(data=request.POST)
    if not form.is_valid():
        return render(request, "form.html", {"form": form})

    # models.Level.objects.create(**form.cleaned_data)  # 使用ModelForm,这样操作也是成功的!
    form.save()

    return redirect(reverse("level_list"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 自定义widgets插件

思考: 无论在使用Form和ModelForm时, 想要让页面好看, 就需要将每个字段的widgets插件中给它设置class样式等.
         字段很多的话? 也一个一个写吗?不至于不至于.

解决方案: 无论是Form还是ModelForm, 在里面写入__init__方法. (此处以ModelForm为例)

class LevelModelForm(forms.ModelForm):
    class Meta:
        model = models.Level
        fields = ['title', 'percent']  # fields = "__all__"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # self.fields是一个字典,类似于 {'title':forms.CharField()对象,"percent":forms.CharField()对象}
        for name, field in self.fields.items():
            field.widget.attrs['class'] = "form-control"
            field.widget.attrs['placeholder'] = "请输入{}".format(field.label)
            if name == "title":
                field.widget.attrs['style'] = "width:10%;"
            else:
                field.widget.attrs['style'] = "width:25%;"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

进一步优化!! 注意: super关键字根据类的MRO"类的继承关系"顺序, 从下到上去找.

class BootStrapForm:  # -- 通常会将其扔到utils目录中,用到它时导入即可.
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        for name, field in self.fields.items():
            field.widget.attrs['class'] = "form-control"
            field.widget.attrs['placeholder'] = "请输入{}".format(field.label)
            if name == "title":
                field.widget.attrs['style'] = "width:10%;"
            else:
                field.widget.attrs['style'] = "width:25%;"
            
class LevelModelForm(BootStrapForm, forms.ModelForm):
    class Meta:
        model = models.Level
        fields = ['title', 'percent']     
        
        
"""
实例化LevelModelForm类时,会执行__init__方法,LevelModelForm类中没有,就先去BootStrapForm中找
BootStrapForm类的__init__方法中 super().__init__(*args, **kwargs) 根据LevelModelForm类mro的继承链
会去forms.ModelForm中找__init__
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 编辑级别

编辑级别的页面,用的是跟“新建级别” 页面 一样的 "form.html", 但不同的一点在于, 编辑时, 要先展示出默认值!!

image-20230404164708589

展示默认值

Form或modelForm显示默认值,都可以通过 initial 属性实现.
比如:
		form = LevelForm(initial={'title': "xxx"})
    form = LevelModelForm(initial={'title': "xxx"})

若使用LevelModelForm,还有种更简便的方式.
举个例子:
    level_object = models.Level.objects.filter(id=pk).first()
    form = LevelModelForm(instance=level_object)
1
2
3
4
5
6
7
8
9

关键代码如下:

def level_edit(request, pk):
    level_object = models.Level.objects.filter(id=pk, active=1).first()
    # 获取页面,显示默认值
    if request.method == "GET":
        form = LevelModelForm(instance=level_object)
        return render(request, 'form.html', {"form": form})

    # 获取数据 + 表单校验
    # !! 此处,写了instance=level_object,后续的save操作是对其更新;不写的话,就是新增.
    form = LevelModelForm(data=request.POST, instance=level_object)
    if not form.is_valid():
        # 校验失败,让用户看错误页面
        return render(request, 'form.html', {"form": form})

    # form.instance.percent = 10
    # 校验成功,根据pk将指定的级别数据进行更新
    form.save()
    return redirect(reverse('level_list'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 删除级别

此处就简单的点击删除按钮就删除啦.. 后续再做处理.
注意: 删除级别是逻辑删除, 不是物理删除!!!

def level_delete(request, pk):
    # models.Level.objects.filter(active=1, id=pk).delete()  # 物理删除
    models.Level.objects.filter(id=pk).update(active=0)  # 逻辑删除
    return redirect(reverse('level_list'))
1
2
3
4

# 小节

Q: Form和ModelForm提供的功能有什么不一样?!
A: ModelForm 比 Form 更加适合增删改查!!

[Form]
编写字段,生成HTML标签.
在字段里可以做widget插件的设置,为对应生成的HTML标签添加class属性、style属性等.
显示出页面,用户填写表单后提交,可以做表单的验证.通过继续往下走,不通过做相应的错误提示.


[ModelForm]
★ Form有的ModelForm都有/都可以!!
  比较优秀的两点!(只有ModelForm才有的,Form没有)
     - 可以不编写字段,直接引用Model表中的字段,自动生成!
     - 保存 (新增、更新)
       <添加>
          form = LevelModelForm(data=request.POST)
          form.save()
       <更新>
          form = LevelModelForm(data=request.POST,instance=对象/表中的某条记录)
          form.save()
       <补充>
          在页面上,只显示了数据表中的一部分字段.用户填写传递到后端后,验证通过,直接save()进行新增操作数据库肯定会报错.
          因为字段不够,数据库的某些字段不能为null.
          举个例子,Level表有title、percent两个字段!新增页面展示在前端的只有title字段!
             form = LevelModelForm(data=request.POST)
             form.instance.percent = 10
             form.save()
          举个例子,Level表有title、percent两个字段!更新页面展示在前端的只有title字段!
             这样做,是ok的!!因为更新操作,不需要所有字段!  -- 只会更新title字段.
                form = LevelModelForm(data=request.POST,instance=对象/表中的某条记录)
                form.save()
             但我们也可以在更新之前,手动操作下.  -- 悄无声息的在后台暗箱操作,只要更新就将percent字段值改为10.
                form = LevelModelForm(data=request.POST,instance=对象/表中的某条记录)
                form.instance.percent = 10
                form.save()
       <注意> 多扩展的字段对save操作不会有任何影响,save操作只会拿自己需要的字段!!
             为啥需要扩写字段:可用于密码修改时,必须 密码与确认密码 一致,才能进行save操作.
                
                
                
[ModelForm]     
1> 注意(假设表中有3个字段active、title、percent):下面三者的适用场景
        - fields = ['title', 'percent']  # 指定,可以控制页面中字段展示的先后顺序!!
        - fields = "__all__"             # 所有
        - exclude = ["active"]           # 除了
2> 可以扩写字段
     举个例子,用户表只有password,往后我们创建用户时,还需要确定密码的字段.就需要我们扩写
  
     class LevelModelForm(BootStrapForm, forms.ModelForm):
         xxx = forms.CharField(label='xxx')
         class Meta:
             model = models.Level
             fields = ['title', 'xxx', 'percent', ]  # 若指定字段的方式的话,记得将扩展的字段一并写进去  
3> 可以重写字段.
     先要知道,无论是使用Form组件,自己编写的字段,还是使用ModelForm自动生成的字段
     其字段的widget插件默认是输入框TextInput
     举个例子,title我想要其在页面展示多选框,那么就得重写.
     Ps:重写的语法跟Form组件里编写字段的一样!所以,想要定义某个字段的widget插件的内容也可以通过重写来实现!

     class LevelModelForm(BootStrapForm, forms.ModelForm):
         title = forms.ChoiceField(label='xxx', choices=((1, "xxx"), (2, "xxxxxx")))
         class Meta:
             model = models.Level
             fields = ['title', 'percent']
            
-- 常见的插件 
   ★ 有两种方式定义插件!!一种是在自定义的ModelForm的自个儿编写的字段中,一种是在Meta中.
widget=forms.TextInput       # 输入框
widget=forms.PasswordInput   # 密码框
widget=forms.Select          # 下拉框
widget=forms.RadioSelect     # 单选框
widget=forms.CheckboxInput   # 多选框
widget=forms.Textarea        # 多行文本框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

菜单和权限
客户管理

← 菜单和权限 客户管理→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式