Xadmin组件构建之filter、pop
一、filter
1 app01\Xadmin.py
#定义我们自己的样式 class BookConfig(ModelXadmin): list_filter=["title","publish","authors",]
2 Xadmin\service\Xadmin.py
# 构建表头数据和构建表单数据本应该放在ModelXadminl类下面list_view视图函数中,但数据太多放在里面会会显得杂乱 # 这里定义一个类专门用来在页面展示数据,把ModelXadminl类中的self以及list_view函数中的data_list和request三个参数传过来 class show_list(object): def __init__(self, config, data_list, request): self.config = config # config代表传过来的self, self.config就是ModelXadminl类中的实例化对象self self.data_list = data_list self.request = request def get_filter_linktags(self): print("list_filter",self.config.list_filter) # list_filter ['title','publish', 'authors'] link_dic={} import copy for filter_field in self.config.list_filter: # list_filter ['title',publish', 'authors'] params = copy.deepcopy(self.request.GET) #第一次访问book页面时:<QueryDict: {}> print("111",params) cid=self.request.GET.get(filter_field,0) #默认取0, print("filter_field:",filter_field) # filter_field: publish filter_field_obj=self.config.model._meta.get_field(filter_field) #由字符串取模型表中的字段对象 print("filter_field_obj:",filter_field_obj) #filter_field_obj: app01.Book.publish print(type(filter_field_obj)) #<class 'django.db.models.fields.related.ForeignKey'> #filter_field_obj.rel 打印的是:<ManyToOneRel: app01.book> 取该字段当前的模型表 #filter_field_obj.rel.to 打印的是:<class 'app01.models.Publish'> 取该字段相关联的模型表 # print("rel...",filter_field_obj.rel.to.objects.all()) #取该字段相关联的模型表中的所有数据 #rel... <QuerySet [<Publish: 苹果出版社>, <Publish: 光子出版社>]> if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField): data_list=filter_field_obj.rel.to.objects.all() #一对一、多对多字段 else: #普通字段 data_list=self.config.model.objects.all().values("pk",filter_field) print("data_list:",data_list) # <QuerySet [{'pk': 1, 'title': '北京折叠'},……]} temp=[] # 处理 全部标签 if params.get(filter_field): del params[filter_field] temp.append("<a href='?%s'>全部</a>"%params.urlencode()) else: temp.append("<a class='active' href='#'>全部</a>") # 处理 数据标签 for obj in data_list: #循环字段关联模型表中的数据<QuerySet [<Publish: 苹果出版社>, <Publish: 光子出版社>]> if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField): pk=obj.pk text=str(obj) params[filter_field] = pk # params里面没有filter_field+"__id"键,添加键值,有键,覆盖其值 else:# data_list= [{"pk":1,"title":"go"},....] print("========") pk=obj.get("pk") text=obj.get(filter_field) params[filter_field] =text print(params) #第一次循环打印结果:<QueryDict: {'publish': [1]}> _url=params.urlencode() #把{'publish': [1]}转成publish=1 if cid==str(pk) or cid==text: link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text) # 路径前没加/,从当前访问的路径开始拼接 else: link_tag = "<a href='?%s'>%s</a>" % (_url, text) temp.append(link_tag) link_dic[filter_field]=temp print("link_dic:",link_dic) #link_dic: {'publish': ["<a class='active' href='#'>全部</a>", # "<a href='?publish__id=1'>苹果出版社</a>", "<a href='?publish__id=2'>光子出版社</a>"], # 'authors': ["<a class='active' href='#'>全部</a>", # "<a href='?authors__id=1'>xiaohei</a>", "<a href='?authors__id=2'>xiaobai</a>"]} return link_dic # 定义每张表的配置类样式 class ModelXadmin(object): list_filter=[]
def get_filter_condition(self,request): filter_condition=Q() #默认查询方式为且 for filter_field,val in request.GET.items(): #获取filter查询的键值条件 if filter_field in self.list_filter: #如果传的键在定义的列表里面,防止把page=1这样的键值对传过来 filter_condition.children.append((filter_field,val)) #注意里面接收的是一个元组 return filter_condition # (AND: ('publish', '2'), ('authors', '1')) # 查看视图 def list_view(self, request): # 这里注册用哪个样式类(默认、自定义),self就是谁 model_name = self.model._meta.model_name # 获取表名 # 获取filter构建Q对象 print("999", request.GET) # 999 <QueryDict: {'page': ['1'], 'authors': ['1'], 'publish': ['2']}> print("777", request.GET.items) # 777 <bound method MultiValueDict._iteritems of <QueryDict: {'page': ['1'], 'authors': ['1'], 'publish': ['2']}>> filter_condition=self.get_filter_condition(request) print("888",filter_condition) #888 (AND: ('authors', '1'), ('publish', '2')) 处理之后的filter查询条件 # 筛选获取当前表所有数据 data_list = self.model.objects.all().filter(search_connection).filter(filter_condition) # <QuerySet [<Book: 北京折叠>, …… ]> # 按照showlist展示数据 showlist = show_list(self, data_list, request) # 实例化一个对象showlist,并传三个参数,其中self是当前ModelXadmin的实例化对象 # 把self传给show_list类后,它里面的__init__进行接收(上面show_list函数用来接收self的参数是config) # 构建一个增加url路径 add_url = self.get_add_url() return render(request, 'list_view.html', locals())
3 list_view.html
<div class="col-md-3"> <!--如果list_filter有值说明是用户自定义的,展示除来--> {% if showlist.config.list_filter %} <div class="filter"> <h4 style="">Filter</h4> {% for filter_field,linktags in showlist.get_filter_linktags.items %} <div class="well"> <!-- class="well"为加面板--> <p>By {{ filter_field.upper }}</p> {% for link in linktags %} <p>{{ link|safe }}</p> {% endfor %} </div> {% endfor %} </div> {% endif %} </div>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <script src="/static/jquery-3.3.1.js"></script> <style> .filter a { text-decoration: none; {# 去除a标签默认样式的下划线 #} color: grey; } .active { color: blue !important; } </style> </head> <body> <h3>查看{{ model_name }}数据</h3> <div class="container"> <div class="row"> <div class="col-lg-9"> <!--添加数据--> <a href="{{ add_url }}" class="btn btn-primary">添加数据</a> <!--搜索数据开始--> {% if showlist.config.search_fields %} <form action="" class="pull-right"> <input type="text" name="q" value="{{ showlist.config.key_word }}"> <button>submit</button> </form> {% endif %} <!--搜索数据结束--> <!--添加form表单是为了在点击Go时确定发送数据的范围(不包括上面submit里面的内容)--> <form action="" method="post"> {% csrf_token %} <!--action操作开始--> <select name="action" id="" style="width: 200px;padding: 5px 8px;display: inline-block"> <option value="">---------------</option> {% for item in showlist.get_action_list %} <option value="{{ item.name }}">{{ item.desc }}</option> {% endfor %} </select> <button type="submit" class="btn btn-info">Go</button> <!--action操作结束--> <!--表格数据开始--> <table class="table table-bordered table-striped"> <thead> <tr> {% for item in showlist.get_header %} <th>{{ item }}</th> {% endfor %} </tr> </thead> <tbody> {% for data in showlist.get_body %} <tr> {% for item in data %} <td>{{ item }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> <!--表格数据结束--> <!--分页开始--> <nav class="pull-right"> <ul class="pagination"> {{ showlist.pagination.page_html|safe }} </ul> </nav> <!--分页结束--> </form> </div> <!--filter开始--> <div class="col-md-3"> <!--如果list_filter有值说明是用户自定义的,展示除来--> {% if showlist.config.list_filter %} <div class="filter"> <h4 style="">Filter</h4> {% for filter_field,linktags in showlist.get_filter_linktags.items %} <div class="well"> <!-- class="well"为加面板--> <p>By {{ filter_field.upper }}</p> {% for link in linktags %} <p>{{ link|safe }}</p> {% endfor %} </div> {% endfor %} </div> {% endif %} </div> <!--filter结束--> </div> </div> <script> //给表头复选框加上点击事件(点击表头复选框,下面框全部选中,反之全部取消) $("#choice").click(function () { if ($(this).prop("checked")) { //prop() 方法用于设置或返回被选元素的属性和值 $(".choice_item").prop("checked", true) } else { $(".choice_item").prop("checked", false) } }) </script> </body> </html>
二、pop
1 Xadmin\service\Xadmin.py
# 定义每张表的配置类样式 class ModelXadmin(object):# 增加视图 def add_view(self, request): model_name = self.model._meta.model_name # 获取表名 ModelFormDemo = self.get_modelform_class() # 获取modelform类变量 form = ModelFormDemo() # 实例化一个对象form # 循环form下每一个字段,如果为一对多或多对多字段为其添加两个属性is_pop=True、 # url = _url + "?pop_res_id=id_%s" % bfield.name #为了在form.html中识别一对多或多对多form字段,为其添加一个+号进行pop操作 for bfield in form: from django.forms.boundfield import BoundField print(bfield.field,type(bfield.field)) # 字段对象、字段类型 print("name",bfield.name) # 字段名(字符串) from django.forms.models import ModelChoiceField if isinstance(bfield.field,ModelChoiceField): #如果form字段是一对多或多对多类型 bfield.is_pop=True #为form字段加属性,以便之后取出判断是否为一对多或多对多字段 print("===>",bfield.field.queryset.model) #一对多或者多对多字段的关联模型表 #===> <class 'app01.models.Publish'> #===> <class 'app01.models.Author'> related_model_name = bfield.field.queryset.model._meta.model_name related_app_label = bfield.field.queryset.model._meta.app_label _url = reverse("%s_%s_add" % (related_app_label, related_model_name)) bfield.url = _url + "?pop_res_id=id_%s" % bfield.name #为bfield加属性 url = _url + "?pop_res_id=id_%s" % bfield.name if request.method == "POST": form = ModelFormDemo(request.POST) if form.is_valid(): obj=form.save() #返回的当前插入的那一条记录 pop_res_id=request.GET.get("pop_res_id") if pop_res_id: #说明是子窗口:执行完父窗口函数后自行关闭 res = {"pk": obj.pk, "text": str(obj), "pop_res_id": pop_res_id} return render(request, "pop.html", {"res": res}) else:#正常查看页面:返回展示页面 return redirect(self.get_list_url()) return render(request, 'add_view.html', locals())
2 templates\form.html
<div class="container"> <div class="row"> <div class="col-md-6 col-xs-8 col-md-offset-3"> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} <div style="position: relative"> <label for="">{{ field.label }}</label> {{ field }} <span class="error pull-right">{{ field.errors.0 }}</span> {% if field.is_pop %} <!--如果是一对多或多对多字段,为其后添加一个+号进行pop操作--> <!--position: absolute是为了绝对定位脱离文档流,把+号放到input标签之后--> <a onclick="pop('{{ field.url }}')" style="position: absolute;right: -30px;top: 20px"><span style="font-size: 28px">+</span></a> {% endif %} </div> {% endfor %} <button type="submit" class="btn btn-default pull-right">提交</button> </form> </div> </div> </div> <script> function pop(url) { window.open(url, "", "width=600,height=400,top=100,left=100") } </script>
pop.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="/static/jquery-3.3.1.js"></script> </head> <body> <script> window.opener.pop_response('{{ res.pk }}',"{{ res.text }}",'{{ res.pop_res_id }}'); //23 hui id_publish window.close() </script> </body> </html>
浙公网安备 33010602011771号