测试Demo
在进行CRM系统的开发过程中, 我突然思考一个问题: Form组件或ModelForm组件 如何实现 多对多 字段的添加..
一开始,我去谷歌上搜索这个问题. 那些阐述的不是很好. 再加上是深夜思考的, 脑袋昏沉沉的 转不过来..
第二天闲暇之余,我去Django官方文档里查 3.2的版本 搜索关键字 ModelMultipleChoiceField、CheckboxSelectMultiple
看官方文档,看到了我想知道的答案!! 于是乎,我急于快速创建一个Demo对其进行验证!
我将代码粘贴到下面,以后进行Demo测试的时候,粘贴复制简单修改下,就可以快速进行测试啦!!
1
2
3
4
5
2
3
4
5
啥样式都没加,测试下来是ok的!!
喵的,我用layui样式弄,就GG了,多选框硬是点了后取消不了.我搞不懂!! 倒腾了下,出现在样式layui-form上!
但不能不加这个啊,不然表单域里的其他表单项的样式就很难看.. 555
最后我退而求其次,这样搞! 给插件添加个属性"lay-ignore": True,这样的话这个多选框就不会应用layui的样式啦!! 曲线救国成功.
类似于这样:
class UserInfoModelForm(forms.ModelForm):
roles = forms.ModelMultipleChoiceField(
label="角色",
queryset=Role.objects.all(),
# -- 该插件的样式 对应上面截图中的第一个
widget=forms.CheckboxSelectMultiple(attrs={
"lay-ignore": True
})
# ModelMultipleChoiceField默认的插件是SelectMultiple -- 该插件的样式 对应上面截图中的第二个
# widget=forms.SelectMultiple(attrs={
# "lay-ignore": True
# })
)
class Meta:
model = models.UserInfo
fields = ['name', 'password', 'nickname', 'gender', 'phone', 'email', 'depart', 'roles']
def __init__(self, *args, **kwargs):
super(UserInfoModelForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
if name != "roles":
field.widget.attrs['class'] = 'layui-input'
field.widget.attrs['autocomplete'] = "off"
PS: ModelMultipleChoiceField 表明是啥类型的数据; CheckboxSelectMultiple表明以什么形式展现数据!!
以及ModelMultipleChoiceField在ORM表中对应怎么的字段类型,Django的官方文档都写明了的!!
★★★ Django的官方文档真的是好东西!!爱看多看.
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
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
补充一点
多对对字段 使用ModelForm内 对应的是 ModelMultipleChoiceField 表单字段对象..
ModelMultipleChoiceField默认使用的插件是 SelectMultiple..
我想在__init__方法里,通过类型判断,将ModelMultipleChoiceField表单字段对象的插件 一劳永逸的改成 CheckboxSelectMultiple..
这样, 表中的多对多字段就无需自己 在class Meta中通过 widgets 指定了..
而且通过 __init__ 的方法,还可以提取父类,其它地方也可以用.
class UserInfoModelForm(forms.ModelForm):
class Meta:
model = models.UserInfo
fields = '__all__'
widgets = {
# "roles": forms.CheckboxSelectMultiple() # 这里写可以
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
if isinstance(field, ModelMultipleChoiceField): # 默认SelectMultiple
# 这里写就得传一下choices
field.widget = forms.CheckboxSelectMultiple(choices=field.widget.choices)
我没仔细看源码 就看了下 CheckboxSelectMultiple 继承的 ChoiceWidget 里的 __init__ 有choices 属性 就实验了下
实验下来 确实可以.. 至于为什么 我的猜想是
在__init__里覆盖字段对象的插件. 像CheckboxSelectMultiple 里面是没有值的!! 要把原来的choices的值拿给它..
至于为啥 在Meta里 重写可以,可能是因为 内部源码将原先的choices值给了它 只是猜想,没有深入的探究.. 暂且告一段落!
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
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
数据库表
from django.db import models
class Role(models.Model):
""" 角色 """
title = models.CharField(verbose_name='角色名称', max_length=32)
def __str__(self):
return self.title
class UserInfo(models.Model):
"""用户表"""
name = models.CharField(verbose_name='用户名', max_length=32)
roles = models.ManyToManyField(verbose_name='拥有的所有角色', to=Role)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
路由
# -- 根路由
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include("app01.urls")),
]
# -- 在项目根目录下的 单个app urls.py
from django.urls import path, include
from . import views
urlpatterns = [
path('user/list/', views.user_list),
path('user/add/', views.user_add),
path('user/edit/<int:pk>/', views.user_edit),
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
视图函数
from django.shortcuts import render, redirect, HttpResponse
from django.urls import reverse
from . import models
from .form import UserInfoModelForm
def user_list(request):
"""列表"""
user_queryset = models.UserInfo.objects.all()
return render(request, 'user_list.html', {'users': user_queryset})
def user_add(request):
"""添加"""
if request.method == 'GET':
form = UserInfoModelForm()
return render(request, 'change.html', {'form': form})
form = UserInfoModelForm(data=request.POST)
if form.is_valid():
form.save()
return HttpResponse("add ok")
return render(request, 'change.html', {'form': form})
def user_edit(request, pk):
"""编辑"""
obj = models.UserInfo.objects.filter(id=pk).first()
if not obj:
return HttpResponse("pk error")
if request.method == 'GET':
form = UserInfoModelForm(instance=obj)
return render(request, 'change.html', {'form': form})
form = UserInfoModelForm(data=request.POST, instance=obj)
if form.is_valid():
form.save()
return HttpResponse("edit ok")
return render(request, 'change.html', {'form': form})
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
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
列表页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="layui-btn-group">
<a href="/app01/user/add/">
<button type="button">添加用户</button>
</a>
</div>
<table class="layui-table" lay-even>
<thead>
<tr>
<th>序号</th>
<th>用户名</th>
<th>选项</th>
</tr>
</thead>
<tbody>
{% for row in users %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ row.name }}</td>
<td>
<a href="/app01/user/edit/{{ row.pk }}/">
<button type="button">修改</button>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
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
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
新增/修改页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<label>{{ field.label }}</label>
<div>
{{ field }}
<span style="color: red;">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<div>
<input type="submit" value="保 存">
</div>
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
表单
from django import forms
from app01 import models
class UserInfoModelForm(forms.ModelForm):
roles = forms.ModelMultipleChoiceField(
queryset=models.Role.objects.all(),
widget=forms.CheckboxSelectMultiple
)
class Meta:
model = models.UserInfo
fields = '__all__'
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
快速添加测试数据, 脚本代码
import os
import sys
import django
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dc_stark.settings')
django.setup()
if __name__ == '__main__':
from apps.app01 import models
for i in range(1, 302):
models.UserInfo.objects.create(
name=f'dc_{i}',
age=10,
email=f'18954787{i}@qq.com',
depart_id=1,
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20