stark - filter、pop、总结
一、filter
效果图
知识点
1.配置得显示Filter,不配置就不显示了
list_filter = ['title','publish', 'authors']
2.前端显示
后端返回 字典
eg:{"publish":["<a href=''>全部</a>","<a href=''>南京出版社</a>","<a href=''>上海出版社</a>"]
"authors":["<a href=''>全部</a>","<a href=''>yuan</a>","<a href=''>egon</a>"]
}
{% if showlist.config.list_filter %}
<h4>Filter</h4>
{% for filter_field, linktags in showlist.get_filter_linktags.items %}
<div class="well">
<p>{{ filter_field.upper }}</p>
{% for link in linktags %}
<p>{{ link|safe }}</p>
{% endfor %}
</div>
{% endfor %}
{% endif %}
3.get_filter_linktags 返回 字典
知识:
1.根据字段 str 取到模型得字段对象
filter_field_obj = self.config.model._meta.get_field(filter_field)
model_name = self.config.model._meta.model_name # 模型名 book
app_label = self.config.model._meta.app_label # app名 app01
2.一对多,多对多,
就是 ForeignKey ManyToManyField 得对象
app01.Book.publish
<class 'django.db.models.fields.related.ForeignKey'>
app01.Book.authors
<class 'django.db.models.fields.related.ManyToManyField'>
3.根据一对多,多对多得对象 找关联得模型表,数据
print("rel...", filter_field_obj.rel.to.objects.all())
rel... <QuerySet [<Publish: 南京出版社>, <Publish: 上海出版社>, <Publish: 河北出版社>]>
rel... <QuerySet [<Author: yuan>, <Author: egon>, <Author: alex>]>
4.取数据,普通字段,和关联字段分开取
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)
5.处理全部标签时
注意:url 上面已经有了该field, 全部,就取消该field, del params[filter_field]
url 上面没有,那就没有
temp = []
if params.get(filter_field):
del params[filter_field]
temp.append("<a href='?%s'>全部</a>" % (params.urlencode()))
else:
temp.append("<a href='#' class='active'>全部</a>")
6.处理数据标签
注意:分开处理关联字段和普通字段
params = copy.deepcopy(self.request.GET)
为url加一个params
params[filter_field] = pk
params[filter_field] = text
每一次取数据都要保留 正在访问得 url 并加上正在访问得field!
_url = params.urlencode()
link_tag = "<a href='?%s'>%s</a>" % (_url, text)
正在点击得,得加上样式 active
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)
# 处理数据标签 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 _url = params.urlencode() # 序列化后得结构 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
def get_filter_linktags(self): print('list_filter:',self.config.list_filter) # ['title', 'publish', 'authors'] link_dic = {} import copy 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) print(filter_field_obj) print(type(filter_field_obj)) """ app01.Book.title <class 'django.db.models.fields.CharField'> app01.Book.publish <class 'django.db.models.fields.related.ForeignKey'> app01.Book.authors <class 'django.db.models.fields.related.ManyToManyField'> """ from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ManyToManyField # print("rel...", filter_field_obj.rel.to.objects.all()) # rel... <QuerySet [<Publish: 南京出版社>, <Publish: 上海出版社>, <Publish: 河北出版社>]> # rel... <QuerySet [<Author: yuan>, <Author: egon>, <Author: alex>]> 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) # 处理全部标签 temp = [] if params.get(filter_field): del params[filter_field] temp.append("<a href='?%s'>全部</a>" % (params.urlencode())) else: temp.append("<a href='#' class='active'>全部</a>") # 处理数据标签 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 _url = params.urlencode() # 序列化后得结构 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 return link_dic
4.Q查询 and
def list_view(self, request):
...
...
# 获取filter得Q对象
filter_condition = self.get_filter_condition(request)
# 筛选当前表得所有数据
data_list = self.model.objects.all().filter(search_connection).filter(filter_condition)
...
...
# Q对象
def get_filter_condition(self, request):
filter_condition = Q() # 默认是 and 不是 or, 根据str,查找val
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
补充:
Q查询根据字段
bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
Q查询根据str
filter_condition = Q()
filter_consition.connector = "or" # 如果不写默认是 and
filter_condition.children.append(("title","yuan"))
filter_condition.children.append(("date","2018-12-12"))
Book.object.filter(filter_condition)
app01/stark.py
# -*- coding:utf-8 -*- from stark.service import stark from .models import * from django.forms import ModelForm class BookModelForm(ModelForm): class Meta: model = Book fields = "__all__" labels = { "title": '书籍名称', "price": '价格', 'publishDate': '出版日期' } from django.shortcuts import HttpResponse class BookConfig(stark.ModelStark): list_display = ['title', 'price','publishDate'] list_display_links = ['title'] modelform_class = BookModelForm search_fields = ['title', 'price'] def patch_init(self, request, queryset): # print("queryset",queryset) queryset.update(price=123) return HttpResponse('批量初始化OK') patch_init.short_description = "批量初始化" actions = [patch_init] list_filter = ['title','publish', 'authors'] stark.site.register(Book, BookConfig) stark.site.register(Publish) stark.site.register(Author) stark.site.register(AuthorDetail)
stark/service/stark.py
from django.conf.urls import url from django.shortcuts import HttpResponse, reverse, redirect, render from django.utils.safestring import mark_safe from django.urls import reverse from django.forms import ModelForm from stark.utils.page import Pagination from django.db.models import Q class ShowList(object): def __init__(self, config, data_list, request): self.config = config self.data_list = data_list self.request = request # 分页 data_count = self.data_list.count() current_page = 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=3, pager_count=11) self.page_data = self.data_list[self.pagination.start:self.pagination.end] self.actions = self.config.new_actions() def get_filter_linktags(self): print('list_filter:',self.config.list_filter) # ['title', 'publish', 'authors'] link_dic = {} import copy 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) print(filter_field_obj) print(type(filter_field_obj)) """ app01.Book.title <class 'django.db.models.fields.CharField'> app01.Book.publish <class 'django.db.models.fields.related.ForeignKey'> app01.Book.authors <class 'django.db.models.fields.related.ManyToManyField'> """ from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ManyToManyField # print("rel...", filter_field_obj.rel.to.objects.all()) # rel... <QuerySet [<Publish: 南京出版社>, <Publish: 上海出版社>, <Publish: 河北出版社>, <Publish: 3>]> # rel... <QuerySet [<Author: yuan>, <Author: egon>, <Author: alex>]> 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) # 处理全部标签 temp = [] if params.get(filter_field): del params[filter_field] temp.append("<a href='?%s'>全部</a>" % (params.urlencode())) else: temp.append("<a href='#' class='active'>全部</a>") # 处理数据标签 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 _url = params.urlencode() # 序列化后得结构 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 return link_dic def get_action_list(self): temp = [] for action in self.actions: temp.append({ "name": action.__name__, "desc":action.short_description }) return temp def get_header(self): # 构建表头 header_list = [] # header_list = ['选择','pk',...'操作','操作'] for field in self.config .new_list_play(): if callable(field): val = field(self.config, header=True) else: if field == "__str__": val = self.config .model._meta.model_name.upper() else: # 根据str 拿字段对象 取中文 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 data in self.page_data: temp = [] for field in self.config.new_list_play(): # ['title','price'] 字符串找对象得属性 反射 # print('field:', field) if callable(field): val = field(self.config, data) else: val = getattr(data, field) if field in self.config.list_display_links: _url = self.config.get_change_url(data) val = mark_safe("<a href='%s'>%s</a>" % (_url, val)) temp.append(val) new_data_list.append(temp) # [['yuan', 12], ['alex', 18], ['egon', 22]] return new_data_list class ModelStark(object): list_display = ["__str__"] list_display_links = [] modelform_class = [] search_fields = [] actions = [] list_filter = [] def __init__(self, model, site): self.model = model self.site = site def patch_delete(self, request, queryset): queryset.delete() patch_delete.short_description = "批量删除" def edit(self, obj=None, header=False): if header: return "操作" _url = self.get_change_url(obj) return mark_safe("<a href='%s'>编辑</a>" % _url) def deletes(self, obj=None, header=False): if header: return '操作' _url = self.get_delete_url(obj) return mark_safe("<a href='%s'>删除</a>" % _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 get_modelform_class(self): if not self.modelform_class: class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" return ModelFormDemo else: return self.modelform_class def add_view(self, request): ModelFormDemo = self.get_modelform_class() form = ModelFormDemo() if request.method == 'POST': form = ModelFormDemo(request.POST) if form.is_valid(): form.save() return redirect(self.get_list_url()) return render(request, 'add_view.html', locals()) def delete_view(self, request, delete_id): url = self.get_list_url() if request.method == 'POST': self.model.objects.filter(pk=delete_id).delete() return redirect(url) return render(request, 'delete_view.html', locals()) def change_view(self, request, change_id): ModelFormDemo = self.get_modelform_class() edit_obj = self.model.objects.filter(pk=change_id).first() 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()) def new_list_play(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp def new_actions(self): temp = [] temp.append(ModelStark.patch_delete) temp.extend(self.actions) return temp def get_change_url(self, obj): model_name = self.model._meta.model_name app_lable = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_lable, model_name), args=(obj.pk,)) return _url def get_delete_url(self, obj): 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 _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_search_condition(self, request): key_words = request.GET.get('q', "") self.key_words = key_words search_connection = Q() if key_words: search_connection.connector = "or" for search_field in self.search_fields: search_connection.children.append((search_field+"__contains", key_words)) return search_connection def get_filter_condition(self, request): filter_condition = Q() # 默认是 and 不是 or 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": print("request.POST:",request.POST) # 'action': ['patch_init'], 'selected_pk': ['1', '2'] action = request.POST.get('action') selected_pk = request.POST.getlist('selected_pk') action_func = getattr(self, action) # 反射 queryset = self.model.objects.filter(pk__in=selected_pk) # 秒!!! ret = action_func(request, queryset) # return ret # 获取searchd得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).filter(filter_condition) # 展示数据 showlist = ShowList(self, data_list, request) # 构建一个查看url add_url = self.get_add_url() return render(request, 'list_view.html',locals()) def get_urls2(self): temp = [] model_name = self.model._meta.model_name app_label = self.model._meta.app_label 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))) temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property def urls2(self): return self.get_urls2(), None, None class StarkSite(object): 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): temp = [] # 模型表,配置类对象 for model, stark_class_obj in self._registry.items(): model_name = model._meta.model_name app_label = model._meta.app_label # 分发增删改查 temp.append(url(r'%s/%s/'%(app_label,model_name), stark_class_obj.urls2)) return temp @property def urls(self): return self.get_urls(), None, None site = StarkSite()
list_view.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> <script src="/static/js/jquery-1.12.4.min.js"></script> <style type="text/css"> .filter a{text-decoration: none; color: grey} .active{ color: red!important;} </style> </head> <body> <h4>数据列表</h4> <div class="container"> <div class="row"> <div class="col-md-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_words }}"><button>submit</button> </form> {% endif %} <form action="" method="post"> {% csrf_token %} <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> <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> <ul class="pagination"> {{ showlist.pagination.page_html|safe }} </ul> </nav> </form> </div> <div class="col-md-3"> {% if showlist.config.list_filter %} <h4>Filter</h4> {% for filter_field, linktags in showlist.get_filter_linktags.items %} <div class="well"> <p>{{ filter_field.upper }}</p> {% for link in linktags %} <p>{{ link|safe }}</p> {% endfor %} </div> {% endfor %} {% endif %} </div> </div> </div> <script type="text/javascript"> $('#choice').click(function () { if($(this).prop('checked')){ $('.choice_item').prop('checked',true) }else{ $('.choice_item').prop('checked',false) } }) </script> </body> </html>
二、pop
效果图
知识点
pop功能:
1.在一对多和多对多字段后渲染 +
2.+对应的跳转路径
3.保存添加记录同时,将原页面的对应的下拉菜单中添加该记录
--------------------------
知识点:
1.在一对多和多对多字段后渲染+
后台需要先判断 是否是 一对多 多对多字段
modelform, 遍历
for bfield in form:
print(bfield.field) # 是每个form字段对象
所以:判断是否是一对多,多对多字段
if isinstance(bfield.field, ModelChoiceField):
bfield.is_pop = True # 为特殊字段加上特有属性,方便前端判断。
注意:ModelMultipleChoiceField(多对多) 继承 ModelChoiceField(一对多)
ModelChoiceField 继承 ChoiceField
for bfield in form: print(type(bfield)) # 字段类型 # <class 'django.forms.boundfield.BoundField'> print('name', bfield.name) # 字段名字符串 publish authors from django.forms.boundfield import BoundField print(bfield.field) # 字段对象 # <django.forms.fields.CharField object at 0x0000014BD355EBA8> # <django.forms.models.ModelChoiceField object at 0x0000014BD355ECF8> # <django.forms.models.ModelMultipleChoiceField object at 0x0000014BD355ED68> from django.forms.models import ModelChoiceField if isinstance(bfield.field, ModelChoiceField): # 给一对多,多对多字段,对象加个属性is_pop;前端就可以判断在哪个位置加+ bfield.is_pop = True 注意: ModelMultipleChoiceField 继承 ModelChoiceField 所以:只需要判断 isinstance(bfield.field, ModelChoiceField)
2.+对应的跳转路径
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
if isinstance(bfield.field, ModelChoiceField): bfield.is_pop = True 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
3.关闭弹出得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()
# 两种情况 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()) <script type="text/javascript"> window.opener.pop_response("{{ res.pk}}","{{ res.text }}","{{ res.pop_res_id }}"); window.close() </script>
4.原页面接收数据,并显示刚添加得数据(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)
pop.html window.opener.pop_response("{{ res.pk}}","{{ res.text }}","{{ res.pop_res_id }}"); window.close()
add_view.html <script type="text/javascript"> function pop_response(pk, text,pop_res_id) { console.log(pk); console.log(text); console.log(pop_res_id); // optiond得文本值 和value var $option = $("<option>"); //<option></option> $option.html(text); $option.val(pk); $option.attr('selected','selected'); $('#'+pop_res_id).append($option) } </script>
前端展示:定位,(父相子绝) <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>
def add_view(self, request): ModelFormDemo = self.get_modelform_class() form = ModelFormDemo() for bfield in form: print('i::', type(bfield)) # 字段类型 # print('i::', bfield.field) # 字段对象 # print('name', bfield.name) # 字段名字符串 publish authors # <class 'django.forms.boundfield.BoundField'> from django.forms.boundfield import BoundField print(bfield.field) from django.forms.models import ModelChoiceField from django.forms.models import ModelMultipleChoiceField # <django.forms.fields.CharField object at 0x0000014BD355EBA8> # <django.forms.models.ModelChoiceField object at 0x0000014BD355ECF8> # <django.forms.models.ModelMultipleChoiceField object at 0x0000014BD355ED68> from django.forms.models import ModelChoiceField if isinstance(bfield.field, ModelChoiceField): bfield.is_pop = True 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 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())
补充:
ChoiceFiled
ModelChoiceFiled(ChoiceFiled) ---- select(单选) --- ForeignKey
MultiModelChoiceFiled (ModelChoiceFiled)----select(多选) --- ManyToManyField
Book模型,form表单,modelform;
modelform帮我们转成了form表单;
bootstrap页面自适应: col-xs-8
class Book(model.Model): title = models.CharField(max_length=32) price = models.IntegerField() publish=model.Foreignkey("Publish") authors=model.ManyToMany("Author") from django.forms import ModelForm class BookForm(ModelForm): class Meta: model=Book fields="__all__" from django import forms class BookForm(forms.Form): title=forms.CharField(max_length=32) price=forms.IntegerField() publish = forms.ModelChoiceFiled("Publish") authors = forms.ModelMultipleChoiceField("Author") form=BookForm()
stark/service/stark.py
from django.conf.urls import url from django.shortcuts import HttpResponse, reverse, redirect, render from django.utils.safestring import mark_safe from django.urls import reverse from django.forms import ModelForm from stark.utils.page import Pagination from django.db.models import Q class ShowList(object): def __init__(self, config, data_list, request): self.config = config self.data_list = data_list self.request = request # 分页 data_count = self.data_list.count() current_page = 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=3, pager_count=11) self.page_data = self.data_list[self.pagination.start:self.pagination.end] self.actions = self.config.new_actions() def get_filter_linktags(self): print('list_filter:',self.config.list_filter) # ['title', 'publish', 'authors'] link_dic = {} import copy 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) print(filter_field_obj) print(type(filter_field_obj)) """ app01.Book.title <class 'django.db.models.fields.CharField'> app01.Book.publish <class 'django.db.models.fields.related.ForeignKey'> app01.Book.authors <class 'django.db.models.fields.related.ManyToManyField'> """ from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ManyToManyField # print("rel...", filter_field_obj.rel.to.objects.all()) # rel... <QuerySet [<Publish: 南京出版社>, <Publish: 上海出版社>, <Publish: 河北出版社>, <Publish: 3>]> # rel... <QuerySet [<Author: yuan>, <Author: egon>, <Author: alex>]> 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) # 处理全部标签 temp = [] if params.get(filter_field): del params[filter_field] temp.append("<a href='?%s'>全部</a>" % (params.urlencode())) else: temp.append("<a href='#' class='active'>全部</a>") # 处理数据标签 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 _url = params.urlencode() # 序列化后得结构 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 return link_dic def get_action_list(self): temp = [] for action in self.actions: temp.append({ "name": action.__name__, "desc":action.short_description }) return temp def get_header(self): # 构建表头 header_list = [] # header_list = ['选择','pk',...'操作','操作'] for field in self.config .new_list_play(): if callable(field): val = field(self.config, header=True) else: if field == "__str__": val = self.config .model._meta.model_name.upper() else: # 根据str 拿字段对象 取中文 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 data in self.page_data: temp = [] for field in self.config.new_list_play(): # ['title','price'] 字符串找对象得属性 反射 # print('field:', field) if callable(field): val = field(self.config, data) else: val = getattr(data, field) if field in self.config.list_display_links: _url = self.config.get_change_url(data) val = mark_safe("<a href='%s'>%s</a>" % (_url, val)) temp.append(val) new_data_list.append(temp) # [['yuan', 12], ['alex', 18], ['egon', 22]] return new_data_list class ModelStark(object): list_display = ["__str__"] list_display_links = [] modelform_class = [] search_fields = [] actions = [] list_filter = [] def __init__(self, model, site): self.model = model self.site = site def patch_delete(self, request, queryset): queryset.delete() patch_delete.short_description = "批量删除" def edit(self, obj=None, header=False): if header: return "操作" _url = self.get_change_url(obj) return mark_safe("<a href='%s'>编辑</a>" % _url) def deletes(self, obj=None, header=False): if header: return '操作' _url = self.get_delete_url(obj) return mark_safe("<a href='%s'>删除</a>" % _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 get_modelform_class(self): if not self.modelform_class: class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" return ModelFormDemo else: return self.modelform_class def add_view(self, request): ModelFormDemo = self.get_modelform_class() form = ModelFormDemo() for bfield in form: print('i::', type(bfield)) # 字段类型 # print('i::', bfield.field) # 字段对象 # print('name', bfield.name) # 字段名字符串 publish authors # <class 'django.forms.boundfield.BoundField'> from django.forms.boundfield import BoundField print(bfield.field) from django.forms.models import ModelChoiceField from django.forms.models import ModelMultipleChoiceField # <django.forms.fields.CharField object at 0x0000014BD355EBA8> # <django.forms.models.ModelChoiceField object at 0x0000014BD355ECF8> # <django.forms.models.ModelMultipleChoiceField object at 0x0000014BD355ED68> from django.forms.models import ModelChoiceField if isinstance(bfield.field, ModelChoiceField): bfield.is_pop = True 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 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()) def delete_view(self, request, delete_id): url = self.get_list_url() if request.method == 'POST': self.model.objects.filter(pk=delete_id).delete() return redirect(url) return render(request, 'delete_view.html', locals()) def change_view(self, request, change_id): ModelFormDemo = self.get_modelform_class() edit_obj = self.model.objects.filter(pk=change_id).first() 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()) def new_list_play(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp def new_actions(self): temp = [] temp.append(ModelStark.patch_delete) temp.extend(self.actions) return temp def get_change_url(self, obj): model_name = self.model._meta.model_name app_lable = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_lable, model_name), args=(obj.pk,)) return _url def get_delete_url(self, obj): 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 _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_search_condition(self, request): key_words = request.GET.get('q', "") self.key_words = key_words search_connection = Q() if key_words: search_connection.connector = "or" for search_field in self.search_fields: search_connection.children.append((search_field+"__contains", key_words)) return search_connection def get_filter_condition(self, request): filter_condition = Q() # 默认是 and 不是 or 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": print("request.POST:",request.POST) # 'action': ['patch_init'], 'selected_pk': ['1', '2'] action = request.POST.get('action') selected_pk = request.POST.getlist('selected_pk') action_func = getattr(self, action) # 反射 queryset = self.model.objects.filter(pk__in=selected_pk) # 秒!!! ret = action_func(request, queryset) # return ret # 获取searchd得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).filter(filter_condition) # 展示数据 showlist = ShowList(self, data_list, request) # 构建一个查看url add_url = self.get_add_url() return render(request, 'list_view.html',locals()) def get_urls2(self): temp = [] model_name = self.model._meta.model_name app_label = self.model._meta.app_label 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))) temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property def urls2(self): return self.get_urls2(), None, None class StarkSite(object): 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): temp = [] # 模型表,配置类对象 for model, stark_class_obj in self._registry.items(): model_name = model._meta.model_name app_label = model._meta.app_label # 分发增删改查 temp.append(url(r'%s/%s/'%(app_label,model_name), stark_class_obj.urls2)) return temp @property def urls(self): return self.get_urls(), None, None site = StarkSite()
add_view.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> <script src="/static/js/jquery-1.12.4.min.js"></script> <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> </head> <body> <h3>添加页面</h3> {% include 'form.html' %} <script type="text/javascript"> function pop_response(pk, text,pop_res_id) { console.log(pk); console.log(text); console.log(pop_res_id); // optiond得文本值 和value var $option = $("<option>"); //<option></option> $option.html(text); $option.val(pk); $option.attr('selected','selected'); $('#'+pop_res_id).append($option) } </script> </body> </html>
form.html
<div class="container"> <div class="row"> <div class="col-md-6 col-xs-10 col-md-offset-1"> <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 %} <a onclick="pop('{{ field.url }}')"><span style="font-size: 23px;position: absolute; right: -23px; top: 25px;">+</span></a> {% endif %} </div> {% endfor %} <button type="submit" class="btn btn-info">btn</button> </form> </div> </div> </div> <script type="text/javascript"> function pop(url) { window.open(url,"","width=500,height=300,top=100,left=100") } </script>
pop.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script type="text/javascript"> window.opener.pop_response("{{ res.pk}}","{{ res.text }}","{{ res.pop_res_id }}"); window.close() </script> </body> </html>
三、stark - 总结
总结
(单例,继承,反射,面向对象,modelform 应用得很好!!)
1.注册表
单例模式 site = StarkSite()
2.生成url
url(r'^stark/', ([],None,None))
3.数据列表展示
可自定义配置显示:
list_display = ["__str__"]
list_display_links = []
modelform_class = []
search_fields = []
actions = []
list_filter = []
4.增删改页面 modelform
5.分页
自定义分页组件 stark/utils/page.py
class Pagination(object):
...
...
6.search模糊查询
Q查询 or
search_connection = Q()
...
data_list = self.model.objects.all().filter(search_connection)
7.action批量处理
def patch_init(self, request, queryset):
queryset.update(price=123)
...
patch_init.short_description = "批量初始化"
actions = [patch_init]
queryset = self.model.objects.filter(pk__in=selected_pk)
8.filter过滤
list_filter = ['title','publish', 'authors']
eg:{"publish":["<a href=''>全部</a>","<a href=''>南京出版社</a>","<a href=''>上海出版社</a>"]
"authors":["<a href=''>全部</a>","<a href=''>yuan</a>","<a href=''>egon</a>"]
}
Q查询 and
filter_condition = Q()
data_list = self.model.objects.all().filter(search_connection).filter(filter_condition)
9.pop弹出
在一对多和多对多字段后渲染 +
+对应的跳转路径
保存添加记录同时,将原页面的对应的下拉菜单中添加该记录
test.py
from django.test import TestCase # Create your tests here. # # class A(object): # # x=12 # def __init__(self,m): # self.z = m # def xxx(self): # print(self.x) # print(self.z) # # class B(A): # x=5 # z = 11 # # b=B(10) # b.xxx() ####################################### # # class Person(object): # def __init__(self,name): # self.name = name # # alex = Person('alex') # print(alex.name) # # s = 'name' # # # print(alex.s) # 用反射 # # getattr(alex,s) # # print(getattr(alex,s)) ####################################### # 没学面向对象之前,都是函数 , # 函数 方法 # class Person(object): # def __init__(self,name): # self.name = name # # def eat(self): # 方法! # print(self) # print('eating...') # # # 实例方法 # # egon = Person('egon') # # egon.eat() # # # 函数 # Person.eat('ss') ####################################### # list = [1,2,3] # list.append(4) # print(list) # list.insert(0,100) # print(list) ####################################### # li = [] # print(len(li)) # # s = "sss" # print(isinstance(s,str)) ####################################### # class Person(object): # def __init__(self,name): # self.name = name # # def __str__(self): # return self.name # # alex = Person('alex') # # print(alex.name) # # print(alex) # # print(alex.__str__()) # print(str(alex)) # # print(getattr(alex,'__str__')()) ####################################### # temp = [] # temp.append(1) # temp.extend([1,2,3]) # print(temp) ####################################### # def foo(): # return # # print(foo.__name__) ####################################### # 查询是字段名称 # Book.objects.filter(Q(title='yuan')|Q(price='123')) # Q() 查询放str, # q = Q() # q.connection = 'or' # q.children.append(('title','yuan')) # q.children.append(('price',123)) ####################################### # ret = self.model.objects.filter(title__startswith='py') # ret = self.model.objects.filter(price__in=[123, 111, 21, 11]) # ret = self.model.objects.filter(price__range=[10, 100]) # ret = self.model.objects.filter(title__contains='y') # ret = self.model.objects.filter(title__contains='o') # ret = self.model.objects.filter(title__icontains='o') # print(ret) # return HttpResponse('ok') ####################################### # def foo(): # print('ok') # # print(foo.__name__) # print(type(foo.__name__)) # foo.desc = '123' # print(foo.desc) # a = foo() # a.desc = 12 # print(a.desc) ####################################### # class A(): # str = "http://127.0.0.1:8000/stark/app01/book/?publish=1&author=5" ####################################### # class A(object): # pass # # class B(A): # pass # # b = B() # print(isinstance(b,A)) # True # print(isinstance(b,B)) # True
code
原始版
https://github.com/alice-bj/stark_pro_0
简洁版