Xadmin弹出窗口
需求分析:
1.在添加页面的一对多和多对多字段后面加上+,点击+后,能显示出添加相应字段的窗口
2.提交后窗口关闭,添加的内容显示到当前页面
1.判断出当前字段是否为ForeignKey或ManyToManyField,
如果是则在后面添加+,不是则不添加。如果不进行判断,每个字段后面都会有+
{% for foo in form_obj %} <div class="form-group" style="position: relative"> <label for="">{{ foo.label }}</label> {{ foo }} <span> {{ foo.errors.0}}</span> <a><span style="font-size: 22px;color: #1b6d85;position: absolute;top: 27px;right:-23px">+</span></a> </div> {% endfor %}
1.在add函数中渲染页面时进行判断:
form_obj = DemoModelForm()
for i in form_obj: i为每个字段对象
print(type(i)) #<class 'django.forms.boundfield.BoundField'>
打印后所有的字段对象类型均为<class 'django.forms.boundfield.BoundField'> 导入 from django.forms.boundfield import BoundField查看其源码: class BoundField(object): def __init__(self, form, field, name): self.form = form self.field = field self.name = name self.html_name = form.add_prefix(name) 根据源码分析,字段类型有可能在field中,打印查看 from django.forms.boundfield import BoundField for i in form_obj: print(type(i.field)) <class 'django.forms.fields.DateField'> <class 'django.forms.fields.DecimalField'> <class 'django.forms.models.ModelChoiceField'> <class 'django.forms.models.ModelMultipleChoiceField'>
2.在后端对符合条件的对象设置属性:
from django.forms.boundfield import BoundField from django.forms.models import ModelChoiceField for boundfield in form_obj: print(type(boundfield.field)) #只要字段关系类型属于ModelChoiceField,那么就是ForeignKey或ManyToManyField #为这个字段对象设置属性,在前端页面通过这个属性值及进行相应设置 if isinstance(boundfield.field,ModelChoiceField): boundfield.is_pop = True return render(request, 'add_view.html', locals())
3.在前端通过其属性来加+
<form action="" method="post" novalidate > {% csrf_token %} {% for foo in form_obj %} <div class="form-group" style="position: relative"> <label for="">{{ foo.label }}</label> {{ foo }} <span> {{ foo.errors.0}}</span> {% if foo.is_pop %} <a "><span style="font-size: 22px;color: #1b6d85;position: absolute;top: 27px;right:-23px">+</span></a> {% endif %} </div> {% endfor %} <input type="submit" class="btn btn-default"> </form> 前端中form_obj值对应后端的form_obj,foo对应boundfield,因此foo.is_pop=boundfield.is_pop
2.点击+后,需要弹出对应的添加窗口。
1.比如,点击出版社,弹出的应该是出版设的添加窗口
点击作者,弹出的是作者的添加窗口。窗口的打开路径应该就是对应表的添加路径
< a onclick = "pop('{{ url }}')" > < span> + < / span > < / a >
2.需要根据当前这个字段,如果是publish,就找到Publish的添加数据路径,如果是authors,就找到Author表的数据添加路径
找到对应表的添加路径,就需要知道该字段连接的表的app和小写的表名。
在ModelForm中能通过字段关系得到该表的类
print(boundfield.field.queryset.model) #<class 'app01.models.Publish'
3.拿到app和表名后,通过反向解析找到publish或Author的添加路径,为了后续添加到当前操作页面,让窗口打开的网页加上参数
显示这种形式Xadmin/app01/publish/add/?pop_id=id_publish
以当前字段标签的id作为参数
boundfield.auto_id 字段对象.auto_id 自动匹配到该id
views.py 弹出窗口GET请求时的操作

def add_view(self, request): form_obj = DemoModelForm() ###判断字段的类型### # print("form_obj是:",form_obj) from django.forms.boundfield import BoundField from django.forms.models import ModelChoiceField for boundfield in form_obj: print(type(boundfield.field)) #只要字段关系类型属于ModelChoiceField,那么就是ForeignKey或ManyToManyField #为这个字段对象设置属性,在前端页面通过这个属性值及进行相应设置 if isinstance(boundfield.field,ModelChoiceField): boundfield.is_pop = True #获得该字段连接的表和所在的app print(boundfield.field.queryset.model) #<class 'app01.models.Publish'> model_class=boundfield.field.queryset.model app_name=model_class._meta.app_label model_name=model_class._meta.model_name #反响解析,得到该表添加数据的路径 url_name = "{}/{}_add".format(app_name, model_name) url = reverse(url_name) #获得该字段在页面上标签的id,自动匹配 print(boundfield.auto_id) #id_publish boundfield.url=url+'?pop_id=%s'%boundfield.auto_id return render(request, 'add_view.html', locals())
form.HTML

<form action="" method="post" novalidate > {% csrf_token %} {% for foo in form_obj %} <div class="form-group" style="position: relative"> <label for="">{{ foo.label }}</label> {{ foo }} <span> {{ foo.errors.0}}</span> #字段关系判断 {% if foo.is_pop %} <a onclick="pop('{{ foo.url }}')"><span style="font-size: 22px;color: #1b6d85;position: absolute;top: 27px;right:-23px">+</span></a> {% endif %} </div> {% endfor %} <input type="submit" class="btn btn-default"> </form> <script> function pop(url) { window.open(url,"","wdith=500,height=300,top=200,left=200") } </script>
3.add POST请求
1.弹出对应添加窗口后,添加完数据提交后,要将数据添加到对应的库中。然后将数据返回到"大窗口"的页面上
2.在使用ModelForm时,使用form_obj.save()就会自动保存,保存之后,需要做到是大窗口添加
的数据不返回,小窗口添加的数据返回,怎么判断是页面添加还是窗口添加的
3.在窗口返回路由时,为其配置了参数,可以通过判断这个参数是否存在,存在则是小窗口添加,就做返回数据的操作
页面添加的地址:Xadmin/app01/book/add/
小窗口添加的地址:/Xadmin/app01/author/add/?pop_id=id_authors
if request.method=='POST': form_obj=DemoModelForm(request.POST) if form_obj.is_valid(): obj=form_obj.save() #判断是页面添加还是窗口添加 pop_id=request.GET.get("pop_id") if pop_id: text=str(obj) #文本内容为此对象str的返回值 #返回关闭窗口的页面 return render(request,"pop.html",locals()) else: # return redirect(list_url) #不能用,相当于在当前路径后面拼接 return redirect(se
4.对关闭窗口页面的操作
在关闭窗口之前,需要将在窗口中添加的出版社名字或者作者名字传到显示页面上,POST请求将loacls()传给了
这个窗口关闭页面,在这个页面我们取到在显示页面需要的数据,如要新建对象的id显示的文本内容,添加到字段的id,
pop.html
<script> window.opener.bar("{{ obj.pk }}","{{ text }}","{{ pop_id }}"); window.close() </script>
5.在显示页面处理窗口关闭页面的数据
<select name="publish" required="" id="id_publish"> <option value=" " selected="">---------</option> <option value="2">昌平出版社</option> <option value="3">西二旗出版社</option> <option value="4">汇德出版社</option> <option value="5">天青烟雨出版社</option> <option value="6">江南出版社</option> </select>
显示页面上出版社的代码如上,我们需要将添加的数据构成这种样式,<option value="2">昌平出版社</option>添加到select标签中
<script src="/static/jquery-3.3.1.js"></script> <script> function bar(pk,text,pop_id){ var $new_tag=$("<option></option>"); $new_tag.html(text); $new_tag.attr("value",pk); {# 设置默认选中 #} $new_tag.attr("selected","selected"); 添加标签 $("#"+pop_id).append($new_tag) } </script>
由于在创建标签时,使用的时jquery对象,因此要进入jquery-3.3.1.js,如果不引入,则新建数据在页面显示失败