一、先看下什么django的admin的pop到底是个什么东西
其实就是这么一个东西,
a、在添加页面,在一对多和多对多的项后加了一个+号
b、点击这个加号,会弹出对应的添加 页面,在新的添加
c、添加完成后,新打开的页面自动关闭,然后把新添加数据的添加到对应的框中
二、下面我们来实现pop功能
a、知识点1,实现点击按钮,弹出一个新的窗口这个功能
首先我们写一个如下的html文件,点击这个文件,会重新打开一个窗口
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="button" value="button"> <script src="/static/jq/jquery-3.3.1.js"></script> <script> $("input").bind("click",function () { {# window.open实现弹出一个新的窗口,然后打开我们对应的的url#} window.open("/test_add/",'','width=400px,height=400px,top=100px,left=200px') }) function bar(args) { console.log(args) } </script> </body> </html>
效果如下
点击后的效果
添加窗口的html代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post"> {% csrf_token %} 书籍名称:<input type="text" name="name"> <input type="submit" value="submit"> </form> </body> </html>
然后我们看下这个form表单对应的视图函数
from app1 import models def test_add(request): if request.method == "POST": name = request.POST.get("name") models.test.objects.create( name = name ) return render(request,"pop.html",{'name':name}) else: return render(request,"test_add.html")
这里的代码我们要分析一下了、
这里为什么返回一个页面呢? 其实主要实现的效果就是添加成功,返回pop这个html文件,然后在pop的html文件中,我们会直接关闭窗口,另外我们这里进行了传参,主要是为了把当前添加的数据渲染到之前我们点击添加按钮的页面上
下面我们看下pop的html的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="/static/jq/jquery-3.3.1.js"></script> <script> {# 打开当前这个窗口对象#} window.opener.bar('{{ name }}') {# 实现关闭页面的效果#} window.close() </script> </body> </html>
这里的代码还是需要解释一下,
这个是执行打开这个窗口的那个对象中的bar函数
这个是关闭当前的页面
我们最后看下bar函数,这样我们就可以实现在子页面和父页面之间进行数据传递的效果了
三、最后我们在我们的stark组件中实现pop功能
1、首先我们需要在每个input标签后面加一个“+”号
{% for field in form_obj %} <div style="position: relative"> <label>{{ field.label }}</label>: {{ field }} <span class="pull-right">{{ field.errors.0 }}</span> {# {% if field.pop %}#} <a href="#" style="position: absolute;right: -30px;top: 30px"><span style="font-size: 20px">[+]</span></a> {# {% endif %}#} </div> {% endfor %}
页面的效果如下
2、下面我们要判断,不能在所有的input标签后面弄一个加号,必须是一对多,或者多对多的才加,我们首先看下前端渲染的form_obj的类型是什么
def add_url(self,request): model_name = self.model._meta.model_name app_name = self.model._meta.app_label # print(model_name,app_name,"============================") print(self.model) form_obj = self.get_model_form_class() form_obj = form_obj() print("form_obj",form_obj,type(form_obj)) from django.forms.boundfield import BoundField from django.forms.models import ModelMultipleChoiceField from django.forms.models import ModelChoiceField if request.method == "GET": for i in form_obj: # if isinstance(i.field,(ModelMultipleChoiceField,ModelChoiceField)): # i.pop = True print(i,type(i),sep="-------------") return render(request,"add_view.html",locals()) else: form_obj = form_obj(request.POST) if form_obj.is_valid(): form_obj.save() path = "/stark/{app}/{model}".format(app=app_name,model=model_name) return redirect(path) return render(request,"add_view.html",locals())
输出如下
我们看到所有的字段的类型都是一个类型,我们导入这个类看下这个类有什么方法
from django.forms.boundfield import BoundField
我们试着打印这个filed的属性的值和type
from django.forms.boundfield import BoundField from django.forms.models import ModelMultipleChoiceField from django.forms.models import ModelChoiceField if request.method == "GET": for i in form_obj: # if isinstance(i.field,(ModelMultipleChoiceField,ModelChoiceField)): # i.pop = True print(type(i.field),sep="-------------") return render(request,"add_view.html",locals())
结果如下
这里我们可以判断,如果字段是ModelMultipleChoiceField和ModelChoiceField的实例对象,则才加 加号
我们这里这样做,如果是ModelMultipleChoiceField和ModelChoiceField的实例对象,我们为这个实例对象赋值一个属性pop = true,然后我们在前端渲染的时候,判断有有个属性,则加 一个加号
后端的代码如下
model_name = self.model._meta.model_name app_name = self.model._meta.app_label # print(model_name,app_name,"============================") print(self.model) form_obj = self.get_model_form_class() form_obj = form_obj() print("form_obj",form_obj,type(form_obj)) from django.forms.boundfield import BoundField from django.forms.models import ModelMultipleChoiceField from django.forms.models import ModelChoiceField if request.method == "GET": for i in form_obj: if isinstance(i.field,(ModelMultipleChoiceField,ModelChoiceField)): i.pop = True print(type(i.field),sep="-------------")
前端的代码如下
<div class="col-md-4 col-lg-offset-4" > {% for field in form_obj %} <div style="position: relative"> <label>{{ field.label }}</label>: {{ field }} <span class="pull-right">{{ field.errors.0 }}</span> {% if field.pop %} <a href="#" style="position: absolute;right: -30px;top: 30px"><span style="font-size: 20px">[+]</span></a> {% endif %} </div> {% endfor %} </div>
页面效果如下
2、下面我们实现点击加号弹出对应的框
首先我们为a标签绑定一个单机的事件,点击这个按钮执行pop函数
{% if field.pop %} {# <a onclick="pop('{{ field.url }}')" style="position: absolute;right: -30px;top: 30px"><span style="font-size: 20px">[+]</span></a>#} <a onclick="pop()" style="position: absolute;right: -30px;top: 30px"><span style="font-size: 20px">[+]</span></a> {% endif %}
我们下面看下pop函数
function pop() { alert(123) }
点击按钮,看到我们的函数已经绑定上了
下面我们就是要实现点击按钮打开一个窗口的效果,首先我们需要拿到每个字段的的url
if request.method == "GET": for i in form_obj: if isinstance(i.field,(ModelMultipleChoiceField,ModelChoiceField)): i.pop = True print(dir(i.field.queryset.model),dir(i.field.queryset.model._meta),sep="-------------") args1 = i.field.queryset.model._meta.model_name args2 = i.field.queryset.model._meta.app_label _url = "/stark/{args2}/{args1}/add".format(args1=args1,args2=args2) i.url = _url return render(request,"add_view.html",locals())
先判断是否为一对多或者多对多,然后调用方法获取跨表的app的名称和表的名称
然后拼接url,然后为这个字段赋值一个url的属性
然后最后在html文件中接收一个url的参数
{% for field in form_obj %} <div style="position: relative"> <label>{{ field.label }}</label>: {{ field }} <span class="pull-right">{{ field.errors.0 }}</span> {% if field.pop %} <a onclick="pop('{{ field.url }}')" style="position: absolute;right: -30px;top: 30px"><span style="font-size: 20px">[+]</span></a> {# <a onclick="pop()" style="position: absolute;right: -30px;top: 30px"><span style="font-size: 20px">[+]</span></a>#} {% endif %} </div>
最后我们看下pop函数
function pop(url) { window.open(url,"","") }
最后我们看下页面的效果
至此,这一部分我们已经讲解完了
3、最后我们在实现这个功能,在弹出的新增的窗口添加新的数据,然后新添加的数据被添加到select标签中,且被选中的状态
首先我们要为每个点击加号点开的窗口添加一个url的参数,用来判断我这次添加的是哪张表的内容
if request.method == "GET": form_obj = form_obj() for i in form_obj: if isinstance(i.field,(ModelMultipleChoiceField,ModelChoiceField)): i.pop = True args1 = i.field.queryset.model._meta.model_name args2 = i.field.queryset.model._meta.app_label _url = "/stark/{args2}/{args1}/add".format(args1=args1,args2=args2) i.url = _url + "?pop_res_id=" + "id_" + args1 return render(request,"add_view.html",locals())
然后我们测试点击加号弹出的窗口的url
get方式处理完成,下面我们看下post的请求的处理
首先判断一下GET的参数中是否有pop_res_id这个变量,如果有的话,则走我们的pop处理的流程,如果没有的话,则走正常的添加的流程
model_form_obj = form_obj(request.POST) if model_form_obj.is_valid(): res = model_form_obj.save() pop_res_id = request.GET.get("pop_res_id",None) if pop_res_id: pop_dict = {"pk":res.id,"text_name":str(res),"field":pop_res_id} return render(request,"test_pop.html",{"pop_dict":pop_dict}) else: path = "/stark/{app}/{model}".format(app=app_name,model=model_name) return redirect(path) return render(request,"add_view.html",locals())
如果走pop的添加流程,则需要借助一个save函数的返回值,因为我们后面要添加标签,所以这3个参数是必不可少的
然后我们在看下pop的html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="/static/jq/jquery-3.3.1.js"></script> <script> {# 打开当前这个窗口对象#} window.opener.bar('{{ pop_dict.field }}','{{ pop_dict.pk }}',"{{ pop_dict.text_name }}"); {# 实现关闭页面的效果#} window.close() </script> </body> </html>
在看添加页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <style> input,select { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border: 1px solid #ccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); box-shadow: inset 0 1px 1px rgba(0,0,0,.075); -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; } </style> </head> <body> <h4>编辑页面</h4> {% include "form.html" %} <script src="/static/jq/jquery-3.3.1.js"></script> <script> function bar(field,pk,text_name) { var $option = $('<option>'); $option.html(text_name); $option.val(pk); $option.attr("selected","selected"); $("#" + field).append($option) } </script> </body> </html>
根据参数,创建option标签,且让他处于选中的状态
最后我们看下页面的效果
页面会实现自动选中的效果
至此pop的功能我们已经全部实现,还是比较复杂的