1. Django项目启动 自动加载文件 制作启动文件

. 注册strak 在apps.py 类里面增加如下

    def ready(self):
from django.utils.module_loading import autodiscover_modules
autodiscover_modules("stark") . 在已经注册的app中创建stark.py文件 加载

2. 在stark中模仿AdminSite ModelAdmin类写代码 注册自己的类

class StarkConfig(object):

    def __init__(self,model_class,site):
self.model_class = model_class
self.site = site class StarkSite(object): def __init__(self):
self._registey = {} def register(self,model_class,stark_config_class=None):
if not stark_config_class:
stark_config_class = StarkConfig self._registey[model_class] = stark_config_class(model_class,self) site = StarkSite()

3. 将注册的类自动生成url

- urls.py
from stark.service import v1 urlpatterns = [
url(r'^stark/', v1.site.urls),
] - 为每个类生成4个url v1.py class StarkConfig(object): def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def get_urls(self):
app_model_name = (self.model_class._meta.app_label,self.model_class._meta.model_name,)
url_patterns = [
url(r'^$',self.changelist_view,name="%s_%s_changlist" %app_model_name),
url(r'^add/$',self.add_view,name="%s_%s_add" %app_model_name),
url(r'^(\d+)/delete/$',self.delete_view,name="%s_%s_delete" %app_model_name),
url(r'^(\d+)/change/$',self.change_view,name="%s_%s_chang" %app_model_name),
]
return url_patterns @property
def urls(self):
return self.get_urls() def changelist_view(self,request,*args,**kwargs):
return HttpResponse('列表') def add_view(self,request,*args,**kwargs):
return HttpResponse('添加') def delete_view(self,request,nid,*args,**kwargs):
return HttpResponse('删除') def change_view(self,request,nid,*args,**kwargs):
return HttpResponse('修改') class StarkSite(object): def __init__(self):
self._registey = {} def register(self,model_class,stark_config_class=None):
if not stark_config_class:
stark_config_class = StarkConfig self._registey[model_class] = stark_config_class(model_class,self) def get_urls(self):
url_pattern = [] for model_class,stark_config_obj in self._registry.items(): app_name = model_class._meta.app_label
model_name = model_class._meta.model_name curd_url = url(r'^%s/%s/' %(app_name,model_name,) , (stark_config_obj.urls,None,None))
url_pattern.append(curd_url) return url_pattern @property
def urls(self):
return (self.get_urls(),None,'stark') site = StarkSite()

4. 列表页面展示

- v1.py
def changelist_view(self,request,*args,**kwargs):
# 处理表头 head_list = []
for field_name in self.list_display:
if isinstance(field_name,str):
# 根据类和字段名称,获取字段对象的verbose_name
verbose_name = self.model_class._meta.get_field(field_name).verbose_name
else:
verbose_name = field_name(self,is_header=True)
head_list.append(verbose_name) # 处理表中的数据
# [ UserInfoObj,UserInfoObj,UserInfoObj,UserInfoObj,]
# [ UserInfo(id=,name='alex',age=),UserInfo(id=,name='alex2',age=),]
data_list = self.model_class.objects.all()
new_data_list = []
for row in data_list:
# row是 UserInfo(id=,name='alex2',age=)
# row.id,row.name,row.age
temp = []
for field_name in self.list_display:
if isinstance(field_name,str):
val = getattr(row,field_name) # # alex2
else:
val = field_name(self,row)
temp.append(val)
new_data_list.append(temp) return render(request,'stark/changelist.html',{'data_list':new_data_list,'head_list':head_list}) - shark.py
class UserInfoConfig(v1.StarkConfig): def checkbox(self,obj=None,is_header=False):
if is_header:
return '选择'
return mark_safe('<input type="checkbox" name="pk" value="%s" />' %(obj.id,))
def edit(self,obj=None,is_header=False):
if is_header:
return '编辑'
return mark_safe('<a href="/edit/%s">编辑</a>' %(obj.id,)) list_display = [checkbox,'id','name',edit]

5. 显示增加按钮

curd 插件-LMLPHP

- 先判断是否显示,再通过反向解析生成增加链接
- 后端
# 是否显示增加按钮
show_add_btn = True
def get_show_btn(self):
return self.show_add_btn return render(request, 'stark/changelist.html', {'data_list': new_data_list, 'head_list': head_list,"add_url":self.get_add_url(),"show_add_btn":self.get_show_bt
- 前端
{% if show_add_btn %}
<a class="btn btn-primary" href="{{ add_url }}">增加
{% endif %}

6. 增加页面

curd 插件-LMLPHP

增加内容页面

    . 通过ModelForm创建公共类 显示和提交

        - 后端

            def add_view(self, request, *args, **kwargs):

                class AddTable(ModelForm):
class Meta:
model = self.model_class
fields = "__all__" if request.method == "GET":
form = AddTable()
return render(request,"stark/add_view.html",{"form":form})
else:
form = AddTable(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", {"form": form}) - 前端
<form method="post" novalidate>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="提交">
</form> . 升级 在子类自定义ModelForm类 子类之后可以自定义类 - v1.py
model_form_class = None
def get_model_form_class(self):
if self.model_form_class:
return self.model_form_class
else:
#方式一:
# class AddTable(ModelForm):
# class Meta:
# model = self.model_class
# fields = "__all__"
# return AddTable
#方式二:
meta = type("Meta",(object,),{"model":self.model_class,"fields":"__all__"})
AddTable = type("AddTable",(ModelForm,),{"Meta":meta})
return AddTable def add_view(self, request, *args, **kwargs):
model_form_class = self.get_model_form_class()
if request.method == "GET":
form = model_form_class()
return render(request,"stark/add_view.html",{"form":form})
else:
form = model_form_class(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", {"form": form})
- stark.py class UserInfoModelForm(ModelForm):
class Meta:
model = models.UserInfo
fields = ["name","password"]
error_messages = {
"name":{
'required':'用户名不能为空'
}
}
class UserInfoConfig(v1.StarkConfig):
model_form_class = UserInfoModelForm

9. 修改 删除

def change_view(self, request, nid, *args, **kwargs):

    obj = self.model_class.objects.filter(pk=nid).first()
if not obj:
return redirect(self.get_list_url())
model_form_class = self.get_model_form_class()
if request.method == "GET":
form = model_form_class(instance=obj)
return render(request,"stark/change_view.html",{"form":form})
else:
form = model_form_class(instance=obj,data=request.POST)
if form.is_valid:
form.save()
return redirect(self.get_list_url())
return render(request,"stark/change_view.html",{"form":form}) def delete_view(self, request, nid, *args, **kwargs):
self.model_class.objects.filter(pk=nid).delete()
return redirect(self.get_list_url())

10. 组合搜索

阶段一:

curd 插件-LMLPHP

阶段一:

    - 派生类
# 三个条件分别为 choice M2M FK
comb_filter = ["gender","depart","roles"] - 基类 comb_filter = []
def get_comb_filter(self):
return self.comb_filter - ChangeList 初始化
self.comb_filter = config.get_comb_filter() from django.db.models import ForeignKey,ManyToManyField
# 根据列表的字符串找到数据的字段对象,判断对象是否是FK,M2M
# 如果不是 为chioce
def gen_comb_filter(self):
data_list = []
for item in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(item)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
data_list.append(_field.rel.to.objects.all())
elif isinstance(_field,ManyToManyField):
data_list.append(_field.rel.to.objects.all())
else:
#choice
data_list.append(_field.choices)
return data_list - 前端 <div>
{% for comb in self.gen_comb_filter %}
<div>
{% for item in comb %}
<a href="">{{ item }}</a>
{% endfor %}
</div>
{% endfor %}
</div>

阶段二:  用户查找的数据封装到类里面  前端循环 和上面效果一样

    class FilterRow(object):

        def __init__(self,data):
self.data = data def __iter__(self):
yield "全部"
for val in self.data:
yield val def gen_comb_filter(self):
data_list = []
for item in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(item)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
data_list.append(FilterRow(_field.rel.to.objects.all()))
elif isinstance(_field,ManyToManyField):
data_list.append(FilterRow(_field.rel.to.objects.all()))
else:
#choice
data_list.append(FilterRow(_field.choices))
return data_list

阶段三: 增加搜索条件可以为多选  封装到类里面判断

        comb_filter = [
v1.filterOption("gender"),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False):
self.filed_name = filed_name
self.muti = muti def gen_comb_filter(self):
data_list = []
for obj in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(obj.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
data_list.append(FilterRow(_field.rel.to.objects.all()))
elif isinstance(_field,ManyToManyField):
data_list.append(FilterRow(_field.rel.to.objects.all()))
else:
#choice
data_list.append(FilterRow(_field.choices))
return data_list

阶段四: 搜索条件自定义  效果如上

好处1: 如部门表有 销售部门 运维部门 开发部门  默认只让显示 销售部门和运维部门
好处2: 可以继承filterOption类 重写filterOption类底下的两个方法 
        comb_filter = [
v1.filterOption("gender"),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False):
self.filed_name = filed_name
self.muti = muti
self.condition = condition def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter()
return _field.rel.to.objects.all() def get_choices(self,_field):
return _field.choices class FilterRow(object):
def __init__(self,data):
self.data = data
def __iter__(self):
yield "全部"
for val in self.data:
yield val def gen_comb_filter(self):
data_list = []
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option.get_queryset(_field))
data_list.append(row)
elif isinstance(_field,ManyToManyField):
row = FilterRow(option.get_queryset(_field))
data_list.append(row)
else:
#choice
row = FilterRow(option.get_choices(_field))
data_list.append(row)
return data_list

阶段五:  返回列表变为生成器

 comb_filter = [
v1.filterOption("gender"),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False):
self.filed_name = filed_name
self.muti = muti
self.condition = condition def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter()
return _field.rel.to.objects.all() def get_choices(self,_field):
return _field.choices class FilterRow(object):
def __init__(self,data):
self.data = data
def __iter__(self):
yield "全部"
for val in self.data:
yield val def gen_comb_filter(self):
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option.get_queryset(_field))
elif isinstance(_field,ManyToManyField):
row = FilterRow(option.get_queryset(_field))
else:
#choice
row = FilterRow(option.get_choices(_field))
yield row

阶段六: 前端循环时 如果是choice根据索引取值 如果是对象 根据对象的方法取值 所以需要做出区分 并且在后端生成url 全部的a标签还未做

curd 插件-LMLPHP

    comb_filter = [
v1.filterOption("gender",is_choice=True),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False,is_choice=False):
""" :param filed_name:
:param muti:
:param condition: 过滤条件
""" self.filed_name = filed_name
self.muti = muti
self.condition = condition
self.is_choice = is_choice def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter() return _field.rel.to.objects.all() def get_choices(self,_field): return _field.choices class FilterRow(object): def __init__(self,option,data,params):
self.option = option
self.data = data
self.params = copy.deepcopy(params) def __iter__(self):
yield mark_safe("<a href={0}>全部</a>".format(""))
for val in self.data:
if self.option.is_choice:
pk,text = val
else:
pk,text = val.pk,str(val)
yield mark_safe("<a href={0}>{1}</a>".format(pk,text)) def gen_comb_filter(self):
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option,option.get_queryset(_field),self.request.GET)
elif isinstance(_field,ManyToManyField):
row = FilterRow(option,option.get_queryset(_field),self.request.GET)
else:
#choice
row = FilterRow(option,option.get_choices(_field),self.request.GET)
yield row

阶段七: 后端生成url 全部未做

        class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False,is_choice=False):
""" :param filed_name:
:param muti:
:param condition: 过滤条件
""" self.filed_name = filed_name
self.muti = muti
self.condition = condition
self.is_choice = is_choice def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter() return _field.rel.to.objects.all() def get_choices(self,_field): return _field.choices class FilterRow(object): def __init__(self,option,data,request):
self.option = option
self.data = data
self.request = request def __iter__(self):
params = copy.deepcopy(self.request.GET)
params._mutable = True yield mark_safe("<a href={0}>全部</a>".format(""))
for val in self.data:
if self.option.is_choice:
pk,text = val
else:
pk,text = val.pk,str(val) params[self.option.filed_name] = pk
url = "%s?%s"%(self.request.path_info,params.urlencode()) yield mark_safe("<a href={0}>{1}</a>".format(url,text)) def gen_comb_filter(self):
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option,option.get_queryset(_field),self.request)
elif isinstance(_field,ManyToManyField):
row = FilterRow(option,option.get_queryset(_field),self.request)
else:
#choice
row = FilterRow(option,option.get_choices(_field),self.request)
yield row

阶段八: 请求的值如果和循环的值如果相同  全部和其它按钮 都判断

        comb_filter = [
v1.filterOption("gender",is_choice=True),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False,is_choice=False):
""" :param filed_name:
:param muti:
:param condition: 过滤条件
""" self.filed_name = filed_name
self.muti = muti
self.condition = condition
self.is_choice = is_choice def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter() return _field.rel.to.objects.all() def get_choices(self,_field): return _field.choices class FilterRow(object): def __init__(self,option,data,request):
self.option = option
self.data = data
self.request = request def __iter__(self):
params = copy.deepcopy(self.request.GET)
params._mutable = True
current_id = params.get(self.option.filed_name) # 如果循环的值 在前端传过来的里面 删除
if self.option.filed_name in params:
del params[self.option.filed_name]
url = "{0}{1}".format(self.request.path_info, params.urlencode())
yield mark_safe('<a href="{0}">全部</a>'.format(url))
else:
url = "{0}{1}".format(self.request.path_info, params.urlencode())
yield mark_safe('<a href="{0}" class="active">全部</a>'.format(url)) for val in self.data:
if self.option.is_choice:
pk,text = str(val[]),str(val[])
else:
pk,text = str(val.pk),str(val) params[self.option.filed_name] = pk
url = "%s?%s"%(self.request.path_info,params.urlencode()) if current_id == pk:
yield mark_safe("<a href={0} class='active'>{1}</a>".format(url, text))
else:
yield mark_safe("<a href={0}>{1}</a>".format(url,text)) def gen_comb_filter(self):
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option,option.get_queryset(_field),self.request)
elif isinstance(_field,ManyToManyField):
row = FilterRow(option,option.get_queryset(_field),self.request)
else:
#choice
row = FilterRow(option,option.get_choices(_field),self.request)
yield row

阶段九: 单选前后台完成

        # 组合搜索过滤
comb_codition = {}
option_list = self.get_comb_filter()
flag = False
for key in request.GET.keys():
value_list = request.GET.getlist(key)
for option in option_list:
if option.filed_name == key:
flag = True
print("")
break
if flag:
print("")
comb_codition["%s__in"%key] = value_list
print(comb_codition) queryset = self.model_class.objects.filter(self.get_search_condition()).filter(**comb_codition)

阶段十  多选完成

    class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False,is_choice=False):
""" :param filed_name:
:param muti:
:param condition: 过滤条件
""" self.filed_name = filed_name
self.muti = muti
self.condition = condition
self.is_choice = is_choice def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter() return _field.rel.to.objects.all() def get_choices(self,_field): return _field.choices class FilterRow(object): def __init__(self,option,data,request):
self.option = option
self.data = data
self.request = request def __iter__(self):
params = copy.deepcopy(self.request.GET) params._mutable = True
current_id = params.get(self.option.filed_name)
current_id_list = params.getlist(self.option.filed_name) # 全部 如果循环的值 在前端传过来的里面 删除
if self.option.filed_name in params:
origin_list = params.pop(self.option.filed_name)
url = "{0}?{1}".format(self.request.path_info, params.urlencode())
yield mark_safe('<a href="{0}">全部</a>'.format(url))
params.setlist(self.option.filed_name,origin_list)
else:
url = "{0}?{1}".format(self.request.path_info, params.urlencode())
yield mark_safe('<a href="{0}" class="active">全部</a>'.format(url)) # 全部 后面的值
for val in self.data:
if self.option.is_choice:
pk,text = str(val[]),str(val[])
else:
pk,text = str(val.pk),str(val)
if not self.option.muti:
# 单选
params[self.option.filed_name] = pk
url = "%s?%s"%(self.request.path_info,params.urlencode()) if current_id == pk:
yield mark_safe("<a href={0} class='active'>{1}</a>".format(url, text))
else:
yield mark_safe("<a href={0}>{1}</a>".format(url,text))
else:
# 多选
_params = copy.deepcopy(params)
id_list = params.getlist(self.option.filed_name)
if pk in current_id_list: id_list.remove(pk)
_params.setlist(self.option.filed_name, id_list)
url = "%s?%s" % (self.request.path_info, _params.urlencode())
yield mark_safe("<a href={0} class='active'>{1}</a>".format(url, text))
else: id_list.append(pk)
_params.setlist(self.option.filed_name,id_list)
url = "%s?%s" % (self.request.path_info, _params.urlencode())
yield mark_safe("<a href={0}>{1}</a>".format(url, text)) queryset = self.model_class.objects.filter(self.get_search_condition()).filter(**comb_codition).distinct()
阶段一:

    - 派生类
# 三个条件分别为 choice M2M FK
comb_filter = ["gender","depart","roles"] - 基类 comb_filter = []
def get_comb_filter(self):
return self.comb_filter - ChangeList 初始化
self.comb_filter = config.get_comb_filter() from django.db.models import ForeignKey,ManyToManyField
# 根据列表的字符串找到数据的字段对象,判断对象是否是FK,M2M
# 如果不是 为chioce
def gen_comb_filter(self):
data_list = []
[
((,男),(,女)),
[obj,obj,obj,obj],
[obj,obj,obj,obj]
]
for item in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(item)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
data_list.append(_field.rel.to.objects.all())
elif isinstance(_field,ManyToManyField):
data_list.append(_field.rel.to.objects.all())
else:
#choice
data_list.append(_field.choices)
return data_list - 前端 <div>
{% for comb in self.gen_comb_filter %}
<div>
{% for item in comb %}
<a href="">{{ item }}</a>
{% endfor %}
</div>
{% endfor %}
</div> 阶段二: 用户查找的数据封装到类里面 前端循环 和上面效果一样 class FilterRow(object): def __init__(self,data):
self.data = data def __iter__(self):
yield "全部"
for val in self.data:
yield val def gen_comb_filter(self):
data_list = []
for item in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(item)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
data_list.append(FilterRow(_field.rel.to.objects.all()))
elif isinstance(_field,ManyToManyField):
data_list.append(FilterRow(_field.rel.to.objects.all()))
else:
#choice
data_list.append(FilterRow(_field.choices))
return data_list 阶段三: 搜索条件可以为多选 封装到类里面判断 comb_filter = [
v1.filterOption("gender"),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False):
self.filed_name = filed_name
self.muti = muti def gen_comb_filter(self):
data_list = []
for obj in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(obj.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
data_list.append(FilterRow(_field.rel.to.objects.all()))
elif isinstance(_field,ManyToManyField):
data_list.append(FilterRow(_field.rel.to.objects.all()))
else:
#choice
data_list.append(FilterRow(_field.choices))
return data_list 阶段四: 搜索条件自定义
好处1: 如部门表有 销售部门 运维部门 开发部门 默认只让显示 销售部门和运维部门
好处2: 可以继承filterOption类 重写filterOption类底下的两个方法 comb_filter = [
v1.filterOption("gender"),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False):
self.filed_name = filed_name
self.muti = muti
self.condition = condition def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter()
return _field.rel.to.objects.all() def get_choices(self,_field):
return _field.choices class FilterRow(object):
def __init__(self,data):
self.data = data
def __iter__(self):
yield "全部"
for val in self.data:
yield val def gen_comb_filter(self):
data_list = []
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option.get_queryset(_field))
data_list.append(row)
elif isinstance(_field,ManyToManyField):
row = FilterRow(option.get_queryset(_field))
data_list.append(row)
else:
#choice
row = FilterRow(option.get_choices(_field))
data_list.append(row)
return data_list 阶段六: 前端循环时 如果是choice根据索引取值 如果是对象 根据对象的方法取值 所以需要做出区分 并且在后端生成url 全部的a标签还未做 comb_filter = [
v1.filterOption("gender",is_choice=True),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False,is_choice=False):
""" :param filed_name:
:param muti:
:param condition: 过滤条件
""" self.filed_name = filed_name
self.muti = muti
self.condition = condition
self.is_choice = is_choice def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter() return _field.rel.to.objects.all() def get_choices(self,_field): return _field.choices class FilterRow(object): def __init__(self,option,data,params):
self.option = option
self.data = data
self.params = copy.deepcopy(params) def __iter__(self):
yield mark_safe("<a href={0}>全部</a>".format(""))
for val in self.data:
if self.option.is_choice:
pk,text = val
else:
pk,text = val.pk,str(val)
yield mark_safe("<a href={0}>{1}</a>".format(pk,text)) def gen_comb_filter(self):
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option,option.get_queryset(_field),self.request.GET)
elif isinstance(_field,ManyToManyField):
row = FilterRow(option,option.get_queryset(_field),self.request.GET)
else:
#choice
row = FilterRow(option,option.get_choices(_field),self.request.GET)
yield row 阶段七: 后端生成url 全部未做 class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False,is_choice=False):
""" :param filed_name:
:param muti:
:param condition: 过滤条件
""" self.filed_name = filed_name
self.muti = muti
self.condition = condition
self.is_choice = is_choice def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter() return _field.rel.to.objects.all() def get_choices(self,_field): return _field.choices class FilterRow(object): def __init__(self,option,data,request):
self.option = option
self.data = data
self.request = request def __iter__(self):
params = copy.deepcopy(self.request.GET)
params._mutable = True yield mark_safe("<a href={0}>全部</a>".format(""))
for val in self.data:
if self.option.is_choice:
pk,text = val
else:
pk,text = val.pk,str(val) params[self.option.filed_name] = pk
url = "%s?%s"%(self.request.path_info,params.urlencode()) yield mark_safe("<a href={0}>{1}</a>".format(url,text)) def gen_comb_filter(self):
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option,option.get_queryset(_field),self.request)
elif isinstance(_field,ManyToManyField):
row = FilterRow(option,option.get_queryset(_field),self.request)
else:
#choice
row = FilterRow(option,option.get_choices(_field),self.request)
yield row 阶段八 请求的值如果和循环的值如果相同 全部和其它按钮 都判断 comb_filter = [
v1.filterOption("gender",is_choice=True),
v1.filterOption("depart"),
v1.filterOption("roles",True)
] class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False,is_choice=False):
""" :param filed_name:
:param muti:
:param condition: 过滤条件
""" self.filed_name = filed_name
self.muti = muti
self.condition = condition
self.is_choice = is_choice def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter() return _field.rel.to.objects.all() def get_choices(self,_field): return _field.choices class FilterRow(object): def __init__(self,option,data,request):
self.option = option
self.data = data
self.request = request def __iter__(self):
params = copy.deepcopy(self.request.GET)
params._mutable = True
current_id = params.get(self.option.filed_name) # 如果循环的值 在前端传过来的里面 删除
if self.option.filed_name in params:
del params[self.option.filed_name]
url = "{0}{1}".format(self.request.path_info, params.urlencode())
yield mark_safe('<a href="{0}">全部</a>'.format(url))
else:
url = "{0}{1}".format(self.request.path_info, params.urlencode())
yield mark_safe('<a href="{0}" class="active">全部</a>'.format(url)) for val in self.data:
if self.option.is_choice:
pk,text = str(val[]),str(val[])
else:
pk,text = str(val.pk),str(val) params[self.option.filed_name] = pk
url = "%s?%s"%(self.request.path_info,params.urlencode()) if current_id == pk:
yield mark_safe("<a href={0} class='active'>{1}</a>".format(url, text))
else:
yield mark_safe("<a href={0}>{1}</a>".format(url,text)) def gen_comb_filter(self):
for option in self.comb_filter:
# item gender
_field = self.model_class._meta.get_field(option.filed_name)
# _field app04.UserInfo.gender
if isinstance(_field,ForeignKey):
#如果字段的类型是ForeignKey 找到他对应的类和数据
row = FilterRow(option,option.get_queryset(_field),self.request)
elif isinstance(_field,ManyToManyField):
row = FilterRow(option,option.get_queryset(_field),self.request)
else:
#choice
row = FilterRow(option,option.get_choices(_field),self.request)
yield row 阶段九 单选完成 # 组合搜索过滤
comb_codition = {}
option_list = self.get_comb_filter()
flag = False
for key in request.GET.keys():
value_list = request.GET.getlist(key)
for option in option_list:
if option.filed_name == key:
flag = True
print("")
break
if flag:
print("")
comb_codition["%s__in"%key] = value_list
print(comb_codition) queryset = self.model_class.objects.filter(self.get_search_condition()).filter(**comb_codition) 阶段十 多选完成 class filterOption(object):
def __init__(self,filed_name,muti=False,condition=False,is_choice=False):
""" :param filed_name:
:param muti:
:param condition: 过滤条件
""" self.filed_name = filed_name
self.muti = muti
self.condition = condition
self.is_choice = is_choice def get_queryset(self,_field):
if self.condition:
return _field.rel.to.objects.filter() return _field.rel.to.objects.all() def get_choices(self,_field): return _field.choices class FilterRow(object): def __init__(self,option,data,request):
self.option = option
self.data = data
self.request = request def __iter__(self):
params = copy.deepcopy(self.request.GET) params._mutable = True
current_id = params.get(self.option.filed_name)
current_id_list = params.getlist(self.option.filed_name) # 全部 如果循环的值 在前端传过来的里面 删除
if self.option.filed_name in params:
origin_list = params.pop(self.option.filed_name)
url = "{0}?{1}".format(self.request.path_info, params.urlencode())
yield mark_safe('<a href="{0}">全部</a>'.format(url))
params.setlist(self.option.filed_name,origin_list)
else:
url = "{0}?{1}".format(self.request.path_info, params.urlencode())
yield mark_safe('<a href="{0}" class="active">全部</a>'.format(url)) # 全部 后面的值
for val in self.data:
if self.option.is_choice:
pk,text = str(val[]),str(val[])
else:
pk,text = str(val.pk),str(val)
if not self.option.muti:
# 单选
params[self.option.filed_name] = pk
url = "%s?%s"%(self.request.path_info,params.urlencode()) if current_id == pk:
yield mark_safe("<a href={0} class='active'>{1}</a>".format(url, text))
else:
yield mark_safe("<a href={0}>{1}</a>".format(url,text))
else:
# 多选
_params = copy.deepcopy(params)
id_list = params.getlist(self.option.filed_name)
if pk in current_id_list: id_list.remove(pk)
_params.setlist(self.option.filed_name, id_list)
url = "%s?%s" % (self.request.path_info, _params.urlencode())
yield mark_safe("<a href={0} class='active'>{1}</a>".format(url, text))
else: id_list.append(pk)
_params.setlist(self.option.filed_name,id_list)
url = "%s?%s" % (self.request.path_info, _params.urlencode())
yield mark_safe("<a href={0}>{1}</a>".format(url, text)) queryset = self.model_class.objects.filter(self.get_search_condition()).filter(**comb_codition).distinct()

组合筛选

11. popup

阶段一: 对于多选或单选 在后面增加popup按钮 其余不加

curd 插件-LMLPHP

    - 在后端生成返回给前端
- 如果是field是ModelChoiceField 则是Fk, 如果是ModelMultipChoice 则是M2M
- GET请求 循环form,打印各个字段的类型 发现类型都为 <class 'django.forms.boundfield.BoundField'> 它是对字段的一次封装 def add_view(self, request, *args, **kwargs): model_form_class = self.get_model_form_class()
if request.method == "GET":
form = model_form_class()
"""
for field in form:
print(type(field)) <class 'django.forms.boundfield.BoundField'>
"""
return render(request, "stark/add_view.html", {"form": form})
else:
form = model_form_class(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", {"form": form}) - 在 BoundField类里面再找一个field属性 九可以找到每个类的属性 最后三个类都为ModelChoiceField的子类 """
for bfield in form:
print(type(bfield.field)) <class 'django.forms.fields.CharField'>
<class 'django.forms.fields.CharField'>
<class 'django.forms.fields.TypedChoiceField'>
<class 'django.forms.models.ModelChoiceField'>
<class 'django.forms.models.ModelMultipleChoiceField'>
"""

说明

    - 后台代码
def add_view(self, request, *args, **kwargs): model_form_class = self.get_model_form_class()
if request.method == "GET":
form = model_form_class()
from django.forms import ModelChoiceField
new_choice = []
for bfield in form:
tmp = {"is_popup":False,"item":bfield}
if isinstance(bfield.field,ModelChoiceField):
tmp["is_popup"] = True
new_choice.append(tmp) return render(request, "stark/add_view.html", {"form": new_choice})
else:
form = model_form_class(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", {"form": form})
- 前端
{% for dic in form %}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">{{ dic.label }}</label>
<div class="col-sm-10" style="position: relative">
{{ dic.item }}
{% if dic.is_popup %}
<div style="position: absolute;right: -8px;top: 10px;">
<a class="glyphicon glyphicon-search" aria-hidden="true"></a>
</div>
{% endif %}
{{ dic.item.errors. }}
</div>
</div>
{% endfor %}

代码

阶段二: 实现popup

<form method="post"  class="form-horizontal" novalidate>
{% csrf_token %}
{% for dic in form %}
<div class="col-sm-6">
<div class="form-group" style="margin-bottom: 20px;">
<label for="inputEmail3" class="col-sm-2 control-label">{{ dic.item.field.label }}</label>
<div class="col-sm-10" style="position: relative">
<!-- 判断,如果field是ModelChoiceField,则FK; 如果是ModelMultipleChoiceField,则M2M -->
{{ dic.item }}
{% if dic.is_popup %}
<div style="position: absolute;right: -8px;top: 10px;">
<a onclick="popUp('{{ dic.popup_url }}')" class="glyphicon glyphicon-search"></a>
</div>
{% endif %}
<div style="position: absolute;font-size: 12px;left: 15px; right: 15px;color: #e4393c;background: #FFEBEB;">{{ dic.item.errors. }}</div>
</div>
</div>
</div>
{% endfor %}
<div class="col-sm-offset-11 col-sm-1">
<input type="submit" class="btn btn-primary" value="提交">
</div>
</form>
<script>
function popupCallback(dic) {
var op = document.createElement('option');
op.value = dic.id;
op.text = dic.text;
op.setAttribute('selected','selected');
document.getElementById(dic.popbackid).appendChild(op); }
function popUp(url) {
var popupPage = window.open(url, url, "status=1, height:500, width:600, toolbar=0, resizeable=0");
}
</script>

add_view.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>正在关闭</title>
</head>
<body>
<script>
(function () {
var dic = {{ json_result|safe }};
opener.popupCallback(dic);
window.close();
})() </script>
</body>
</html>

popup_response.html

 def add_view(self, request, *args, **kwargs):

        model_form_class = self.get_model_form_class()
_popbackid = request.GET.get('_popbackid') if request.method == "GET":
form = model_form_class()
from django.forms import ModelChoiceField
new_choice = []
for bfield in form:
temp = {"is_popup":False,"item":bfield}
if isinstance(bfield.field,ModelChoiceField):
#获取类名
related_class_name = bfield.field.queryset.model
app_model_name = related_class_name._meta.app_label,related_class_name._meta.model_name
print(app_model_name)
base_url = reverse("stark:%s_%s_add"%app_model_name)
popurl = "%s?_popbackid=%s"%(base_url,bfield.auto_id)
temp["popup_url"] = popurl
temp["is_popup"] = True
new_choice.append(temp) return render(request, "stark/add_view.html", {"form": new_choice})
else:
form = model_form_class(request.POST)
if form.is_valid():
form_obj = form.save()
if _popbackid:
# 是popup请求
# render一个页面,写自执行函数
result = {'id':form_obj.pk, 'text':str(form_obj),'popbackid':_popbackid }
return render(request,'stark/popup_response.html',{'json_result':json.dumps(result,ensure_ascii=False)})
else:
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", {"form": form})

v1.py

阶段三:  编辑和增加生成的页面都用到popup 代码需要重写一次,so引入 templatetags

from django.template import Library
from django.forms import ModelChoiceField
from django.shortcuts import reverse register = Library() @register.inclusion_tag('stark/form.html')
def form(model_form_obj): new_choice = []
for bfield in model_form_obj:
temp = {"is_popup": False, "item": bfield}
if isinstance(bfield.field, ModelChoiceField):
# 获取类名
related_class_name = bfield.field.queryset.model
app_model_name = related_class_name._meta.app_label, related_class_name._meta.model_name
print(app_model_name)
base_url = reverse("stark:%s_%s_add" % app_model_name)
popurl = "%s?_popbackid=%s" % (base_url, bfield.auto_id)
temp["popup_url"] = popurl
temp["is_popup"] = True
new_choice.append(temp) return {"form": new_choice}

templatetags/change.form.py

{% load staticfiles %}
{% load change_form %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static "stark/bootstrap/css/bootstrap.css" %}" />
<link rel="stylesheet" href="{% static "stark/css/stark-form.css" %}" />
</head>
<body> <div class="container" style="width: 800px;height: 500px">
<h1 class="text-center">增加页面</h1>
{% form form %}
</div>
</body>
</html>

add_view.html

{% load staticfiles %}
{% load change_form %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static "stark/bootstrap/css/bootstrap.css" %}" />
<link rel="stylesheet" href="{% static "stark/css/stark-form.css" %}" />
</head>
<body>
{# <h1>修改页面</h1>#}
{# <form method="post" novalidate>#}
{# {% csrf_token %}#}
{# {{ form.as_p }}#}
{# <input type="submit" value="提交">#}
{# </form>#} <div class="container" style="width: 800px;height: 500px">
<h1 class="text-center">编辑页面</h1>
{% form form %}
</div> </body>
</html>

change_view.html

    def add_view(self, request, *args, **kwargs):

        model_form_class = self.get_model_form_class()
_popbackid = request.GET.get('_popbackid') if request.method == "GET":
form = model_form_class() return render(request, "stark/add_view.html", {"form": form})
else:
form = model_form_class(request.POST)
if form.is_valid():
form_obj = form.save()
if _popbackid:
# 是popup请求
# render一个页面,写自执行函数
result = {'id':form_obj.pk, 'text':str(form_obj),'popbackid':_popbackid }
return render(request,'stark/popup_response.html',{'json_result':json.dumps(result,ensure_ascii=False)})
else:
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", {"form": form})

def add_view()

12. 其它列可编辑

子类
#. 其它列可编辑
# def get_list_display(self):
# data = []
# if self.list_display:
# data.extend(self.list_display)
# data.append(v1.StarkConfig.delete)
# data.insert(, v1.StarkConfig.checkbox)
# return data
edit_link = ["title"] class StarkConfig(object):
edit_link = ["title"] edit_link = []
def get_edit_link(self):
result = []
if self.edit_link:
result.extend(self.edit_link)
return result class ChangeList(object):
self.edit_link = config.get_edit_link() def body_list(self):
# 处理表中的数据
# [ UserInfoObj,UserInfoObj,UserInfoObj,UserInfoObj,]
# [ UserInfo(id=,name='alex',age=),UserInfo(id=,name='alex2',age=),]
data_list = self.data_list
new_data_list = []
for row in data_list:
# row是 UserInfo(id=,name='alex2',age=)
# row.id,row.name,row.age
temp = []
for field_name in self.list_display:
if isinstance(field_name,str):
val = getattr(row,field_name) # # alex2
# 判断是否在编辑列表中
if field_name in self.edit_link:
val = self.edit_link_url(row.pk,val)
else:
val = field_name(self.config,row)
temp.append(val)
new_data_list.append(temp) return new_data_list # 其它列的作为编辑功能的url
def edit_link_url(self,pk,text):
query_str = self.request.GET.urlencode() # page=&nid=
params = QueryDict(mutable=True)
params[self.config._query_param_key] = query_str return mark_safe('<a href="%s?%s">%s</a>' % (self.config.get_change_url(pk), params.urlencode(),text))

整个流程

. Django项目启动 自动加载文件 制作启动文件

    . 注册strak 在apps.py 类里面增加如下

        def ready(self):
from django.utils.module_loading import autodiscover_modules
autodiscover_modules("stark") . 在已经注册的app中创建stark.py文件 加载 . 在stark中模仿AdminSite ModelAdmin类写代码 注册自己的类 class StarkConfig(object): def __init__(self,model_class,site):
self.model_class = model_class
self.site = site class StarkSite(object): def __init__(self):
self._registey = {} def register(self,model_class,stark_config_class=None):
if not stark_config_class:
stark_config_class = StarkConfig self._registey[model_class] = stark_config_class(model_class,self) site = StarkSite() . 将注册的类自动生成url - urls.py
from stark.service import v1 urlpatterns = [
url(r'^stark/', v1.site.urls),
] - 为每个类生成4个url v1.py class StarkConfig(object): def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def get_urls(self):
app_model_name = (self.model_class._meta.app_label,self.model_class._meta.model_name,)
url_patterns = [
url(r'^$',self.changelist_view,name="%s_%s_changlist" %app_model_name),
url(r'^add/$',self.add_view,name="%s_%s_add" %app_model_name),
url(r'^(\d+)/delete/$',self.delete_view,name="%s_%s_delete" %app_model_name),
url(r'^(\d+)/change/$',self.change_view,name="%s_%s_chang" %app_model_name),
]
return url_patterns @property
def urls(self):
return self.get_urls() def changelist_view(self,request,*args,**kwargs):
return HttpResponse('列表') def add_view(self,request,*args,**kwargs):
return HttpResponse('添加') def delete_view(self,request,nid,*args,**kwargs):
return HttpResponse('删除') def change_view(self,request,nid,*args,**kwargs):
return HttpResponse('修改') class StarkSite(object): def __init__(self):
self._registey = {} def register(self,model_class,stark_config_class=None):
if not stark_config_class:
stark_config_class = StarkConfig self._registey[model_class] = stark_config_class(model_class,self) def get_urls(self):
url_pattern = [] for model_class,stark_config_obj in self._registry.items(): app_name = model_class._meta.app_label
model_name = model_class._meta.model_name curd_url = url(r'^%s/%s/' %(app_name,model_name,) , (stark_config_obj.urls,None,None))
url_pattern.append(curd_url) return url_pattern @property
def urls(self):
return (self.get_urls(),None,'stark') site = StarkSite() . 列表页面展示 - v1.py
def changelist_view(self,request,*args,**kwargs):
# 处理表头 head_list = []
for field_name in self.list_display:
if isinstance(field_name,str):
# 根据类和字段名称,获取字段对象的verbose_name
verbose_name = self.model_class._meta.get_field(field_name).verbose_name
else:
verbose_name = field_name(self,is_header=True)
head_list.append(verbose_name) # 处理表中的数据
# [ UserInfoObj,UserInfoObj,UserInfoObj,UserInfoObj,]
# [ UserInfo(id=,name='alex',age=),UserInfo(id=,name='alex2',age=),]
data_list = self.model_class.objects.all()
new_data_list = []
for row in data_list:
# row是 UserInfo(id=,name='alex2',age=)
# row.id,row.name,row.age
temp = []
for field_name in self.list_display:
if isinstance(field_name,str):
val = getattr(row,field_name) # # alex2
else:
val = field_name(self,row)
temp.append(val)
new_data_list.append(temp) return render(request,'stark/changelist.html',{'data_list':new_data_list,'head_list':head_list}) - shark.py
class UserInfoConfig(v1.StarkConfig): def checkbox(self,obj=None,is_header=False):
if is_header:
return '选择'
return mark_safe('<input type="checkbox" name="pk" value="%s" />' %(obj.id,))
def edit(self,obj=None,is_header=False):
if is_header:
return '编辑'
return mark_safe('<a href="/edit/%s">编辑</a>' %(obj.id,)) list_display = [checkbox,'id','name',edit] . 显示增加按钮 - 先判断是否显示,再通过反向解析生成增加链接
- 后端
# 是否显示增加按钮
show_add_btn = True
def get_show_btn(self):
return self.show_add_btn return render(request, 'stark/changelist.html', {'data_list': new_data_list, 'head_list': head_list,"add_url":self.get_add_url(),"show_add_btn":self.get_show_btn()}) - 前端
{% if show_add_btn %}
<a class="btn btn-primary" href="{{ add_url }}">增加</a>
{% endif %} . 增加内容页面 . 通过ModelForm创建公共类 显示和提交 - 后端 def add_view(self, request, *args, **kwargs): class AddTable(ModelForm):
class Meta:
model = self.model_class
fields = "__all__" if request.method == "GET":
form = AddTable()
return render(request,"stark/add_view.html",{"form":form})
else:
form = AddTable(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", {"form": form}) - 前端
<form method="post" novalidate>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="提交">
</form> . 升级 在子类自定义ModelForm类 子类之后可以自定义类 - v1.py
model_form_class = None
def get_model_form_class(self):
if self.model_form_class:
return self.model_form_class
else:
#方式一:
# class AddTable(ModelForm):
# class Meta:
# model = self.model_class
# fields = "__all__"
# return AddTable
#方式二:
meta = type("Meta",(object,),{"model":self.model_class,"fields":"__all__"})
AddTable = type("AddTable",(ModelForm,),{"Meta":meta})
return AddTable def add_view(self, request, *args, **kwargs):
model_form_class = self.get_model_form_class()
if request.method == "GET":
form = model_form_class()
return render(request,"stark/add_view.html",{"form":form})
else:
form = model_form_class(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", {"form": form})
- stark.py class UserInfoModelForm(ModelForm):
class Meta:
model = models.UserInfo
fields = ["name","password"]
error_messages = {
"name":{
'required':'用户名不能为空'
}
}
class UserInfoConfig(v1.StarkConfig):
model_form_class = UserInfoModelForm . 修改 和 删除
def change_view(self, request, nid, *args, **kwargs): obj = self.model_class.objects.filter(pk=nid).first()
if not obj:
return redirect(self.get_list_url())
model_form_class = self.get_model_form_class()
if request.method == "GET":
form = model_form_class(instance=obj)
return render(request,"stark/change_view.html",{"form":form})
else:
form = model_form_class(instance=obj,data=request.POST)
if form.is_valid:
form.save()
return redirect(self.get_list_url())
return render(request,"stark/change_view.html",{"form":form}) def delete_view(self, request, nid, *args, **kwargs):
self.model_class.objects.filter(pk=nid).delete()
return redirect(self.get_list_url())
05-11 16:12