# -*- coding: utf-8 -*-
# @Time : 2019/4/12 11:51
# @Author : xxx
# @Email : xxx@admin.com
# @File : sites.py
# @Software: PyCharm
from django.urls import path,re_path
from django.shortcuts import HttpResponse,render,redirect
from app01.models import *
from django.utils.safestring import mark_safe
from django.core.exceptions import FieldDoesNotExist
from django.urls import reverse
from django.db.models import Q
class ShowList(object):
def __init__(self,request,config_obj,queryset):
self.config_obj=config_obj
self.queryset=queryset
self.request=request
self.pager_queryset=self.get_pager_queryset()
def get_pager_queryset(self):
from stark.utils.page import Pagination
current_page = self.request.GET.get("page", 1)
self.pagination = Pagination(self.request, current_page, self.queryset, per_page_num=self.config_obj.per_page_num or 5)
queryset = self.queryset[self.pagination.start:self.pagination.end]
return queryset
def get_header(self):
'''
获取表头
:return:
'''
# 构建表头
header_list = []
for field_or_func in self.config_obj.get_new_list_display():
if callable(field_or_func):
val = field_or_func(self.config_obj, header=True)
header_list.append(val)
# header_list.append(field_or_func.__name__)
else:
if field_or_func == "__str__":
val = self.config_obj.model._meta.model_name.upper()
print(val)
else:
field_obj = self.config_obj.model._meta.get_field(field_or_func)
val = field_obj.verbose_name
header_list.append(val)
return header_list
def get_body(self):
'''
获取标体
:return:
'''
# 构建展示数据
new_data = []
for obj in self.pager_queryset:
temp = []
for field_or_func in self.config_obj.get_new_list_display(): # ["title","price","state","publish",show_authors]
if callable(field_or_func):
# field:方法
val = field_or_func(self.config_obj, obj)
else:
try:
# field:字符串
from django.db.models.fields.related import ManyToManyField
field_obj = self.config_obj.model._meta.get_field(field_or_func)
# 判断是否多对多字段
if type(field_obj) == ManyToManyField:
raise Exception("list_display不能是多对多字段!")
# 判断字段是否 拥有choices属性
if field_obj.choices:
val = getattr(obj, "get_%s_display" % field_or_func)()
else:
val = getattr(obj, field_or_func)
if field_or_func in self.config_obj.list_display_links:
val = mark_safe("<a href='%s'>%s</a>" % (self.config_obj.get_change_url(obj), val))
except FieldDoesNotExist as e:
# val=obj
val = getattr(obj, field_or_func)()
temp.append(val)
new_data.append(temp)
print("new_data", new_data) # [['python', Decimal('123.00')], ['java', Decimal('234.00')]]
'''
目标数据结构
new_data=[
["python",123],
["java",234]
]
'''
return new_data
class ModelStark(object):
'''
默认配置类
'''
list_display=("__str__",)
list_display_links = []
model_form_class=None
per_page_num=None
search_fields=[]
search_val=None
list_filter=[]
def __init__(self,model):
self.model=model
self.model_name = self.model._meta.model_name
self.app_label = self.model._meta.app_label
# 反向解析当前访问表的增删改查URL
def get_list_url(self):
# 反向解析当前表的删除的URL
list_url = reverse("%s_%s_list" % (self.app_label, self.model_name))
return list_url
def get_add_url(self):
# 反向解析当前表的删除的URL
add_url = reverse("%s_%s_add" % (self.app_label, self.model_name))
return add_url
def get_delete_url(self,obj):
# 反向解析当前表的删除的URL
delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,))
return delete_url
def get_change_url(self, obj):
# 反向解析当前表的删除的URL
change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,))
return change_url
# 三个默认列
def show_checkbox(self, obj=None, header=False):
if header:
return mark_safe("<input type='checkbox'>")
return mark_safe("<input name='_selected_action' value=%s type='checkbox'>"%obj.pk)
def show_delbtn(self, obj=None, header=False):
if header:
return "删除"
return mark_safe("<a href='%s'>删除</a>"%self.get_delete_url(obj))
def show_editbtn(self, obj=None, header=False):
if header:
return "编辑"
return mark_safe("<a href='%s'>编辑</a>" %self.get_change_url(obj))
# 构建新的list_display
def get_new_list_display(self):
temp=[]
temp.extend(self.list_display)
temp.append(ModelStark.show_editbtn)
temp.append(ModelStark.show_delbtn)
temp.insert(0,ModelStark.show_checkbox)
return temp
def get_search_condition(self,request):
val = request.GET.get("q")
q = Q()
if val:
self.search_val = val
q.connector = "or"
for field in self.search_fields: # ["title","price"]
print(field)
# queryset=queryset.filter(Q(title__contains=val)|Q(price__contains=val))
q.children.append((field + "__contains", val))
else:
self.search_val=None
return q
# def get_search_links(self):
# import copy
# params = copy.deepcopy(self.request.GET) # {"publish":1,"authors":1}
# search_button = "<a class='btn btn-primary' href='?%s'>Search!</a>" % params.urlencode()
# return search_button
#################### action操作 ###############################
# action参数
def patch_delete(self,request,queryset): # [1,3]
queryset.delete()
patch_delete.desc="批量删除"
actions=[]
def get_new_actions(self):
temp=[]
temp.extend(self.actions)
temp.append(self.patch_delete)
return temp
def get_action_dict(self):
actions_list=[]
for func in self.get_new_actions():
actions_list.append({
"name":func.__name__,
"desc":func.desc,
})
return actions_list # [{"name":"patch_delete","desc":"批量删除"}]
#################### 多筛选操作 ###############################
def get_list_filter_links(self):
list_filter_links={}
print(self.list_filter) # ["publish","authors","state"]
for filter_field in self.list_filter:
import copy
params = copy.deepcopy(self.request.GET) # {"publish":1,"authors":1}
current_field_val=params.get(filter_field)
print('params--->',params) #params---> <QueryDict: {}> http://127.0.0.1:8000/stark/app01/book/
filter_field_obj=self.model._meta.get_field(filter_field) #获取模型类的字段类对象 eg:state = models.IntegerField(choices=[(1,"已出版"),(2,"未出版社")],default=1)
print(">>>",type(filter_field_obj)) #["publish","authors","state"]---><class 'django.db.models.fields.related.ForeignKey'>,<class 'django.db.models.fields.related.ManyToManyField'>,<class 'django.db.models.fields.IntegerField'>
from django.db.models.fields.related import ForeignKey,ManyToManyField
# 针对一对多或者多对多过滤字段
if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
print(filter_field_obj.remote_field)#["publish","authors","state"]---><ManyToOneRel: app01.book>,<ManyToManyRel: app01.book>,
print("____>",filter_field_obj.remote_field.model) # eg:<class 'app01.models.Publish'> 找到 模型类的字段类对象 的那个字段类 eg:state--->IntegerField publish---> Publish
#["publish","authors","state"]---><class 'app01.models.Publish'>,<class 'app01.models.Author'>,
data=filter_field_obj.remote_field.model.objects.all() # eg:<QuerySet [<Publish: 苹果出版设>, <Publish: 橘子出版设>, <Publish: 西瓜出版社>]>
print(data)
elif filter_field_obj.choices:
# print(filter_field_obj.remote_field) None
# 针对choice字段类型
data=filter_field_obj.choices # eg:[(1,"已出版"),(2,"未出版社")]
else:
raise Exception("过滤字段不能太普通!")
# 为每一个数据构建成一个a标签
temp = []
# params2=copy.deepcopy(params)
if params.get(filter_field): #对list_filter_links和params.urlencode()的影响
del params[filter_field]
all = "<a class='btn btn-default btn-sm' href='?%s'>全部</a>"%params.urlencode() #用户点击时 所有的
temp.append(all)
for item in data:
if type(item)==tuple:
# 元组类型
pk,text = item
else:
# model对象类型
pk,text = item.pk,str(item)
params[filter_field]=pk # ***********
_url="?%s"%(params.urlencode()) # ***********
print('url',_url)
if current_field_val==str(pk):
link="<a class='active btn btn-default btn-sm' href='%s'>%s</a>"%(_url,text)
else:
link = "<a class='btn btn-default btn-sm' href='%s'>%s</a>" % (_url, text)
temp.append(link)
list_filter_links[filter_field]=temp
# print('hahhahaha',list_filter_links)
'''
print('hahhahaha',list_filter_links)
hahhahaha {
'publish': ["<a class='btn btn-default btn-sm' href='?'>全部</a>", "<a class='btn btn-default btn-sm' href='?publish=1'>苹果出版设</a>", "<a class='btn btn-default btn-sm' href='?publish=2'>橘子出版设</a>", "<a class='btn btn-default btn-sm' href='?publish=3'>西瓜出版社</a>"],
'authors': ["<a class='btn btn-default btn-sm' href='?'>全部</a>", "<a class='btn btn-default btn-sm' href='?authors=1'>alex</a>", "<a class='btn btn-default btn-sm' href='?authors=2'>egon</a>"],
'state': ["<a class='btn btn-default btn-sm' href='?'>全部</a>", "<a class='btn btn-default btn-sm' href='?state=1'>已出版</a>", "<a class='btn btn-default btn-sm' href='?state=2'>未出版社</a>"]}
'''
return list_filter_links
def get_list_filter_condition(self):
q=Q()
for filter_field,val in self.request.GET.items():
if filter_field in ["page","q"]:continue
q.children.append((filter_field,val))
return q
###########################################################################################
# 视图函数
def list_view(self,request):
'''
self: 当前访问模型表对应的配置类对象
self.model: 当前访问模型表
:param request:
:return:
'''
self.request=request
if request.method=="POST":
# action操作
action_func_str=request.POST.get("action")
if action_func_str:
action_func=getattr(self,action_func_str)
_selected_action=request.POST.getlist("_selected_action") # [1,3]
queryset=self.model.objects.filter(pk__in=_selected_action)
action_func(request,queryset)
queryset=self.model.objects.all()
# search过滤
search_condition=self.get_search_condition(request)
# list_filter多级过滤+
list_filter_condition=self.get_list_filter_condition()
queryset=queryset.filter(search_condition).filter(list_filter_condition)
show_list=ShowList(request,self,queryset)
# 相关变量
table_name=self.model._meta.verbose_name
add_url=self.get_add_url()
return render(request,'stark/list_view.html',locals())
def get_model_form(self):
from django.forms import ModelForm
class BaseModelForm(ModelForm):
class Meta:
model = self.model
fields = "__all__"
return self.model_form_class or BaseModelForm
def add_view(self,request):
BaseModelForm=self.get_model_form()
if request.method=="GET":
form_obj=BaseModelForm()
return render(request,"stark/add_view.html",locals())
else:
form_obj = BaseModelForm(request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
else:
return render(request, "stark/add_view.html", locals())
def change_view(self,request,id):
BaseModelForm=self.get_model_form()
edit_obj=self.model.objects.filter(pk=id).first()
if request.method=="GET":
form_obj=BaseModelForm(instance=edit_obj)
return render(request,"stark/change_view.html",locals())
else:
form_obj = BaseModelForm(request.POST,instance=edit_obj)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
else:
return render(request, "stark/change_view.html", locals())
def delete_view(self,request, id):
if request.method=="POST":
self.model.objects.filter(pk=id).delete()
return redirect(self.get_list_url())
list_url=self.get_list_url()
return render(request,"stark/delete_view.html",locals())
@property
def get_urls(self):
temp = [
path("", self.list_view,name="%s_%s_list"%(self.app_label,self.model_name)),
path("add/", self.add_view,name="%s_%s_add"%(self.app_label,self.model_name)),
re_path("(d+)/change/", self.change_view,name="%s_%s_change"%(self.app_label,self.model_name)),
re_path("(d+)/delete/", self.delete_view,name="%s_%s_delete"%(self.app_label,self.model_name)),
]
return (temp, None, None)
class StarkSite:
'''
stark全局类
'''
def __init__(self):
self._registry = {}
def register(self, model, admin_class=None, **options):
admin_class = admin_class or ModelStark
self._registry[model] = admin_class(model)
def get_urls(self):
# 动态为注册的模型类创建增删改查URL
temp = []
# {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)}
for model, config_obj in self._registry.items():
print("---->", model, config_obj)
model_name = model._meta.model_name
app_label = model._meta.app_label
temp.append(
path("%s/%s/" % (app_label,model_name),config_obj.get_urls)
)
'''
path("stark/app01/book",BookConfig(Book).list_view)
path("stark/app01/book/add",BookConfig(Book).add_view)
path("stark/app01/publish",ModelAdmin(Publish).list_view)
path("stark/app01/publish/add",ModelAdmin(Publish).add_view)
'''
return temp
@property
def urls(self):
return self.get_urls(),None,None
site = StarkSite()