1、Window open() 方法
http://www.runoob.com/jsref/met-win-open.html
效果图
2、admin的pop添加按钮
3、stark之pop功能
1、父相子绝
2、多对多字段如何渲染
3、isinstance判断类型
4、 一对多或多对多字段的关联模型
5、bootstrap页面自适应col-xs-8
6、model表显示的是__str__
7、构造url
8、传递数据,window.open使用
3、知识点总结
1、pop功能:
1.在一对多和多对多字段后渲染 +
2.+对应的跳转路径
3.保存添加记录同时,将原页面的对应的下拉菜单中添加该记录
2、在一对多和多对多字段后渲染+
后台需要先判断 是否是 一对多 多对多字段
modelform, 遍历
for bfield in form:
print(bfield.field) # 是每个form字段对象
所以:判断是否是一对多,多对多字段
if isinstance(bfield.field, ModelChoiceField):
bfield.is_pop = True # 为特殊字段加上特有属性,方便前端判断。 注意:ModelMultipleChoiceField(多对多) 继承 ModelChoiceField(一对多)
ModelChoiceField 继承 ChoiceField
3、+对应的跳转路径
form表单得一对多,多对多得字段对象
bfield.field.queryset.model # 相关联得模型!!<class 'app01.models.Publish'>
#(模型表得对象去找 filter_field_obj.rel.to.objects.all(),到to是模型。) 根据queryset找model queryset.model
根据model找queryset model.objects.all() related_model_name = bfield.field.queryset.model._meta.model_name # publish
related_app_label = bfield.field.queryset.model._meta.app_label # app01 反向解析url
_url = reverse("%s_%s_add" % (related_app_label, related_model_name)) # 为bfield添加自己特有的url,
# ?后面是为了,区分是top,还是正常页面打开/add/,方便之后关闭,以及赋值。
bfield.url = _url + "?pop_res_id=id_%s" % bfield.name
4、关闭弹出得top页面
top弹出,关闭页面,回到到原页面;
正常添加,跳转到list页面; 返回 res = {"pk":obj.pk, 'text':str(obj), "pop_res_id": pop_res_id}
到pop.html,方便关闭pop页面 pop.html:
关闭以及将接收得数据传到原页面上!
window.opener.pop_response("{{ res.pk}}","{{ res.text }}","{{ res.pop_res_id }}");
window.close()
5、原页面接收数据,并显示刚添加得数据(window.opener)
拿到 id, text, pop_res_id
动态创建 option append到对应得下拉框中,并选中;
var $option = $("<option>");
$option.html(text);
$option.val(pk);
$option.attr('selected','selected'); $('#'+pop_res_id).append($option)
6、前端展示:定位,(父相子绝)
<div style="position: relative">
{% if field.is_pop %}
<a onclick="pop('{{ field.url }}')"><span style="font-size: 23px;position: absolute; right: -23px; top: 25px;">+</span></a>
{% endif %}
</div>
7、补充:
ChoiceFiled
ModelChoiceFiled(ChoiceFiled) ---- select(单选) --- ForeignKey
MultiModelChoiceFiled (ModelChoiceFiled)----select(多选) --- ManyToManyField Book模型,form表单,modelform;
modelform帮我们转成了form表单;
bootstrap页面自适应: col-xs-8
4、coding代码
1、starkadmin
from django.shortcuts import HttpResponse
from stark.service import stark
from .models import *
from django.forms import ModelForm class AuthorConfig(stark.ModelStark):
list_display = ['nid', 'name', 'age']
list_display_links = ['name','age'] class BookModelForm(ModelForm):
class Meta:
model = Book
fields = "__all__" labels = {
"authors":"作者",
"publishDate":"出版日期",
} class BookConfig(stark.ModelStark):
# list_display = ['title', 'price','publish','authors']
list_display = ['__str__',]
list_display_links = ['title']
modelform_class = BookModelForm
search_fields = ['title','price']
list_filter = ['title','publish','authors'] # 一对多,多对多字段 # 批量修改数据
def patch_init(self,request,queryset):
queryset.update(price=111) # return HttpResponse("批量初始化OK") patch_init.short_description = "批量初始化" actions = [patch_init] stark.site.register(Book,BookConfig)
stark.site.register(Publish)
stark.site.register(Author,AuthorConfig)
stark.site.register(AuthorDetail) # print(stark.site._registry)
2、stark/service/stark.py
# -*- coding: utf-8 -*-
# @Time : 2018/08/17 0017 14:46
# @Author : Venicid
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect from django.utils.safestring import mark_safe
from django.urls import reverse
from django.db.models import Q # 与或非
from django.db.models.fields.related import ForeignKey
from django.db.models.fields.related import ManyToManyField
import copy from stark.utils.page import Pagination
class ShowList(object):
def __init__(self,config, data_list,request):
self.config = config # MOdelStark实例对象
self.data_list = data_list # 数据
self.request =request # 分页
data_count = self.data_list.count()
current_page = int(self.request.GET.get('page',1))
base_path = self.request.path
self.pagination = Pagination(current_page,data_count,base_path,self.request.GET,per_page_num=10, pager_count=11,) # 分页后的数据
self.page_data = self.data_list[self.pagination.start:self.pagination.end] # actions 批量初始化,字段
# self.actions = self.config.actions # [patch_init]
self.actions = self.config.new_actions() # [pathch_delete,patch_init,]
# 构建数据[{'name':'path_init',"desc":'xxxxx'}] # filter的tag如何生成的
def get_filter_linktags(self):
link_dic = {}
for filter_field in self.config.list_filter: # ['title','publish','authors']
# 1.获取url中的相关字段,后面比较
current_id = self.request.GET.get(filter_field,0)
pararms = copy.deepcopy(self.request.GET) # 2 页面生成 各种字段
filter_field_obj = self.config.model._meta.get_field(filter_field)
print(filter_field_obj,type(filter_field_obj))
# app01.Book.publish < class 'django.db.models.fields.related.ForeignKey'>
# app01.Book.authors < class 'django.db.models.fields.related.ManyToManyField'> # print('rel...',filter_field_obj.re) # <ManyToOneRel: app01.book>
# print('rel...',filter_field_obj.re.to.objects.all()) # <QuerySet [<Publish: 南京出版社>, <Publish: 北京出版社>]> # 解决步骤
# print('rel...',filter_field_obj.__dict__)
# print('rel...',filter_field_obj.remote_field)
# print('rel...',filter_field_obj.remote_field.__dict__)
# print("rel...", filter_field_obj.remote_field.model.objects.all())
# <QuerySet [<Publish: 南京出版社>, <Publish: 北京出版社>]>
# <QuerySet [<Author: jack>, <Author: tom>]> # 一对一字段or一对多字段
if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
data_list = filter_field_obj.remote_field.model.objects.all()
else:
# 普通字段
data_list = self.config.model.objects.all().values('pk',filter_field) # 3、 生成标签的href
temp = []
if pararms.get(filter_field):
del pararms[filter_field]
temp.append("<a href='?%s'>全部</a>"%pararms.urlencode())
else:
temp.append("<a href='#' class='active'>全部</a>") # 处理filter字段的href
for obj in data_list:
# print(data_list)
# 一对一,一对多字段
if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
pk = obj.pk
text = str(obj)
pararms[filter_field] = pk
else:
# 普通字段
pk = obj.get('pk')
text = obj.get(filter_field)
pararms[filter_field] = text _url = pararms.urlencode()
# print(type(current_id),type(pk),type(text))
if str(current_id) == str(pk) or str(current_id) ==str(text):
link_tag = "<a href='?%s' class='active'>%s</a>"%(_url,text)
else:
link_tag = "<a href='?%s'>%s</a>"%(_url,text)
temp.append(link_tag)
link_dic[filter_field] = temp return link_dic def get_action_list(self):
"""action批量初始化,构架数据"""
temp = []
for action in self.actions:
temp.append(
{'name':action.__name__, # class的类名
"desc":action.short_description # class的属性
}
)
return temp def get_header(self):
# 构建表头
header_list = [] # # header_list = ['选择','pk',...'操作','操作']
for field in self.config.new_list_play():
if callable(field):
# header_list.append(field.__name__)
val = field(self.config, header=True)
header_list.append(val)
else:
if field == "__str__":
header_list.append(self.config.model._meta.model_name.upper())
else:
val = self.config.model._meta.get_field(field).verbose_name # 中文名称
header_list.append(val) return header_list def get_body(self):
# 构建表单
new_data_list = []
for obj in self.page_data: #分页后的数据 # Book表模型,Author表模型
temp = []
for field in self.config.new_list_play(): # ["__str__"] ['name','age']
if callable(field): # edit() 可调用的
val = field(self.config,obj) # 直接调用edit()函数
else:
try:
field_obj = self.config.model._meta.get_field(field)
if isinstance(field_obj,ManyToManyField):
ret = getattr(obj,field).all() # 反射 obj是实例对象,name是方法
t = []
for obj in ret:
t.append(str(obj))
val = ','.join(t) else:
val = getattr(obj, field)
# list_display_links 按钮
if field in self.config.list_display_links:
_url = self.config.get_change_url(self,obj)
val = mark_safe("<a href='%s'>%s</a>"%(_url,val)) # __str__ 的步骤
except Exception as e:
val = getattr(obj,field) # <bound method Book.__str__ of <Book: php>
_url = self.config.get_change_url(obj) # /app01/book/3/change/
val = mark_safe("<a href='%s'> %s </a>" % (_url, val())) temp.append(val) new_data_list.append(temp) # print('new_data_list',new_data_list) # 构造数据 [['jack', 44], ['mark', 33]] return new_data_list class ModelStark(object):
list_display = ['__str__',] # 子类中没有,直接用父类自己的
list_display_links = []
modelform_class = []
search_fields = [] # 模糊查询字段
actions = []
list_filter = [] # 过滤字段 # 批量删除
def patch_delete(self,request,queryset):
queryset.delete() patch_delete.short_description = "Delete selected " def __init__(self,model, site):
self.model = model
self.site = site # 增删改查url
def get_add_url(self):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_add" %(app_label,model_name))
return _url def get_list_url(self):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_list" %(app_label,model_name))
return _url def get_change_url(self,obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_change" %(app_label,model_name),args=(obj.pk,))
return _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='%s'>"%obj.pk) def edit(self,obj=None, header=False):
if header:
return "操作"
# 方案1:固定url
# return mark_safe("<a href=/stark/app01/userinfo/%s/change>编辑</a>")
# 方案2:拼接url
# return mark_safe("<a href='%s/change'>编辑</a>") # 方案3:反向解析
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_change"%(app_label,model_name),args=(obj.pk,))
# print("_url",_url)
return mark_safe("<a href='%s'>编辑</a>"%_url) def deletes(self,obj=None, header=False):
if header:
return "操作"
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_delete"%(app_label,model_name),args=(obj.pk,))
return mark_safe("<a href='%s'>删除</a>"%_url) # ModelForm组件渲染 list、增、删、改页面
def get_modelform_class(self):
"""ModelForm组件"""
if not self.modelform_class:
from django.forms import ModelForm
class ModelFormDemo(ModelForm):
class Meta:
model = self.model
fields = "__all__"
return ModelFormDemo
else:
return self.modelform_class def new_list_play(self):
"""构建 ['checkbox','pk', 'name', 'age', edit,'delete']"""
temp = []
temp.append(ModelStark.checkbox)
temp.extend(self.list_display)
if not self.list_display_links:
temp.append(ModelStark.edit)
temp.append(ModelStark.deletes)
return temp # action = ['delete',...]
def new_actions(self):
temp = []
temp.append(ModelStark.patch_delete) # delete添加
temp.extend(self.actions) # 如果定义新的,就扩展到temp中
return temp '''
def list_view(self,request):
ret1 = self.model.objects.filter(title__startswith='py')
ret2 = self.model.objects.filter(price__in=[11,22,33,44,55])
ret3 = self.model.objects.filter(price__range=[10,20])
ret4 = self.model.objects.filter(title__contains='O')
ret5 = self.model.objects.filter(title__icontains='O')
return HttpResponse("过滤成功")
''' def get_search_condition(self,request):
"""search模糊查询"""
key_word = request.GET.get("q",'')
self.key_word = key_word search_connection = Q()
if key_word:
search_connection.connector = "or"
for search_field in self.search_fields:
search_connection.children.append((search_field+"__contains", key_word)) return search_connection def get_filter_condition(self,request):
"""filter过滤处理"""
filter_condition = Q() # 并且
for filter_field,val in request.GET.items():
if filter_field in self.list_filter: # list_filter = ['publish','authors']
filter_condition.children.append((filter_field,val))
return filter_condition def list_view(self, request):
if request.method == 'POST':
# print('post',request.POST)
action = request.POST.get("action") # action': ['patch_init'],
if action:
selected_pk = request.POST.getlist('selected_pk') # 'selected_pk': ['5']}>
action_func = getattr(self,action) # 反射查询 action # 取出实例方法 queryset = self.model.objects.filter(pk__in=selected_pk) # 查询
ret = action_func(request,queryset) # 执行action() # 执行实例方法()
# return ret # 获取search的Q对象
search_connection = self.get_search_condition(request) # 获取filter构建Q对象
filter_condition = self.get_filter_condition(request) # 筛选获取当前表所有数据
# data_list = self.model.objects.all().filter(search_connection)
data_list = self.model.objects.all().filter(search_connection).filter(filter_condition) #按照showlist展示页面, 构建表头,表单
show_list = ShowList(self,data_list,request) # self=ModelSTark实例对象 # 构建一个查看addurl
add_url = self.get_add_url()
return render(request,'list_view.html', locals()) def add_view(self, request):
ModelFormDemo=self.get_modelform_class()
form = ModelFormDemo() # 打印form的每个字段
from django.forms.boundfield import BoundField
from django.forms.models import ModelChoiceField
from django.forms.models import ModelMultipleChoiceField for bfield in form:
# print(type(bfield)) # <class 'django.forms.boundfield.BoundField'>
# print('field',bfield.field) # <django.forms.models.ModelChoiceField object at 0x000000F2C0DEDC50>
# print('name',bfield.name) # publish if isinstance(bfield.field,ModelChoiceField):
bfield.is_pop = True
# print(bfield.field.queryset.model) # 一对多或多对多字段的关联模型
# <class 'app01.models.Publish'> <class 'app01.models.Author'> # print(bfield.field.queryset.model._meta) # app01.author
related_app_name = bfield.field.queryset.model._meta.app_label # app01
related_model_name = bfield.field.queryset.model._meta.model_name # author _url = reverse("%s_%s_add"%(related_app_name,related_model_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() # window.open添加页面 要返回的数据
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_view.html',locals())
else:
return redirect(self.get_list_url()) return render(request, "add_view.html",locals()) def delete_view(self, request, id):
url = self.get_list_url()
if request.method == "POST":
self.model.objects.filter(pk=id).delete()
return redirect(url)
return render(request, "delete_view.html", locals()) def change_view(self, request, id):
edit_obj = self.model.objects.filter(pk=id).first() ModelFormDemo=self.get_modelform_class()
form = ModelFormDemo(instance=edit_obj)
if request.method == "POST":
form = ModelFormDemo(request.POST,instance=edit_obj)
if form.is_valid():
form.save()
return redirect(self.get_list_url()) return render(request, "change_view.html",locals()) #构造 add/delete/change
def get_urls2(self):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label temp = []
temp.append(url(r'^$', self.list_view, name='%s_%s_list'%(app_label,model_name)))
temp.append(url(r'^add/', self.add_view, name='%s_%s_add'%(app_label,model_name)))
temp.append(url(r'^(\d+)/delete/', self.delete_view, name='%s_%s_delete'%(app_label,model_name)))
temp.append(url(r'^(\d+)/change/', self.change_view, name='%s_%s_change'%(app_label,model_name))) return temp @property
def urls2(self): return self.get_urls2(), None, None class StarkSite(object):
"""site单例类"""
def __init__(self):
self._registry = {} def register(self,model, stark_class=None):
"""注册"""
if not stark_class:
stark_class = ModelStark self._registry[model] = stark_class(model,self) def get_urls(self):
"""构造一层urls app01/book"""
temp = []
for model, stark_class_obj in self._registry.items():
# print(model, 'stark_clas_obj', stark_class_obj) # 不同的model模型表
"""
<class 'app01.models.UserInfo'> ----> <app01.starkadmin.UserConfig object at 0x00000072DDB65198>
<class 'app01.models.Book'> ----> <stark.service.stark.ModelStark object at 0x00000072DDB65240>
""" app_label = model._meta.app_label # app01
model_name = model._meta.model_name # book
# temp.append(url(r'^%s/%s'%(app_label, model_name),([],None,None)))
temp.append(url(r'^%s/%s/'%(app_label, model_name),stark_class_obj.urls2))
"""
path('app01/userinfo/',UserConfig(Userinfo,site).urls2),
path('app01/book/',ModelStark(Book,site).urls2),
""" return temp @property
def urls(self): # return [],None,None
return self.get_urls(),None,None site = StarkSite() # 单例对象
3、list_View.html
{% extends 'base.html' %} {% block title %}
<title>list页面</title>
{% endblock %} {% block css %}
<style type="text/css">
.active {
color: red !important;
} .filter a {
text-decoration: none;
color: grey;
}
</style>
{% endblock %}
{% block content %}
<div class="col-md-9"> <a class="btn btn-primary" href="{{ add_url }}">添加数据</a> {% if show_list.config.search_fields %}
<form action="" method="get">
<input type="text" class="form-control" style="width: 30%;display: inline-block;margin-top: 8px"
name="q"
value="{{ show_list.config.key_word }}">
<button class="btn btn-success">submit</button>
</form>
{% endif %} <form action="" method="post">
{% csrf_token %}
<select name="action" class="form-control"
style="display: inline-block;width: 200px;margin: 8px 8px 8px 0;height: 35px;vertical-align: -2px">
<option value="">-------</option>
{% for item in show_list.get_action_list %}
<option value="{{ item.name }}">{{ item.desc }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-primary">Go</button> <table class="table table-bordered table-striped">
<tr>
{% for header in show_list.get_header %}
<th>{{ header }}</th>
{% endfor %}
</tr> {% for data in show_list.get_body %}
<tr>
{% for item in data %}
<td>{{ item }}</td>
{% endfor %} </tr>
{% endfor %}
</table>
</form> <nav>
<ul class="pagination">
{{ show_list.pagination.page_html|safe }}
</ul>
</nav>
</div>
<div class="col-md-3">
<div class="filter">
{% for filter_field, linktags in show_list.get_filter_linktags.items %} <div class="well">
<p>{{ filter_field.upper }}</p> {% for link in linktags %}
<p>{{ link|safe }}</p>
{% endfor %} </div> {% endfor %} </div>
</div>
{% endblock %} {% block javascript %}
<script type="text/javascript">
$('#choice').click(function () {
if ($(this).prop('checked')) { //对象自身属性中是否具有指定的属性
$('.choice_item').prop("checked", true)
} else {
$('.choice_item').prop("checked", false)
}
})
</script>
{% endblock %}
4、add_view.html
{% extends 'base.html' %} {% block title %}
<title>add页面</title>
{% endblock %} {% block css %}
<style type="text/css">
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;
} .error {
color: red;
}
</style>
{% endblock %} {% block header %}
<h3>add页面</h3>
{% endblock %} {% block content %}
<div class="col-md-6 col-xs-8 col-md-offset-4">
{% include 'form.html' %}
</div> {% endblock %} {% block javascript %}
<script type="text/javascript">
//打开open页面
function pop(url) {
window.open(url, '', 'width=600,height=400,left=100,right=100')
}
//open页面操作完成,返回数据
function pop_response(pk,text,id) {
//选择哪一个select标签
// option的文本值与value值
var $option =$('<option>'); //<option></option>
$option.html(text); //<option>南京出版社</option>
$option.val(pk); //<option val=111>南京出版社</option>
$option.attr("selected","selected"); //<option val=111 selected>南京出版社</option>
$('#'+id).append($option)
}
</script>
{% endblock %}
5、form.html
<form action="" method="post" novalidate>
{% for field in form %}
{% csrf_token %}
<div class="form-group" style="position:relative;">
<label for="">{{ field.label }}</label>
{{ field }}
<span class="error pull-right">{{ field.errors.0 }}</span>
{% if field.is_pop %}
<a onclick="pop('{{ field.url }}')" href=""
style="font-size: 25px;position: absolute;top: 25px;right: -25px"><span>+</span></a>
{% endif %}
</div>
{% endfor %}
<br>
<button class="btn btn-success pull-right">提交</button>
</form>
6、pop_view.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <script>
//pop页面返回到上一个页面的数据
{# window.opener.pop_response();#}
window.opener.pop_response('{{ res.pk }}','{{ res.text }}','{{ res.pop_res_id }}'); window.close();
</script> </body>
</html>