类Xadmin插件--海豚插件
一款类似于xadmin的仿制Django内部admin管理的插件。用于系统开发。起个好听的名字。海豚插件。
from django.contrib import admin from Dolphin.service.Dolphin import site,ModelDolphin from django.shortcuts import HttpResponse,render,redirect,reverse from django.utils.safestring import mark_safe from ocas.models import * from django.forms import ModelForm # Register your models here. class CompanyConfig(ModelDolphin): # def xxx(self,obj=None,header=False): # if header: # return "显示标题(可修改)" # return 123 list_display=('id','full_name','short_name','registered_capital', 'quality_management_system_certificate',"occupational_health_and_safety_system_certificate", 'environmental_management_system_certificate','staff_headcount','staff_productor_count','staff_main_network_engineer_count', 'b_city','qualifications' ) list_display_links = ("full_name",) search_fields = ('full_name',) list_per_page=5 # def patch_init(self,request,queryset): # print(queryset) # patch_init.short_description = "批量初始化" # actions = (patch_init,) list_filter = ('b_city','qualifications','using_software',)# class StaffModelForm(ModelForm): class Meta: model=Staff fields="__all__" exclude=('is_delete',) class StaffConfig(ModelDolphin): list_display = ('id','sjdw','user','gender','native_place',) modelform_class = StaffModelForm search_fields = ('user',) list_filter = ('sjdw',) class SoftwareConfig(ModelDolphin): # list_display = ('id',) list_per_page = 5 site.register(Company,CompanyConfig) site.register(Qualification) site.register(Software,SoftwareConfig) site.register(Vendor) site.register(City) site.register(Staff,StaffConfig) site.register(UserInfo,) # print(site._registry)
#!/usr/bin/env python3 #-*- coding:utf-8 -*- ''' Administrator 2019/2/20 ''' from django.urls import include, path, re_path from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect,reverse from django.utils.safestring import mark_safe from collections import deque from django.forms import ModelForm from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger import copy from django.db.models import Q from django.db.models.fields.related import ManyToManyField,ForeignKey from django.forms.models import ModelMultipleChoiceField,ModelChoiceField from django.forms.fields import BooleanField # from Dolphin.service.dolform import ModelFormDemo class ShowList(object): def __init__(self,config,data_list,request): self.config=config self.data_list=data_list self.request=request self.page_data=self.get_query_sets() #action self.actions=self.config.new_actions def get_action_list(self): temp=[] # print(self.actions) for func_obj in self.actions: temp.append({ "name":func_obj.__name__, # "desc":func_obj.short_description # "desc":getattr(func_obj,"short_description",func_obj.__name__) "desc":func_obj.short_description if hasattr(func_obj, "short_description") else func_obj.__name__ }) return temp def get_header(self): # 构建表头数据 header_list = [] for filed in self.config.new_list_play(): if callable(filed): # header_list.append(filed.__name__.upper()) header_list.append(filed(self.config, header=True)) elif filed == "__str__": header_list.append(self.config.model._meta.model_name.upper()) else: header_list.append(self.config.model._meta.get_field(filed).verbose_name) # header_list.append(filed[:10].upper return header_list def get_body(self): new_data_list = [] for obj in self.page_data: temp = [] for field in self.config.new_list_play(): if callable(field): val = field(self.config, obj) else: try: field_obj=self.config.model._meta.get_field(field) if isinstance(field_obj,ManyToManyField): ret = getattr(obj, field).all() t = [] for m2m_obj in ret: t.append('''<span class='label label-primary'>{0}</span>'''.format(m2m_obj)) val=mark_safe('<br>'.join(t)) else: val = getattr(obj, field) if field in self.config.list_display_links: _url = self.config.reverse_url("change", obj) val = mark_safe("<a href={0}>{1}</a>".format(_url, val)) except Exception as e: val = getattr(obj, field) # if field in self.config.list_display_links: # _url = self.config.reverse_url("change", obj) # val = mark_safe("<a href={0}>{1}</a>".format(_url, val)) temp.append(val) new_data_list.append(temp) return new_data_list def get_query_sets(self): paginator = Paginator(self.data_list, self.config.list_per_page) page = self.request.GET.get('page') try: query_sets = paginator.page(page) except PageNotAnInteger: query_sets = paginator.page(1) except EmptyPage: query_sets = paginator.page(paginator.num_pages) # Paginator.num_pages:总共分页数 return query_sets def new_pagination(self): params = copy.deepcopy(self.request.GET) # {"page":"12","title_startwith":"py","id__gt":"5"} url_prefix = self.request.path#当前路径 page_html="" added_dot_ele = False for page_num in self.page_data.paginator.page_range: params["page"] = page_num if page_num<3 or page_num>self.page_data.paginator.num_pages-2 \ or abs(self.page_data.number-page_num)<=2:#代表前面两页和后面两页 ele_class="" if self.page_data.number==page_num: added_dot_ele=False ele_class="active" page_html+='''<li class="{0}"><a href="{1}?{2}">{3}</a></li>'''.format(ele_class,url_prefix,params.urlencode(),page_num) else: if not added_dot_ele: page_html += '<li><a>...</a></li>' added_dot_ele = True if self.page_data.has_previous(): params["page"] = self.page_data.previous_page_number() page_html='''<li><a href="{0}?{1}">«</a></li>'''.format(url_prefix,params.urlencode())+page_html else: page_html='''<li class="disabled" ><a href="javascript:void(0);">«</a></li>'''+page_html if self.page_data.has_next(): params["page"] =self.page_data.next_page_number() page_html+='''<li><a href="{0}?{1}">»</a></li>'''.format(url_prefix,params.urlencode()) else: page_html+='''<li class="disabled" ><a href="javascript:void(0);">»</a></li>''' return page_html def get_filter_linktags(self): link_list={} url_prefix = self.request.path # 当前路径 for filter_field in self.config.list_filter: params = copy.deepcopy(self.request.GET) cid=self.request.GET.get(filter_field,0) filter_field_obj=self.config.model._meta.get_field(filter_field) filter_field_verbose_name="以 {0}".format(filter_field_obj.verbose_name) if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField): # data_list=filter_field_obj.rel.to.objects.all() #django 1.0的用法 data_list=filter_field_obj.related_model.objects.all()#django 2.0 的用法 else: data_list=self.config.model.objects.values("pk",filter_field) temp=[] #处理 全部标签 if params.get(filter_field): del params[filter_field] active = "" else: active = "active" temp.append('''<li class={0}><a href='{1}?{2}'>全部</a></li>'''.format(active,url_prefix,params.urlencode(),)) for obj in data_list: if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField): pk=obj.pk text=str(obj) params[filter_field] = pk else: pk=obj.get("pk") text=obj.get(filter_field) params[filter_field] = text if cid==str(pk) or cid==text: active="active" else: active="" # params[filter_field]=pk a_ele = '''<li class={0}><a href='{1}?{2}'>{3}</a></li>'''.format(active,url_prefix,params.urlencode(),text) temp.append(a_ele) # link_list[filter_field_verbose_name]=temp return link_list class ModelDolphin(object): list_display = ('__str__',) list_display_links = () list_per_page=10#默认每页展示10行数据 search_fields=()#关键词搜索 actions=()#自定义字段 list_filter=()#分组查询 #用户可以自定义ModelForm modelform_class=None def __init__(self,model,site): self.model=model self.site=site def reverse_url(self,string,obj=None): app_label = self.model._meta.app_label model_name = self.model._meta.model_name if string in ["change","delete",]: _url = reverse("{app_label}_{model_name}_{action}".format(app_label=app_label, model_name=model_name, action=string), args=(obj.pk,)) return _url elif string in ["add","list"]: _url = reverse("{app_label}_{model_name}_{action}".format(app_label=app_label, model_name=model_name, action=string),) return _url else: raise AttributeError("参数有误,没有这个属性.只能使用 change/delete/add/list") def edit(self,obj=None,header=False): if header: return "编辑" app_label = self.model._meta.app_label model_name = self.model._meta.model_name # _url =reverse( "{app_label}_{model_name}_change".format(app_label=app_label, model_name=model_name),args=(obj.pk,)) _url=self.reverse_url("change",obj) # return mark_safe("<a href='{0}/change'>编辑</a>".format(obj.pk)) return mark_safe("<a href='{_url}'>编辑</a>".format(_url=_url,pk=obj.pk)) def deletes(self,obj=None,header=False): if header: return "删除" # app_label = self.model._meta.app_label # model_name = self.model._meta.model_name # _url = reverse("{app_label}_{model_name}_delete".format(app_label=app_label, model_name=model_name), # args=(obj.pk,)) _url=self.reverse_url("delete",obj) return mark_safe('''<a href='{_url}'>删除</a>'''.format(_url=_url)) def checkbox(self,obj=None,header=False): if header: return mark_safe("<input id='choice' type='checkbox'/>") return mark_safe("<input class='choice_item' type='checkbox' name='selected_pk' value='{val}'/>".format(val=obj.pk)) def new_list_play(self): temp=[] temp.append(ModelDolphin.checkbox) temp.extend(self.list_display) if not self.list_display_links: temp.append(ModelDolphin.edit) temp.append(ModelDolphin.deletes) return temp def display_all_related_objs(self,objs): #objs=[objs,] ul_ele="<ul>" for obj in objs: li_ele='''<li>{0}:{1}</li>'''.format(obj._meta.verbose_name,obj.__str__().strip("<>")) ul_ele+=li_ele for m2m_field in obj._meta.local_many_to_many: sub_ul_ele="<ul>" m2m_field_obj=getattr(obj,m2m_field.name) for o in m2m_field_obj.select_related(): li_ele = '''<li>{0}:{1}</li>'''.format(m2m_field.verbose_name, o.__str__().strip("<>")) sub_ul_ele+=li_ele sub_ul_ele+="</ul>" ul_ele+=sub_ul_ele for related_obj in obj._meta.related_objects: if 'ManyToManyRel' in related_obj.__repr__(): if hasattr(obj, related_obj.get_accessor_name()): # hassattr(customer,'enrollment_set') accessor_obj = getattr(obj, related_obj.get_accessor_name()) # print("-------ManyToManyRel", accessor_obj, related_obj.get_accessor_name()) # 上面accessor_obj 相当于 customer.enrollment_set if hasattr(accessor_obj, 'select_related'): # slect_related() == all() target_objs = accessor_obj.select_related() # .filter(**filter_coditions) # target_objs 相当于 customer.enrollment_set.all() sub_ul_ele = "<ul style='color:red'>" for o in target_objs: li_ele = '''<li> %s: %s </li>''' % (o._meta.verbose_name, o.__str__().strip("<>")) sub_ul_ele += li_ele sub_ul_ele += "</ul>" ul_ele += sub_ul_ele elif hasattr(obj, related_obj.get_accessor_name()): # hassattr(customer,'enrollment_set') accessor_obj = getattr(obj, related_obj.get_accessor_name()) # 上面accessor_obj 相当于 customer.enrollment_set if hasattr(accessor_obj, 'select_related'): # slect_related() == all() target_objs = accessor_obj.select_related() # .filter(**filter_coditions) # target_objs 相当于 customer.enrollment_set.all() else: print("one to one i guess:", accessor_obj) target_objs = accessor_obj if len(target_objs) > 0: # print("\033[31;1mdeeper layer lookup -------\033[0m") # nodes = recursive_related_objs_lookup(target_objs,model_name) nodes = self.display_all_related_objs(target_objs) ul_ele += nodes ul_ele += "</ul>" return ul_ele def batch_remove(self,request,queryset): queryset.delete() # relected_obj=self.display_all_related_objs(queryset) # # print(html_str) # return render(request,'dolphin/dol_delete.html',locals()) batch_remove.short_description = "批量删除" @property def new_actions(self): temp=[] temp.append(ModelDolphin.batch_remove) temp.extend(self.actions) return temp def set_pop_tags(self,forms): for bfield in forms: if isinstance(bfield.field,ModelMultipleChoiceField) or isinstance(bfield.field,ModelChoiceField): bfield.is_pop=True #两种方式获取类 一对多 或者多对多 的关联表 模型 # filter_field_obj = self.model._meta.get_field(bfield.name).related_model filter_field_obj=bfield.field.queryset.model # print("----",bfield.name,type(bfield.name),filter_field_obj) # print("+++++++++",filter_field_obj2) related_app_label = filter_field_obj._meta.app_label related_model_name = filter_field_obj._meta.model_name _url=reverse("{related_app_label}_{related_model_name}_add".format(related_app_label=related_app_label, related_model_name=related_model_name)) bfield.url=_url+"?pop_res_id={0}".format(bfield.auto_id ) # if isinstance(bfield.field,BooleanField): # print(bfield) # bfield.is_checkbox=True return forms def get_modelform_class(self): if not self.modelform_class: class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" # exclude = ("is_delete",) return ModelFormDemo else: return self.modelform_class def get_search_condition(self,request): key_word = request.GET.get("key_words","") self.key_word=key_word # 获取当前列表中的所有数据 search_connection = Q() search_connection.connector = "OR" for search_field in self.search_fields: search_connection.children.append(("{field}__contains".format(field=search_field), key_word)) return search_connection def get_filter_condition(self,request): filter_condition = Q() #默认且 for filter_field, val in request.GET.items(): if filter_field in self.list_filter: filter_condition.children.append((filter_field, val)) return filter_condition def list_view(self, request): if request.method=="POST": action= request.POST.get("action") selected_pk=request.POST.getlist("selected_pk") warning_flag=False if hasattr(self,action) and selected_pk: action_func=getattr(self,action) queryset = self.model.objects.filter(pk__in=selected_pk) action_func(request, queryset) else: warning_flag=True search_condition=self.get_search_condition(request)#获取 第一次关键词筛选的条件get("selected_pk") filter_condition=self.get_filter_condition(request)#获取 第二次过滤的条件 # data_list=self.model.objects.all().filter(search_connection)#第一次筛选后的所有数据 通过关键词查询 data_list = self.model.objects.get_queryset().filter(search_condition).filter(filter_condition) # 解决异常 UnorderedObjectListWarning showlist=ShowList(self,data_list,request) #拿到表名 tablename=self.model._meta.verbose_name_plural #构建一个添加数据连接 add_url=self.reverse_url("add") return render(request, "dol_list.html", locals())#可以是 locals() def add_view(self, request): ModelFormDemo=self.get_modelform_class() list_url=self.reverse_url('list') forms =self.set_pop_tags(ModelFormDemo()) if request.method=="POST": forms=self.set_pop_tags(ModelFormDemo(request.POST)) if forms.is_valid(): obj=forms.save() pop_res_id=request.GET.get("pop_res_id",None) if pop_res_id: res={"pk":obj.pk,"text":str(obj),"pop_res_id":pop_res_id} return render(request, "dol_pop.html", {"res":res}) else: return redirect(list_url) return render(request, "dol_add.html", locals()) def change_view(self, request, id): ModelFormDemo = self.get_modelform_class() edit_obj=self.model.objects.filter(pk=id).first() list_url = self.reverse_url('list') if request.method=="POST": forms=ModelFormDemo(request.POST,instance=edit_obj) if forms.is_valid(): forms.save() return redirect(list_url) return render(request, "dol_change.html", locals()) forms = ModelFormDemo(instance=edit_obj) return render(request, "dol_change.html", locals()) def delete_view(self, request, id): url_list = self.reverse_url('list') delete_obj=self.model.objects.filter(pk=id) print("####",delete_obj) if request.method=="POST": delete_obj.delete() return redirect(url_list) delete_obj_detail=self.display_all_related_objs(delete_obj) return render(request, "dol_delete.html", locals()) @property def get_urls2(self): app_label = self.model._meta.app_label model_name = self.model._meta.model_name temp = [] # print("#"*20) temp.append(re_path(r'^$', self.list_view,name="{app_label}_{model_name}_list".format(app_label=app_label,model_name=model_name))) temp.append(re_path(r'^add/$', self.add_view,name="{app_label}_{model_name}_add".format(app_label=app_label,model_name=model_name))) temp.append(re_path(r'^(\d+)/change/$', self.change_view,name="{app_label}_{model_name}_change".format(app_label=app_label,model_name=model_name))) temp.append(re_path(r'^(\d+)/delete/$', self.delete_view,name="{app_label}_{model_name}_delete".format(app_label=app_label,model_name=model_name))) return temp @property def urls2(self): return self.get_urls2, None, None class DolphinSite(object): def __init__(self,name='admin'): self._registry={} def get_urls(self): temp=[] for model,dolphin_class_obj in self._registry.items(): app_name=model._meta.app_label model_name=model._meta.model_name temp.append(path('{0}/{1}/'.format(app_name,model_name),dolphin_class_obj.urls2),) return temp @property def urls(self): return self.get_urls(),None,None def register(self, model, dolphin_class=None, **options): if not dolphin_class: dolphin_class=ModelDolphin self._registry[model] = dolphin_class(model, self)#放进字典里面 site=DolphinSite()