from django.urls import path, re_path
from django.shortcuts import HttpResponse, render
from app01 import models
from django.urls import reverse
from django.utils.safestring import mark_safe
class ModelStark(object):
"""
默认配置类
"""
list_display = ("__str__",) # 创建一个变量,如果用户没有自定义则走默认的模型str(str在app01的models定义了),用来满足自定制,
# 注意这里要加逗号,因为下面会迭代追加,一个字符串会分成一个个
# print(list_display, "list_display")
def __init__(self, model):
print("init")
self.model = model
self.model_name = self.model._meta.model_name # 获取model名称
self.app_label = self.model._meta.app_label # 获取model所在app名称
# 反向解析当前访问表的增删改查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, obj):
# 反向解析当前表的添加URL
add_url = reverse("%s_%s_add" % (self.app_label, self.model_name))
return add_url
def get_delete_url(self, obj):
# 反向解析当前表的删除URL,args 获取当前obj的pk值,下面list_views里面获取了每条对象,即obj
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_delete" % (self.app_label, self.model_name), args=(obj.pk,))
return change_url
# 三个默认的列,即功能
def show_checkbox(self, obj=None, header=False):
"""
mark_safe: 将添加的字节进行编译,HTML默认将添加字节做字符串处理
:param obj: None
:param header: True
:return:
"""
if header:
return mark_safe("<input type='checkbox'>")
return mark_safe("<input type='checkbox'>")
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):
"""
mark_safe: 将添加的字节进行编译,HTML默认将添加字节做安全处理,这是告诉它此字符串是安全的
:param obj: 每条数据的对象
:param header: 如果添加header命名则走自定制页面
:return:
"""
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) # 迭代增加元素,即将list_display拆开一个个添加进去
temp.append(ModelStark.show_delbtn) # 将删除模型类对象添加进去
temp.append(ModelStark.show_editbtn) # 将编辑模型类对象添加进去
temp.insert(0, ModelStark.show_checkbox) # 多选按钮插入到第0个位置
"""
[<function ModelStark.show_checkbox at 0x0000025CCDED1C80>,
'title', 'price', 'state', 'publisher',
<function BookConfig.show_authors at 0x0000025CCDED1400>,
<function ModelStark.show_delbtn at 0x0000025CCDED1D08>,
<function ModelStark.show_editbtn at 0x0000025CCDED1D90>]
"""
return temp # 此时temp如上
def list_view(self, request):
queryset = self.model.objects.all() # 获取当前对象的Queryset,即当前book表或publish表数据
# 构建表头,将表头和表内数据分开成两个列表
header_list = []
for field_or_func in self.get_new_list_display():
# print("field_or_func", field_or_func)
if callable(field_or_func): # 判断是否可调用对象
val = field_or_func(self, header=True) # 将函数header改成True并调用得到值做表头
header_list.append(val) # 是的话调用函数并将值添加到表头
else:
if field_or_func == "__str__":
val = self.model._meta.model_name.upper()
else:
# app01.Book.title 取到对应models模型字段的对象
field_obj = self.model._meta.get_field(field_or_func)
# 获取到对应表头名
val = field_obj.verbose_name
header_list.append(val)
# 构建展示数据
new_data = []
for obj in queryset: # 将当前表数据循环获取出每一条的对象!!!
temp = []
for field_or_func in self.get_new_list_display(): # 循环新display列表, 并将内容取出来
if callable(field_or_func): # 如果是可调用对象
val = field_or_func(self, obj) # 运行函数并获取值
else:
try:
from django.db.models.fields.related import ManyToManyField
field_obj = self.model._meta.get_field(field_or_func) # 获取到对象
# 判断是否多对多字段,判断该函数需要导入上面模块,如果直接写多对多则报错,需要自己写函数
if type(field_obj) == ManyToManyField:
raise Exception("list_display can't many To many")
# 判断字段是否拥有 choices 属性
if field_obj.choices:
# obj.get_state_display() 该方法能获取到choices对象目前的值
val = getattr(obj, "get_%s_display" % field_or_func)()
else:
# 所有对象空间如果有属性值,利用以下方法即可反射获取值
val = getattr(obj, field_or_func)
except Exception as e:
# 进入publish时,走上面语句则会报错,所以直接用这个方法,近添加首个
val = getattr(obj, field_or_func)() # 直接用str获取当前对象的数据即可
temp.append(val) # 将数据一行一行的添加
new_data.append(temp)
print("-------->new_data", new_data)
return render(request, 'stark/list_view.html', locals())
def add_view(self, request):
# 添加
return HttpResponse("add_view111")
def change_view(self, request, id):
# 编辑
return HttpResponse("change_view")
def delete_view(self, request, id):
# 删除
return HttpResponse("delete_view")
@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):
# model就是用户模型类(指向空间,即book或publish),admin_class 如果用户没有自己创建样式装修则用默认的
admin_class = admin_class or ModelStark # or 的方法,选择为非0的对象赋值
self._registry[model] = admin_class(model) # 给字典不断添加键值对,以model为键,配置类为值
def get_urls(self):
# 动态为注册的模型类创建增删改查URL
# 创建temp存放path
temp = []
# {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)}
for model, config_obj in self._registry.items():
# 将上面函数拿到的各个模型类键值对提取出来,config_obj就是
model_name = model._meta.model_name # 该方法是拿model的名字(字符串)
app_label = model._meta.app_label # 该方法是拿model所在app的字符串
"""
二级分发小例子:
urls = [
path('index/', index),
path('book/',([
path('test01/', test01),
path('test02/', test02),
],None,None))
]
"""
# 给temp添加path,即book_manage/book 或 book_manage/publish
# config_obj.get_urls 即是增删改查的路径(二级分发),默认就会有自己的二级temp
temp.append(
path("%s/%s/" % (app_label, model_name), config_obj.get_urls)
)
return temp
@property
def urls(self):
# property 方法是使调用它的时候直接使用urls即可调用(默认Admin做的,可以参考urls里面的Admin)
return self.get_urls(), None, None # 调用时返回这些值,None是固定传的,一个是app名字一个是空间名字所以直接None
site = StarkSite()
"""
将全局类实例化成一个对象,所有的调用都使用这个实例化对象,
这样空间就永远是一个,只是实例化键值对来指向它
"""