CRM系统-----学员管理系统---admin自定义开发2
7.admin的自定制开发
10.实现与django admin filter_horizontal一样的复选框
实现效果:
首先要在king_admin中添加 filter_horizontal = ["tags"]
前段的页面:
1 <form class="form-horizontal" role="form" method="post" onsubmit="return SeclectAllChosenDate()"> 2 {% csrf_token %} 3 <span style="color: red">{{ form_obj.errors }}</span> 4 {% for field in form_obj %} 5 <div class="form-group"> 6 <label class="col-sm-2 control-label" style="font-weight: normal"> 7 {% if field.field.required %} 8 <b>{{ field.label }}:</b> 9 {% else %} 10 {{ field.label }}: 11 {% endif %} 12 </label> 13 <div class="col-sm-4"> 14 {% if field.name in admin_class.filter_horizontal %} 15 <div class="col-md-5" > 16 {% get_m2m_obj_list admin_class field form_obj as m2m_obj_list %} 17 <select id="id_{{ field.name }}_from" multiple style="height: 200px!important; width: 100%;"> 18 {% for obj in m2m_obj_list %} 19 <option value="{{ obj.id }}" ondblclick="MoveElementTo(this,'id_{{ field.name }}_to','id_{{ field.name }}_from')">{{ obj }}</option> 20 {% endfor %} 21 </select> 22 </div> 23 <div class="col-md-1" style="margin-top: 50px"> 24 >> << 25 </div> 26 <div class="col-md-5"> 27 {% get_m2m_selected_obj_list form_obj field as m2m_selected_obj_list %} 28 <select tags="chosen_list" id="id_{{ field.name }}_to" name="{{ field.name }}" multiple style="height: 200px!important; width: 100%;"> 29 {% for obj in m2m_selected_obj_list %} 30 <option value="{{ obj.id }}" ondblclick="MoveElementTo(this,'id_{{ field.name }}_from','id_{{ field.name }}_to')">{{ obj }}</option> 31 {% endfor %} 32 </select> 33 </div> 34 35 {% else %} 36 {{ field }} 37 {% endif %} 38 </div> 39 </div> 40 {% endfor %} 41 42 <div> 43 <div class="col-lg-5"> 44 <button type="submit" class="btn btn-info pull-right" >保存</button> 45 </div> 46 </div> 47 </form>
实现前段的tags显示:
1 @register.simple_tag 2 def get_m2m_obj_list(admin_class,field,form_obj): 3 """返回待选标签数据""" 4 field_obj =getattr(admin_class.model,field.name) 5 all_obj_list = field_obj.rel.model.objects.all() 6 # print("-------",field_obj,all_obj_list) 7 if form_obj.instance.id: 8 selected_field_obj = getattr(form_obj.instance,field.name) 9 selected_obj_list = selected_field_obj.all() 10 # print("#########",selected_field_obj,selected_obj_list) 11 else: 12 return all_obj_list 13 14 standby_obj_list=[] 15 for obj in all_obj_list: 16 if obj not in selected_obj_list: 17 standby_obj_list.append(obj) 18 19 return standby_obj_list 20 21 @register.simple_tag 22 def get_m2m_selected_obj_list(form_obj,field): 23 """返回已选标签数据""" 24 if form_obj.instance.id: 25 field_obj =getattr(form_obj.instance,field.name) 26 # print(field_obj.all()) 27 return field_obj.all()
注意:需要在views中的table_obj_change函数里面把admin_class传到前段
实现鼠标双击后标签的移动:
1 <script> 2 function MoveElementTo (ele,target_id,new_target_id) { 3 var opt_ele = "<option value='" + $(ele).val() + "' ondblclick=MoveElementTo(this,'"+ new_target_id +"','"+ target_id +"')>" + $(ele).text() + "</option>"; 4 {#<option value="2" ondblclick="MoveElementTo(this,'id_tags_from','id_tags_to')">B类</option>#} 5 {#console.log(opt_ele);#} 6 $("#" +target_id).append(opt_ele); 7 $(ele).remove(); 8 9 } 10 11 function SeclectAllChosenDate() { 12 13 $("select[tags='chosen_list'] option").each(function () { 14 $(this).prop("selected",true); 15 16 }); 17 return true; 18 19 } 20 21 </script>
这是修改的标签的复选框设置,但是新增的复选框设置没有变化。。。
需要在table_obj_add函数把admin_class添加进去,然后设置
1 @register.simple_tag 2 def get_m2m_obj_list(admin_class,field,form_obj): 3 """返回待选标签数据""" 4 field_obj =getattr(admin_class.model,field.name) 5 all_obj_list = field_obj.rel.model.objects.all() 6 # print("-------",field_obj,all_obj_list) 7 if form_obj.instance.id: 8 selected_field_obj = getattr(form_obj.instance,field.name) 9 selected_obj_list = selected_field_obj.all() 10 # print("#########",selected_field_obj,selected_obj_list) 11 else: 12 return all_obj_list 13 14 standby_obj_list=[] 15 for obj in all_obj_list: 16 if obj not in selected_obj_list: 17 standby_obj_list.append(obj) 18 19 return standby_obj_list 20 21 @register.simple_tag 22 def get_m2m_selected_obj_list(form_obj,field): 23 """返回已选标签数据""" 24 if form_obj.instance.id: 25 field_obj =getattr(form_obj.instance,field.name) 26 # print(field_obj.all()) 27 return field_obj.all()
最终效果:
后续:没有写全选设置,后续补充
11.开发删除页面
前段写上删除按钮: 在form标签内
1 <div class="form-group"> 2 <div class="col-sm-2"> 3 4 <a class="btn btn-danger" href="{% url "table_obj_delete" app_name table_name form_obj.instance.id %}">删除</a> 5 6 </div> 7 <div class="col-sm-4"> 8 <button type="submit" class="btn btn-info pull-right" >保存</button> 9 </div> 10 </div>
url.py
re_path('(\w+)/(\w+)/(\d+)/delete/', views.table_obj_delete, name="table_obj_delete"),
views.py
1 def table_obj_delete(request,app_name,table_name,obj_id): 2 """删除页面""" 3 admin_class = king_admin.enabled_admins[app_name][table_name] 4 obj = admin_class.model.objects.get(id=obj_id) 5 6 if request.method == "POST": 7 obj.delete() 8 return redirect("/king_admin/%s/%s/"%(app_name,table_name)) 9 10 return render(request,"king_admin/table_obj_delete.html",{"obj":obj, 11 "admin_class":admin_class, 12 "app_name":app_name, 13 "table_name":table_name})
table_obj_delete.html
1 {% extends 'king_admin/table_index.html' %} 2 {% load tags %} 3 {% block container %} 4 5 <div> 6 {% display_obj_related obj %} 7 <form method="post">{% csrf_token %} 8 <input type="submit" class="btn btn-danger" value="Yes,I'm sure" /> 9 <a class="btn btn-info" href="{% url "table_objs" app_name table_name %}" >No,Take me back</a> 10 11 </form> 12 </div> 13 14 {% endblock %}
tags.py
1 @register.simple_tag 2 def recursive_related_objs_lookup(objs): 3 # model_name = objs[0]._meta.model_name 4 ul_ele = "<ul>" 5 print("----",objs) 6 for obj in objs: 7 li_ele = """<li> %s: %s</li>"""%(obj._meta.verbose_name,obj.__str__().strip("<>")) 8 ul_ele +=li_ele 9 10 #for local many to many 11 for m2m_field in obj._meta.local_many_to_many: 12 sub_ul_ele = "<ul>" 13 m2m_field_obj = getattr(obj,m2m_field.name) 14 for o in m2m_field_obj.select_related(): 15 li_ele = """<li> %s: %s</li>"""%(m2m_field.verbose_name,o.__str__().strip("<>")) 16 sub_ul_ele +=li_ele 17 sub_ul_ele += "</ul>" 18 ul_ele +=sub_ul_ele 19 20 for related_obj in obj._meta.related_objects: 21 if "ManyToManyRel" in related_obj.__repr__(): 22 if hasattr(obj, related_obj.get_accessor_name()): 23 accessor_obj = getattr(obj, related_obj.get_accessor_name()) 24 # print("accessro_obj",accessor_obj) 25 26 if hasattr(accessor_obj, "all"): # 查询出来所有的数据 27 target_objs = accessor_obj.all() 28 sub_ul_ele = "<ul style='color:red'>" 29 for o in target_objs: 30 li_ele = """<li> %s: %s</li>""" % (o._meta.verbose_name, o.__str__().strip("<>")) 31 sub_ul_ele += li_ele 32 sub_ul_ele += "</ul>" 33 ul_ele += sub_ul_ele 34 35 36 elif hasattr(obj,related_obj.get_accessor_name()): 37 accessor_obj = getattr(obj,related_obj.get_accessor_name()) 38 # print("accessro_obj",accessor_obj) 39 40 if hasattr(accessor_obj,"all"):# 查询出来所有的数据 41 target_objs = accessor_obj.all() 42 # print(target_objs) 43 else: 44 print("one to one i guess:",accessor_obj) 45 target_objs = accessor_obj 46 47 if target_objs: 48 nodes = recursive_related_objs_lookup(target_objs) 49 ul_ele +=nodes 50 ul_ele +="</ul>" 51 return ul_ele 52 53 54 @register.simple_tag 55 def display_obj_related(objs): 56 """把对象及所有相关联的数据取出来""" 57 objs = [objs,] 58 if objs: 59 model_class = objs[0]._meta.model 60 mode_name = objs[0]._meta.model_name 61 # print(model_class,mode_name) 62 return mark_safe(recursive_related_objs_lookup(objs))
效果:
12.action功能
实现action的功能,这样就可以自定制事件。例如:
前段页面: 设置checkbox全选和action的功能框
1 {% extends "king_admin/table_index.html" %} 2 {% load tags %} 3 {% block container %} 4 5 <div class="row"> 6 <div class="panel panel-info"> 7 <div class="panel-heading"> 8 <h3 class="panel-title">{% get_bulid_name admin_class %}</h3> 9 </div> 10 <div> 11 <a href="{{ request.path }}add/"> 12 <button class="btn btn-info pull-right" style="margin: 10px" >增加</button> 13 </a> 14 </div> 15 <div class="panel-body"> 16 <div class="row"> 17 <form class="" method="get"> 18 {% for filter_fields in admin_class.list_filters %} 19 <div class="col-lg-2"> 20 <span>{{ filter_fields }}</span> 21 {% render_filter_ele filter_fields admin_class filter_condtions %} 22 </div> 23 {% endfor %} 24 <button type="submit" class="btn btn-info" style="margin-top: 20px">检索</button> 25 26 <div class="row"> 27 <div class="col-lg-3"> 28 <input type="search" name="_q" class="form-control" style="margin: 10px 15px" value="{{ search_text }}" placeholder="----->>提示" /> 29 </div> 30 <button type="submit" class="btn btn-info" style="margin: 10px 15px">搜索</button> 31 <span>Search by 32 {% for column in admin_class.search_fields %} 33 {{ column }}, 34 {% endfor %} 35 </span> 36 </div> 37 38 </form> 39 </div> 40 41 <div class="row"> 42 <form onsubmit="return ActionSubmit(this)" method="POST">{% csrf_token %} 43 <span style="float: left; font-size: large;margin:10px 0px 0px 30px">Action:</span> 44 <div class="col-md-2" > 45 <select class="form-control" id="action_list" name="action"> 46 <option>-------</option> 47 {% for action in admin_class.actions %} 48 <option value="{{ action }}">{{ action }}</option> 49 {% endfor %} 50 </select> 51 </div> 52 <button type="submit" class="btn btn-info" >Go</button> 53 </form> 54 </div> 55 56 <table class="table table-hover"> 57 <thead> 58 <tr> 59 <th><input type="checkbox" onclick="CheckAllToggle(this)" /></th> 60 {% for column in admin_class.list_display %} 61 {% bulid_table_header_column column orderby_key filter_condtions %} 62 {# <th><a href="?o={{ column }}">{{ column }} </a></th>#} 63 64 {% endfor %} 65 </tr> 66 </thead> 67 <tbody> 68 {# {% get_query_sets admin_class as query_sets %}#} 69 {% for obj in query_sets %} 70 <tr> 71 <td><input tag="obj_checkbox" type="checkbox" value="{{ obj.id }}" /></td> 72 {% bulid_table_row request obj admin_class %} 73 </tr> 74 {% endfor %} 75 </tbody> 76 77 <tfoot> 78 <tr> 79 <td></td> 80 <td>总共{{ query_sets.paginator.count }}条</td> 81 </tr> 82 </tfoot> 83 </table> 84 85 <!--<div class="pagination"> 86 <span class="step-links"> 87 {% if query_sets.has_previous %} 88 <a href="?page=1">« first</a> 89 <a href="?page={{ query_sets.previous_page_number }}">previous</a> 90 {% endif %} 91 92 <span class="current"> 93 Page {{ query_sets.number }} of {{ query_sets.paginator.num_pages }}. 94 95 </span> 96 97 {% if query_sets.has_next %} 98 <a href="?page={{ query_sets.next_page_number }}">next</a> 99 <a href="?page={{ query_sets.paginator.num_pages }}">last »</a> 100 {% endif %} 101 </span> 102 </div>--> 103 104 <nav > 105 <ul class="pagination"> 106 {% if query_sets.has_previous %} 107 <li><a href="?page=1&{% page filter_condtions previous_orderby search_text %}">首页</a></li> 108 <li> 109 <a href="?page={{ query_sets.previous_page_number }}&{% page filter_condtions previous_orderby search_text %}">上一页</a></li> 110 {% endif %} 111 112 {% for loop_counter in query_sets.paginator.page_range %} 113 {% render_page_ele loop_counter query_sets filter_condtions previous_orderby search_text %} 114 {% endfor %} 115 116 {% if query_sets.has_next %} 117 <li><a href="?page={{ query_sets.next_page_number }}&{% page filter_condtions previous_orderby search_text %}">下一页</a></li> 118 119 <li><a href="?page={{ query_sets.paginator.num_pages }}&{% page filter_condtions previous_orderby search_text %}">尾页</a></li> 120 {% endif %} 121 </ul> 122 </nav> 123 124 </div> 125 </div> 126 </div> 127 128 129 <script> 130 function CheckAllToggle(ele) { 131 if ($(ele).prop("checked")){ 132 $('input[tag="obj_checkbox"]').prop("checked",true) 133 }else{ 134 $('input[tag="obj_checkbox"]').prop("checked",false) 135 } 136 } 137 138 function ActionSubmit(form_ele) { 139 var selected_ids = []; 140 $("input[tag='obj_checkbox']:checked").each(function() { 141 selected_ids.push($(this).val()); 142 }); 143 var selected_action = $("#action_list").val(); 144 console.log(selected_ids); 145 console.log(selected_action); 146 if (selected_ids.length == 0){ 147 alert("No object got selected!"); 148 149 } 150 if ( selected_action == "-------" ){ 151 alert("No action got selected!"); 152 }; 153 154 var selected_ids_ele = "<input name='selected_ids' type='hidden' value='" + selected_ids.toString() + "'>" 155 $(form_ele).append(selected_ids_ele); 156 157 return ture; 158 159 160 } 161 162 </script> 163 {% endblock %}
后端
1 def display_table_objs(request,app_name,table_name): 2 3 admin_class = king_admin.enabled_admins[app_name][table_name] 4 5 if request.method == "POST": #action is coming!! 6 # print(request.POST) 7 selected_ids = request.POST.get("selected_ids") 8 action = request.POST.get("action") 9 if selected_ids: 10 selected_objs = admin_class.model.objects.filter(id__in=selected_ids.split(",")) 11 else: 12 raise KeyError("No object selected!") 13 14 if hasattr(admin_class,action): 15 action_func = getattr(admin_class,action) 16 request._admin_action = action 17 return action_func(admin_class,request,selected_objs) 18 19 # object_list = admin_class.model.objects.all() 20 object_list,filter_condtions = table_filter(request,admin_class) #过滤后的结果 21 22 object_list = table_search(request,admin_class,object_list) #搜索数据 23 24 object_list,orderby_key = table_sort(request,admin_class,object_list) #排序后的结果 25 26 paginator = Paginator(object_list,admin_class.list_per_page) # Show 25 contacts per page 27 page = request.GET.get('page') 28 query_sets = paginator.get_page(page) 29 30 return render(request, "king_admin/table_objs.html",{"admin_class":admin_class, 31 "query_sets":query_sets, 32 "filter_condtions":filter_condtions, 33 "orderby_key":orderby_key, 34 "previous_orderby":request.GET.get("o",""), 35 "search_text":request.GET.get("_q",""),} )
1 class BaseAdmin(object): 2 list_display = [] 3 list_filters = [] 4 search_fields = [] 5 list_per_page = 20 6 ordering = None 7 filter_horizontal = [] 8 actions = ["delete_selectd_obj",] 9 10 def delete_selectd_obj(self,request,querysets): 11 # print(self,request,querysets) 12 app_name = self.model._meta.app_label 13 table_name = self.model._meta.model_name 14 print(app_name,table_name) 15 if request.POST.get("delete_confirm") == "yes": 16 querysets.delete() 17 return redirect("/king_admin/%s/%s/"%(app_name,table_name)) 18 selected_ids = ",".join([str(i.id) for i in querysets]) 19 20 return render(request,"king_admin/table_obj_delete.html",{"obj": querysets, 21 "admin_class":self, 22 "app_name":app_name, 23 "table_name":table_name, 24 "selected_ids":selected_ids, 25 "action":request._admin_action})
每页的action设置:
1 actions = ["delete_selectd_obj","test",] 2 def test(self,request,queryset): 3 print("in set") 4 test.dispaly_name = "测试"
table_objs.html
1 <select class="form-control" id="action_list" name="action"> 2 <option>-------</option> 3 {% for action in admin_class.actions %} 4 <option value="{{ action }}">{% get_action_verbose_name admin_class action %} </option> 5 {% endfor %} 6 </select>
tags.py
1 @register.simple_tag 2 def get_action_verbose_name( admin_class ,action): 3 action_func = getattr(admin_class,action) 4 return action_func.dispaly_name if hasattr(action_func,"dispaly_name") else action
13.readonly fields只读
在添加页面有些字段我们不想更改,这样就需要这些字段是只读的
在king_admin中添加
readonly_fields = []
1 def create_model_form(request,admin_class): 2 3 def __new__(cls,*args,**kwargs): 4 for field_name,field_obj in cls.base_fields.items(): 5 field_obj.widget.attrs["class"] = "form-control" 6 if field_name in admin_class.readonly_fields: 7 field_obj.widget.attrs["disabled"] = "disabled" 8 9 10 return ModelForm.__new__(cls)
但是有一个问题是,只读的字段一旦保存就出现错误,不能提交数据
所以只能用ajax把标签的diabled去掉
1 function SeclectAllChosenDate() { 2 3 $("select[tags='chosen_list'] option").each(function () { 4 $(this).prop("selected",true); 5 6 }); 7 $("form").find("[disabled]").removeAttr("disabled"); 8 return true; 9 10 }
这样就出现另外一个问题 在标签中修改数据后保存也是可以的,这就不行了。
14.kingadmin实现后端表单验证
1 def default_clean(self): 2 """给所有的form默认加一个clean验证""" 3 # print(self) 4 error_list=[] 5 for field in admin_class.readonly_fields: 6 field_val = getattr(self.instance,field) 7 field_val_form_frontend = self.cleaned_data.get(field) 8 # print(field_val,field_val_form_frontend) 9 if field_val != field_val_form_frontend: 10 error_list.append(ValidationError( 11 _('Field %(field)s is readonly, data should be %(val)s'), 12 code='invalid', 13 params={'field':field,"val":field_val}, 14 )) 15 16 17 #自定制验证信息开始 18 self.ValidationError = ValidationError 19 response = admin_class.default_form_validation(self) 20 if response: 21 error_list.append(response) 22 #自定制验证信息结束 23 24 if error_list: 25 raise ValidationError(error_list) 26 27 28 setattr(_model_form_class,"clean",default_clean)
在customer中添加自定制验证信息:
1 def default_form_validation(self): 2 """定义一个自定制的验证""" 3 consult_content = self.cleaned_data.get("content","") 4 if len(consult_content) < 15: 5 return self.ValidationError( 6 ('Field %(field)s is 咨询的内容不能小于15个字符'), 7 code='invalid', 8 params={'field':"content"},)
效果:
单个字段的验证:
在customer中king_admin:
1 def clean_name(self): 2 """定义单个字段的验证""" 3 if not self.cleaned_data["name"]: 4 self.add_error("name","name is not null")
1 def __new__(cls,*args,**kwargs): 2 for field_name,field_obj in cls.base_fields.items(): 3 field_obj.widget.attrs["class"] = "form-control" 4 if field_name in admin_class.readonly_fields: 5 field_obj.widget.attrs["disabled"] = "disabled" 6 7 8 #定义单个字段的验证 9 if hasattr(admin_class,"clean_%s"%field_name): 10 field_clean_func = getattr(admin_class,"clean_%s"%field_name) 11 setattr(cls,"clean_%s"%field_name,field_clean_func) 12 13 return ModelForm.__new__(cls)
效果:
针对m2m的tags字段的readonly验证:
1 def default_clean(self): 2 """给所有的form默认加一个clean验证""" 3 # print(self) 4 error_list=[] 5 for field in admin_class.readonly_fields: 6 field_val = getattr(self.instance,field) #val in db 7 8 if hasattr(field_val,"select_related"):#m2m 9 m2m_objs=getattr(field_val,"select_related")().select_related() 10 m2m_vals = [ i[0] for i in m2m_objs.values_list("id")] 11 set_m2m_vals = set(m2m_vals) 12 set_m2m_from_frontend =set([i.id for i in self.cleaned_data.get(field)]) 13 if set_m2m_vals != set_m2m_from_frontend: 14 self.add_error(field,"readonly field") 15 continue 16 17 18 19 field_val_form_frontend = self.cleaned_data.get(field) 20 # print(field_val,field_val_form_frontend) 21 if field_val != field_val_form_frontend: 22 error_list.append(ValidationError( 23 _('Field %(field)s is readonly, data should be %(val)s'), 24 code='invalid', 25 params={'field':field,"val":field_val}, 26 )) 27 28 29 #自定制验证信息开始 30 self.ValidationError = ValidationError 31 response = admin_class.default_form_validation(self) 32 if response: 33 error_list.append(response) 34 #自定制验证信息结束 35 36 if error_list: 37 raise ValidationError(error_list)
1 <div class="col-sm-4"> 2 {% if field.name in admin_class.filter_horizontal %} 3 <div class="col-md-5" > 4 {% get_m2m_obj_list admin_class field form_obj as m2m_obj_list %} 5 <select id="id_{{ field.name }}_from" multiple style="height: 200px!important; width: 100%;"> 6 {% if field.name in admin_class.readonly_fields %} 7 {% for obj in m2m_obj_list %} 8 <option value="{{ obj.id }}" >{{ obj }}</option> 9 {% endfor %} 10 {% else %} 11 {% for obj in m2m_obj_list %} 12 <option value="{{ obj.id }}" ondblclick="MoveElementTo(this,'id_{{ field.name }}_to','id_{{ field.name }}_from')">{{ obj }}</option> 13 {% endfor %} 14 {% endif %} 15 16 </select> 17 </div> 18 <div class="col-md-1" style="margin-top: 50px"> 19 >> << 20 </div> 21 <div class="col-md-5"> 22 {% get_m2m_selected_obj_list form_obj field as m2m_selected_obj_list %} 23 <select tags="chosen_list" id="id_{{ field.name }}_to" name="{{ field.name }}" multiple style="height: 200px!important; width: 100%;"> 24 {% if field.name in admin_class.readonly_fields %} 25 {% for obj in m2m_selected_obj_list %} 26 <option value="{{ obj.id }}" >{{ obj }}</option> 27 {% endfor %} 28 {% else %} 29 {% for obj in m2m_selected_obj_list %} 30 <option value="{{ obj.id }}" ondblclick="MoveElementTo(this,'id_{{ field.name }}_from','id_{{ field.name }}_to')">{{ obj }}</option> 31 {% endfor %} 32 {% endif %} 33 34 </select> 35 </div> 36 37 {% else %} 38 {{ field }}<span style="color: red">{{ field.errors }}</span> 39 {% endif %} 40 </div>
15.king_admin实现创建数据时不进行readonly fields验证
1 def __new__(cls,*args,**kwargs): 2 for field_name,field_obj in cls.base_fields.items(): 3 field_obj.widget.attrs["class"] = "form-control" 4 5 if not hasattr(admin_class,"is_add_form"): #这是表示add表单的时候,不需要disabled 6 if field_name in admin_class.readonly_fields: 7 field_obj.widget.attrs["disabled"] = "disabled" 8 9 10 #定义单个字段的验证 11 if hasattr(admin_class,"clean_%s"%field_name): 12 field_clean_func = getattr(admin_class,"clean_%s"%field_name) 13 setattr(cls,"clean_%s"%field_name,field_clean_func) 14 15 return ModelForm.__new__(cls) 16 17 def default_clean(self): 18 """给所有的form默认加一个clean验证""" 19 # print(self) 20 error_list=[] 21 if self.instance.id: #这是修改表单 22 for field in admin_class.readonly_fields: 23 field_val = getattr(self.instance,field) #val in db 24 25 if hasattr(field_val,"select_related"):#m2m 26 m2m_objs=getattr(field_val,"select_related")().select_related() 27 m2m_vals = [ i[0] for i in m2m_objs.values_list("id")] 28 set_m2m_vals = set(m2m_vals) 29 set_m2m_from_frontend =set([i.id for i in self.cleaned_data.get(field)]) 30 if set_m2m_vals != set_m2m_from_frontend: 31 self.add_error(field,"readonly field") 32 continue 33 34 field_val_form_frontend = self.cleaned_data.get(field) 35 # print(field_val,field_val_form_frontend) 36 if field_val != field_val_form_frontend: 37 error_list.append(ValidationError( 38 _('Field %(field)s is readonly, data should be %(val)s'), 39 code='invalid', 40 params={'field':field,"val":field_val}, 41 ))
1 def table_obj_add(request,app_name,table_name): 2 """添加页面""" 3 admin_class = king_admin.enabled_admins[app_name][table_name] 4 admin_class.is_add_form = True 5 model_class_form = create_model_form(request, admin_class) 6 7 8 if request.method == "POST": 9 form_obj = model_class_form(request.POST) 10 if form_obj.is_valid(): 11 form_obj.save() 12 return redirect(request.path.replace("/add/","/")) 13 else: 14 form_obj = model_class_form() 15 16 return render(request, "king_admin/table_obj_add.html", {"form_obj": form_obj,"admin_class":admin_class,"app_name":app_name,"table_name":table_name})
1 {% if field.name in admin_class.readonly_fields and not admin_class.is_add_form %}
16.king admin实现整张表的只读
17.King admin的动态url菜单优化
1 class Menu(models.Model): 2 """菜单表""" 3 name = models.CharField(max_length=32) 4 url_type_choice = ((0,"alias"),(1,"absolute_url")) 5 url_type = models.SmallIntegerField(choices=url_type_choice,default=0) 6 url_name = models.CharField(max_length=64) 7 8 def __str__(self): 9 return self.name 10 11 class Meta: 12 verbose_name = "菜单表" 13 verbose_name_plural = "菜单表"
1 <div class="col-sm-3 col-md-2 sidebar"> 2 <ul class="nav nav-sidebar"> 3 {% for role in request.user.userprofile.roles.all %} 4 {% for menu in role.menus.all %} 5 <li class=""><a href="{% if menu.url_type == 0 %}{% url menu.url_name %}{% else %}{{ menu.url_name }}{% endif %}">{{ menu.name }}</a></li> 6 {% endfor %} 7 {% endfor %} 8 </ul> 9 10 </div>
这样设置只能是跳转到其他的页面。
####################################################