多选字段filter_horizontal的实现
Django Admin实现方式
Django admin后台显示效果
在admin.py中注册
class CustomerInfoAdmin(admin.ModelAdmin):
list_display = ['name', 'contact_type', 'contact', 'consultant', 'consult_content', 'status', 'created_time']
list_filter = ['source', 'consultant', 'status', 'created_time']
search_fields = ['contact', 'consultant__name', 'consult_content']
readonly_fields = ['contact', 'status']
filter_horizontal = ['consult_courses']
admin.site.register(CustomerInfo, CustomerInfoAdmin)
如果没有filter_horizontal = ['consult_courses']
,效果如下
![](https://img.haomeiwen.com/i5730845/248b61c1a2ff9f17.png)
如果有,效果如下
![](https://img.haomeiwen.com/i5730845/53d1963c9e66a8cb.png)
当数据量很大的时候,可以通过搜索批量添加。
djadmin中实现filter_horizontal
在父类和子类中添加filter_horizontal字段
首先在djadmin应用djadmin_base.py中添加filter_horizontal
空列表
# djadmin/djadmin_base.py
class BaseDjAdmin(object):
list_display = []
list_filter = []
search_fields = []
readonly_fields = []
filter_horizontal = []
修改crm应用的djadmin.py增加filter_horizontal
字段
# 注册model
class CustomerInfoAdmin(BaseDjAdmin): # 不使用object,直接继承BaseDjAdmin
list_display = ['name', 'contact_type', 'contact', 'consultant', 'consult_content', 'status', 'created_time']
list_filter = ['source', 'consultant', 'status', 'created_time']
search_fields = ['contact', 'consultant__name', 'consult_content']
readonly_fields = ['contact', 'status']
filter_horizontal = ['consult_courses']
site.register(models.CustomerInfo, CustomerInfoAdmin)
获取多对多关联表数据
修改djadmin应用的模板标签djadmin_tags.py,增加获取多对多字段关联表的所有数据
@register.simple_tag
def get_available_m2m_data(field_name, admin_class):
"""获取多对多字段关联表的所有数据"""
field_obj = admin_class.model._meta.get_field(field_name) # 获取字段对象
# consult_courses = models.ManyToManyField(Course, verbose_name='咨询课程') # 多对多关联课程
# 这是一个多对多字段,通过consult_courses对象获取到Course,也就是获取到所有咨询的课程
obj_list = field_obj.related_model.objects.all()
return obj_list
模板中展示多选框
修改djadmin应用templates/djadmin/table_edit.html,当遍历的字段在多选列表内,就使用多选的样式
{% get_available_m2m_data field.name admin_class as available_m2m_data %}
后面的的 as availavle_m2m_data
是定义一个变量(里面存了自定义模板标签里面返回的数据 return obj_list
)
因为在前端不能直接循环从后台返回的queryset数据(obj_list),所以前端在引用自定用模板标签的时候可以定义一个变量,里面就保存了所有后台传过来的数据
{% extends 'djadmin/base.html' %}
{% load djadmin_tags %}
{% block title %}
数据表编辑 - 后台管理
{% endblock %}
{% block content %}
<h1 class="page-header">{{ app_name }} - {{ model_name }} - 编辑 {{ obj }} </h1>
<!--<div>{{ form_obj }}</div>-->
<form class="form-horizontal" method="post">
{{ 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-md-5">
<select multiple class="form-control">
{% get_available_m2m_data field.name admin_class as vailable_m2m_data %}
{% for obj in vailable_m2m_data %}
<option value="{{ obj.id }}">{{ obj }}</option>
{% endfor %}
</select>
</div>
{% else %}
{{ field }}
{% endif %}
<span style="color: red">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %}
{% csrf_token %}
{% if not admin_class.form_add %}
{# 当修改数据时,才显示下方只读字段,当增加数据时,下方则不会显示 #}
{% for field in admin_class.readonly_fields %}
<label class="col-sm-2 control-label">{{ field }}</label>
<div class="col-sm-10">
<p>{% get_obj_field_val form_obj field %}</p>
</div>
{% endfor %}
{% endif %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</form>
{% endblock %}
访问 http://127.0.0.1:8000/djadmin/crm/customerinfo/1/change/ 发现,选择列表都显示在该框内了
![](https://img.haomeiwen.com/i5730845/b9bb53e078f3ba27.png)
已选择数据展示右方
现在需要在右边增加一个select框,用于存放已选中的,修改djadmin_tags.py增加一个模板标签,用来获取已选择的数据
@register.simple_tag
def get_selected_m2m_data(field_name, form_obj, admin_class):
"""获取已选中的多对多数据"""
selected_data = getattr(form_obj.instance, field_name).all()
return selected_data
在table_edit.html多选框右边增加已选中数据
<div class="col-sm-10">
{% if field.name in admin_class.filter_horizontal %}
<!--字段名在多选的字段中,使用多选框-->
<div class="col-md-5">
<select multiple class="form-control">
{% get_available_m2m_data field.name admin_class as vailable_m2m_data %}
{% for obj in vailable_m2m_data %}
<option value="{{ obj.id }}">{{ obj }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-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>
{% else %}
{{ field }}
{% endif %}
<span style="color: red">{{ field.errors.0 }}</span>
</div>
![](https://img.haomeiwen.com/i5730845/c6d5bd4271549848.png)
现在右边已经显示已选中的数据,但是左边不应该显示已被选中的数据,需要进行排序
未选择数据展示左方
修改table_edit.html,将form_obj
传给获取可选数据的模板标签中
<div class="col-md-5">
<select multiple class="form-control">
{% get_available_m2m_data field.name form_obj admin_class as vailable_m2m_data %}
{% for obj in vailable_m2m_data %}
<option value="{{ obj.id }}">{{ obj }}</option>
{% endfor %}
</select>
</div>
通过集合求差集过滤排除左边已选咨询课程数据
@register.simple_tag
def get_available_m2m_data(field_name, form_obj, admin_class):
"""获取多对多字段关联表的所有数据"""
field_obj = admin_class.model._meta.get_field(field_name) # 获取字段对象
# consult_courses = models.ManyToManyField(Course, verbose_name='咨询课程') # 多对多关联课程
# 这是一个多对多字段,通过consult_courses对象获取到Course,也就是获取到所有咨询的课程
obj_list = field_obj.related_model.objects.all()
obj_list = set(obj_list) # 所有咨询课程的集合
selected_data = set(getattr(form_obj.instance, field_name).all()) # 所以已选中课程的即可
# 通过集合求差集,得到未选中的自选课程,填充到左边多选框中
return obj_list - selected_data
![多选框](_v_images/20181120134903424_27469.png)
网友评论