第八章、只讀字段處理和filter_horizontal的實現
8.1.只讀字段的處理
(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
現在增加和修改就都沒問題了
8.2.filter_horizontal的實現
默認咨詢課程后台顯示的樣子
添加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
- 在生成field的時候判斷在不在filter_horizontal里面,在的話就用我們設置的select下拉框,不在就默認的
- {% get_available_m2m_data field.name admin_class as available_m2m_data %} 后面的的 as availavle_m2m_data 是定義一個變量(里面存了自定義模板標簽里面返回的數據 return obj_list)
因為在前端不能直接循環從后台返回的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="VerificationBeforeFormSubmit()"> {% 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 ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_to')" value="{{ obj.id }}">{{ 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 }}_from')">{{ 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-2"> <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 VerificationBeforeFormSubmit() { $("select[tag] option").prop('selected',true); } </script>
現在保存的時候沒有問題,但是 添加的時候會報錯(因為添加的時候,值都是為空,獲取不到filter_horizontal的值所有報錯),下一章解決