(1)kingadmin/admin_base.py
# kingadmin/admin_base.py
class BaseKingAdmin(object):
list_display = []
list_filter = []
search_fields = []
#只读
readonly_fields = []
(2)crm/kingadmin.py
(3)kingadmin/form_handle.py
(4)table_obj_change_component.html
{#kingadmin/templates/kingadmin/table_obj_change_component.html#}
{% load kingadmin_tags %}
<form class="form-horizontal" method="post">
{% csrf_token %}
{{ form_obj.errors }}
{% for field in form_obj %}
<div class="form-group">
<label class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{{ field }}
<span style="color: red;">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %}
{% for field in admin_class.readonly_fields %}
<div class="form-group">
<label class="col-sm-2 control-label">{{ field }}</label>
<div class="col-sm-10">
<p>{% get_obj_field_val form_obj field %}</p>
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-sm-offset-11 col-sm-10">
<button type="submit" class="btn btn-info">Save</button>
</div>
</div>
</form>
(5)kingadmin_tags.py
@register.simple_tag
def get_obj_field_val(form_obj,field):
'''获取只读字段的值'''
return getattr(form_obj.instance,field)
现在修改的时候没问题,但是在添加的时候会报错(提示那两个只读字段为空,因为设置成了readonly_field,添加的时候确实没有添加值)
下面解决这个报错,在前后端都添加一个判断
(4)kingadmin/views.py
(5)form_handle.py
(6)table_obj_change_component.html
现在增加和修改就都没问题了
默认咨询课程后台显示的样子
添加filter_horizontal(数据量大的时候很方便)后显示的样子(可以批量添加,还可以在里面搜索)
下面我们在kingadmin中实现这个功能
(1)kingadmin/admin_base.py
# kingadmin/admin_base.py
class BaseKingAdmin(object):
list_display = []
list_filter = []
search_fields = []
#只读
readonly_fields = []
filter_horizontal = []
(2)crm/kingadmin.py
(3)kingadmin/kingadmin_tags.py
@register.simple_tag
def get_available_m2m_data(field_name,admin_class):
'''返回的是m2m字段关联表的所有数据'''
#获取字段的对象
field_obj = admin_class.model._meta.get_field(field_name)
#consult_courses = models.ManyToManyField('Course',verbose_name='咨询课程')
#consult_courses是一个m2m,通过consult_courses对象获取到Course(也就是获取到所有咨询的课程)
obj_list = field_obj.related_model.objects.all()
return obj_list
(4)table_obj_change_component.html
因为在前端不能直接循环从后台返回的querysets数据(obj_list),所以前端在引用自定用模板标签的时候可以定义一个变量,里面就保存了所有后台传过来的数据
{#kingadmin/templates/kingadmin/table_obj_change_component.html#}
{% load kingadmin_tags %}
<form class="form-horizontal" method="post">
{% csrf_token %}
{{ form_obj.errors }}
{% for field in form_obj %}
<div class="form-group">
<label class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{% if field.name in admin_class.filter_horizontal %}
<div class="col-lg-5">
<select multiple class="form-control">
{% get_available_m2m_data field.name admin_class as available_m2m_data %}
{% for obj in available_m2m_data %}
<option value="{{ obj.id }}">{{ obj }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-5"></div>
{% else %}
{{ field }}
{% endif %}
<span style="color: red;">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %}
效果:
右边添加一个select框(存放已选中的)
kingadmin_tags.py
@register.simple_tag
def get_selected_m2m_data(field_name,form_obj,admin_class):
'''返回已选的m2m数据'''
#获取被选中的数据
selected_data = getattr(form_obj.instance,field_name).all()
return selected_data
table_obj_change_component.html
<div class="col-lg-5">
<select multiple class="form-control">
{% get_selected_m2m_data field.name form_obj admin_class as selected_m2m_data %}
{% for obj in selected_m2m_data %}
<option value="{{ obj.id }}">{{ obj }}</option>
{% endfor %}
</select>
</div>
效果:
通过集合求差集过滤出左边已选咨询课程
kingadmin_tags.py
@register.simple_tag
def get_available_m2m_data(field_name,form_obj,admin_class):
'''返回的是m2m字段关联表的所有数据'''
#获取字段的对象
field_obj = admin_class.model._meta.get_field(field_name)
#consult_courses = models.ManyToManyField('Course',verbose_name='咨询课程')
#consult_courses是一个m2m,通过consult_courses对象获取到Course(也就是获取到所有咨询的课程)
#所有咨询课程的集合
obj_list = set(field_obj.related_model.objects.all())
#选中的咨询课程集合
selected_data = set(getattr(form_obj.instance, field_name).all())
#返回的时候,集合求差集,得到未选中的咨询课程(左边)
return obj_list - selected_data
效果:
js触发事件
table_obj_change_component.html
可以通过双击咨询课程,来选择
{#kingadmin/templates/kingadmin/table_obj_change_component.html#}
{% load kingadmin_tags %}
<form class="form-horizontal" method="post" onsubmit="VerficationBeforeSubmit()">
{% csrf_token %}
{{ form_obj.errors }}
{% for field in form_obj %}
<div class="form-group">
<label class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{% if field.name in admin_class.filter_horizontal %}
<div class="col-lg-5">
<select id="id_{{ field.name }}_from" multiple class="form-control">
{% get_available_m2m_data field.name form_obj admin_class as available_m2m_data %}
{% for obj in available_m2m_data %}
<option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_to')">{{ obj }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-5">
<select tag="selected_m2m" id="id_{{ field.name }}_to" multiple class="form-control" name="{{ field.name }}">
{% get_selected_m2m_data field.name form_obj admin_class as selected_m2m_data %}
{% for obj in selected_m2m_data %}
<option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_fromm')">{{ obj }}</option>
{% endfor %}
</select>
</div>
{% else %}
{{ field }}
{% endif %}
<span style="color: red;">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %}
{% if not admin_class.form_add %} <!--如果是修改表单-->
{% for field in admin_class.readonly_fields %}
<div class="form-group">
<label class="col-sm-2 control-label">{{ field }}</label>
<div class="col-sm-10">
<p>{% get_obj_field_val form_obj field %}</p>
</div>
</div>
{% endfor %}
{% endif %}
<div class="form-group">
<div class="col-sm-offset-11 col-sm-10">
<button type="submit" class="btn btn-info">Save</button>
</div>
</div>
</form>
<script>
function MoveSelectedOption(ele,target_id){
var new_target_id = $(ele).parent().attr('id');
var option = "<option value='" +$(ele).val() +"'ondblclick=MoveSelectedOption(this,'"+new_target_id+"')>" + $(ele).text() + "</option>";
$("#"+ target_id).append(option);
$(ele).remove();
}
function VerficationBeforeSubmit() {
$("select[tag] option").prop("selected",true);
}
</script>
现在保存的时候没有问题,但是 添加的时候会报错(因为添加的时候,值都是为空,获取不到filter_horizontal的值所有报错),下一章解决