今日内容前戏

静态字段和字段

先来看下面一段代码

class Foo:
x = 1 # 类变量、静态字段、静态属性 def __init__(self):
y = 6 # 实例变量、字段、对象属性 # 实例方法
def func(self):
pass # 静态方法
@staticmethod
def func():
pass # 类方法
@classmethod
def func():
pass @property
def start(self)
pass

官方说法:x称之为 类变量  y称之为 实例变量

在java和C#中,分别叫 静态字段字段

函数名

获取函数的名字,可通过 函数.__name__  方法获取。

那么在django模板中,不能使用双下方法。会报错!

urlencode

什么是urlencode

URL编码(URL encoding),也称作百分号编码(Percent-encoding), 是特定上下文的统一资源定位符 (URL)的编码机制。

为什么要用urlencode

发现现在几乎所有的网站都对url中的汉字和特殊的字符,进行了urlencode操作,也就是:

http://hi.baidu.com/%BE%B2%D0%C4%C0%CF%C8%CB/creat/blog/

这个样子,中间%形式的。注意:urlencode是一种编码,它不是加密方式

url转义是为了符合url的规范,因为在标准的url规范中中文和很多的字符是不允许出现在url中的。

URLEncode就是将URL中特殊部分进行编码。URLDecoder就是对特殊部分进行解码。

因为当字符串数据以url的形式传递给web服务器时,字符串中是不允许出现空格和特殊字符的

譬如:你要传的字符串数据时name=lisi&wangwu  这里的lisi&wangwu是一个字符串  但是服务器只会将lisi识别出来

所以要用到urlencode对这个字符串进行编码

协调开发时,所使用的编码不同。比如:A发送参数携带中文时,使用gbk。B接收时,使用utf-8解码时,就会出现乱码。

那么A发送url的参数使用了URLEncode编码,B接收时使用URLDecoder解码,就不会出现乱码了!

举例:

有一个字典

info = {'k1':'v1','k2':'v2'}

需要转换为

k1=v1&k2=v2

怎么转换?

第一种方法:使用for循环

info = {'k1':'v1','k2':'v2'}
list_1 = []
for k,v in info.items():
# print('{}={}'.format(k,v))
list_1.append('{}={}'.format(k,v)) res = '&'.join(list_1)
print(res)

执行输出:k1=v1&k2=v2

第一种方法:urlencode

from urllib.parse import urlencode
info = {'k1':'v1','k2':'v2'}
print(urlencode(info))

以上可以看出,使用urlencode更方便!

字典有多少个key-value,都可以方便的转换!

QueryDict

在django中,request.GET的值类型,是什么呢?是QueryDict

新建一个项目untitled3,注意:django的版本为1.11

修改urls.py,增加路径index

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
]

修改views.py,增加index视图函数

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
print(request.GET,type(request.GET))
return HttpResponse('ok')

启动django项目,访问index页面

查看Pycharm控制台输出:

<QueryDict: {}> <class 'django.http.request.QueryDict'>

它是一个QueryDict类型

在url上面添加参数,访问url

http://127.0.0.1:8000/index/?name=xiao&age=20

查看Pycharm控制台输出:

<QueryDict: {'age': [''], 'name': ['xiao']}> <class 'django.http.request.QueryDict'>

修改views.py,导入QueryDict,查看源码

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict
print(request.GET) v = request.GET.urlencode()
print(v) return HttpResponse('ok')

它内部调用了urlencode,具体位置,我找不到了...

刷新页面,查看Pycharm控制台输出:

<QueryDict: {'name': ['xiao'], 'age': ['']}>
name=xiao&age=20

request.GET.urlencode,可以直接调用!

能不能在request.GET中,添加一个值呢?

修改views.py,增加一个值

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict
print(request.GET)
request.GET['k1'] = 666 # 增加一个值
v = request.GET.urlencode()
print(v) return HttpResponse('ok')

刷新页面,效果如下:

提示:  这个QueReDICT实例是不可变的,这个是django安全策略做的

如果一定要修改,需要设置一个参数

request.GET._mutable = True

一定要重启django,否则不生效!刷新页面,效果如下:

查看Pycharm控制台输出:

<QueryDict: {'age': [''], 'name': ['xiao']}>
k1=666&age=20&name=xiao

发现k1已经添加进去了!

为了避免对后续的程序有影响,需要使用深copy

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict
import copy
print(request.GET) params = copy.deepcopy(request.GET) # 使用深copy
params._mutable = True # 允许修改
params['k1'] = 666 # 增加一个值
v = params.urlencode()
print(v) return HttpResponse('ok')

重启django项目,刷新页面,效果同上!

查看Pycharm控制台输出,效果同上!

QueryDict 源码里面,提供了方法__deepcopy__ ,它也是做深copy的

def __deepcopy__(self, memo):
result = self.__class__('', mutable=True, encoding=self.encoding)
memo[id(self)] = result
for key, value in six.iterlists(self):
result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo))
return result

查看request.GET.copy()的源码,它实际上,就是调用了__deepcopy__

def copy(self):
"""Returns a mutable copy of this object."""
return self.__deepcopy__({})

修改views.py,改用request.GET.copy()

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict
import copy
print(request.GET) params = request.GET.copy() # 使用深copy
params._mutable = True # 允许修改
params['k1'] = 666 # 增加一个值
v = params.urlencode()
print(v) return HttpResponse('ok')

重启django项目,刷新页面,效果同上!

查看Pycharm控制台输出,效果同上!

修改views.py,增加一个列表

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict
import copy
print(request.GET) params = request.GET.copy() # 使用深copy
params._mutable = True # 允许修改
params['k1'] = 666 # 增加一个值
params['k2'] = [11,12] # 增加一个列表
v = params.urlencode()
print(v) return HttpResponse('ok')

刷新页面,查看Pycharm控制台输出:

<QueryDict: {'age': [''], 'name': ['xiao']}>
name=xiao&k2=%5B11%2C+12%5D&k1=666&age=20

发现k2=%5B11%2C+12%5D,这并不是我们想要的结果!

列表需要使用setlist才行

修改views.py,使用setlist

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict
import copy
print(request.GET) params = request.GET.copy() # 使用深copy
params._mutable = True # 允许修改
params['k1'] = 666 # 增加一个值
params.setlist('k4',[11,12]) # 增加一个列表
v = params.urlencode()
print(v) return HttpResponse('ok')

重启django项目,刷新页面。查看Pycharm控制台输出:

<QueryDict: {'name': ['xiao'], 'age': ['']}>
k1=666&k4=11&k4=12&name=xiao&age=20

可以看到k4=11&k4=12,它分别设置了2个值!

那么获取k4使用getlist

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict
import copy
print(request.GET) params = request.GET.copy() # 使用深copy
params._mutable = True # 允许修改
params['k1'] = 666 # 增加一个值
params.setlist('k4',[11,12]) # 增加一个列表
print(params.get('k1'))
print(params.getlist('k4')) # 获取列表,使用getlist
v = params.urlencode()
print(v) return HttpResponse('ok')

刷新页面,查看Pycharm控制台输出:

<QueryDict: {'name': ['xiao'], 'age': ['']}>
666
[11, 12]
k4=11&k4=12&k1=666&name=xiao&age=20

在列表中,增加一个值,使用append

修改views.py

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict
import copy
print(request.GET) params = request.GET.copy() # 使用深copy
params._mutable = True # 允许修改
params['k1'] = 666 # 增加一个值
params.setlist('k4',[11,12]) # 增加一个列表 old = params.getlist('k4') # 获取列表
old.append('v4') # 最加一个值
params.setlist('k4',old) # 重新设置
v = params.urlencode()
print(v) return HttpResponse('ok')

刷新页面,查看Pycharm控制台输出:

<QueryDict: {'name': ['xiao'], 'age': ['']}>
name=xiao&k1=666&age=20&k4=11&k4=12&k4=v4

可以发现,k4有3个值

保留原来搜索条件

先来访问一个链接:

http://127.0.0.1:8000/index/?name=xiao&age=20

需要在视图函数中的request.GET中,添加一个值_filter。用来保存原来的搜索条件

_filter = "name=xiao&age=20"

最后变成QueryDict,如何操作?

修改views.py

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
from django.http.request import QueryDict url_params_str = request.GET.urlencode()
# mutable=True 表示可修改
query_dic = QueryDict(mutable=True)
# 添加一个key为_filter
query_dic['_filter'] = url_params_str
print(query_dic) # 重新编码
new_params = query_dic.urlencode()
print(new_params) return HttpResponse('ok')

刷新页面,查看Pycharm控制台输出:

<QueryDict: {'_filter': ['name=xiao&age=20']}>
_filter=name%3Dxiao%26age%3D20

最后一个值,使用urlencode编码了

举例:

假设一个场景,先搜索到了一些学生。注意:此时url是有搜索条件的

点击添加按钮,跳转到添加页面。注意:此时的url带有原搜索条件

添加完成后,跳转到原来的页面。注意:此时的url带有原搜索条件

修改urls.py,增加路径

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
url(r'^add_stu/', views.add_stu),
]

修改views.py,增肌视图函数

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.
def index(request):
from django.http.request import QueryDict url_params_str = request.GET.urlencode()
# mutable=True 表示可修改
query_dic = QueryDict(mutable=True)
# 添加一个key为_filter
query_dic['_filter'] = url_params_str
print(query_dic) # 重新编码
new_params = query_dic.urlencode()
print(new_params) # 跳转地址
target_url = "/add_stu?%s" %new_params
print(target_url)
# 渲染一个a标签
return HttpResponse('<a href="%s">添加学生</a>'%target_url) def add_stu(request):
if request.method == "GET":
return render(request,"add_stu.html") # 接收到数据,保存到数据库...
# 获取搜索条件
origin_params = request.GET.get('_filter')
# 返回地址,保留原搜索添加
back_url = "/index/?%s" %origin_params
# 重定向页面
return redirect(back_url)

重启django程序,访问页面

注意:这里直接访问的是带有搜索条件的

http://127.0.0.1:8000/index/?name=xiao&age=20

效果如下:

点击添加学生,页面跳转

注意:这里面的_filter,就是原来的搜索条件

点击提交按钮,页面跳转

此时页面:还是带有原来的搜索条件

Q查询高级用法

一般页面使用的搜索功能,都是使用了模糊搜索

比如:查询name包含 "大"或者email包含 "qq"的记录

q = Q()
q.connecter = "OR" # 使用OR作为连接符
# 合并条件进行查询,__contains表示使用like查询
q.children.append(('name__contains', '大'))
q.children.append(('email__contains', 'qq'))

它相当于 name like '%大%' OR email like '%qq%'

一、批量操作

务必下载github代码:

https://github.com/987334176/luffy_stark/archive/v1.1.zip

因为下面的内容,都是这份代码来修改的!

修改stark-->templates-->stark-->changelist.html,增加select标签

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
<form class="form-inline" method="post">
{% csrf_token %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
<option value="">批量删除</option>
<option value="">初始化</option>
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
</form>
<table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table> </div> {% endblock %}

修改 app01-->stark.py,注释部分代码。增加复选框显示

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url class UserInfoConfig(StarkConfig):
list_display = ['id', 'username'] class DepartModelForm(forms.ModelForm):
class Meta:
model = models.Depart
fields = "__all__" def clean_name(self): # 定义钩子
# print(self.cleaned_data['name'])
return self.cleaned_data['name'] class DepartConfig(StarkConfig):
list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
# model_form_class = DepartModelForm
# def get_add_btn(self): # 返回None,表示不显示添加按钮
# pass
# def changelist_view(self, request): # 重写changelist_view方法
# # 渲染自定义的列表页面
# return render(request,'stark/custom_list.html')
# def get_urls(self): # 自定义路由
# info = self.model_class._meta.app_label, self.model_class._meta.model_name
#
# urlpatterns = [
# url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
# ]
# return urlpatterns site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

访问页面: http://127.0.0.1:8000/stark/app01/userinfo/list/

点击下拉菜单,效果如下:

下拉框里面的元素应该是动态的,它是可配置的,每个选项包含了一个id。

如果使用表单提交,那么表单数据就很大了

修改stark-->server-->stark.py,添加变量action_list。用来定义要批量操作的选项

为了防止列表来回更改,导致列表元素重复。使用extend

添加multi_delete和multi_init方法,将action_list传给模板

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除
pass def multi_init(self): # 批量初始化
pass def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
# 根据排序列表进行排序
queryset = self.model_class.objects.all().order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

修改app01-->stark.py,定义action_list

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url class UserInfoConfig(StarkConfig):
list_display = ['id', 'username'] class DepartModelForm(forms.ModelForm):
class Meta:
model = models.Depart
fields = "__all__" def clean_name(self): # 定义钩子
# print(self.cleaned_data['name'])
return self.cleaned_data['name'] class DepartConfig(StarkConfig):
list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
# model_form_class = DepartModelForm
# 批量操作
action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
# def get_add_btn(self): # 返回None,表示不显示添加按钮
# pass
# def changelist_view(self, request): # 重写changelist_view方法
# # 渲染自定义的列表页面
# return render(request,'stark/custom_list.html')
# def get_urls(self): # 自定义路由
# info = self.model_class._meta.app_label, self.model_class._meta.model_name
#
# urlpatterns = [
# url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
# ]
# return urlpatterns site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

修改 stark-->templates-->stark-->changelist.html,使用for循环action_list

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
<form class="form-inline" method="post">
{% csrf_token %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for func in action_list %}
<option value="">{{ func.__name__ }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
</form>
<table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table> </div> {% endblock %}

重启django,查看页面:http://127.0.0.1:8000/stark/app01/depart/list/

效果如下:

提示  变量和属性不能从下划线开始

怎么办呢?让后端把函数名传过来,使用列表生成式

修改stark-->server-->stark.py,使用列表生成式

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除
pass def multi_init(self): # 批量初始化
pass def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
# 根据排序列表进行排序
queryset = self.model_class.objects.all().order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
action_list = [ func.__name__ for func in action_list ]
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

修改 stark-->templates-->stark-->changelist.html,修改变量名

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
<form class="form-inline" method="post">
{% csrf_token %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for name in action_list %}
<option value="">{{ name }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
</form>
<table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table> </div> {% endblock %}

刷新页面,效果如下:

它是英文的,要中文展示,怎么搞?

python一切皆对象,可以为函数添加一个属性。属性名无所谓!

新建一个a.py文件,内容如下:

def func():
print(123) func.aa = '批量删除' print(func.aa)

执行输出:批量删除

同样的道理,给函数multi_delete和multi_init,添加属性text

在列表生成式中,获取text属性

修改stark-->server-->stark.py,

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除
pass multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self): # 批量初始化
pass multi_init.text = "批量初始化" # 添加自定义属性text def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
# 根据排序列表进行排序
queryset = self.model_class.objects.all().order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取text属性
action_list = [ func.text for func in action_list ]
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

重启django,刷新页面,效果如下:

光有中文,无法知道它对应的是哪个函数?value值都是1

修改stark-->server-->stark.py,更改列表生成式的元素为字典

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除
pass multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self): # 批量初始化
pass multi_init.text = "批量初始化" # 添加自定义属性text def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
# 根据排序列表进行排序
queryset = self.model_class.objects.all().order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

修改 stark-->templates-->stark-->changelist.html,value值为函数名

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
<form class="form-inline" method="post">
{% csrf_token %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for item in action_list %}
<option value="{{ item.name }}">{{ item.text }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
</form>
<table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table> </div> {% endblock %}

刷新页面,效果如下:

可以看到value值已经渲染出来了!

那么问题来了,table表格的数据,没有包含到form表单里面

修改 stark-->templates-->stark-->changelist.html,更改form表单

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
<form class="form-inline" method="post">
{% csrf_token %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for item in action_list %}
<option value="{{ item.name }}">{{ item.text }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div> <table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table>
</form>
</div> {% endblock %}

修改stark-->server-->stark.py,修改changelist_view方法,接收post数据

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除
pass multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self): # 批量初始化
pass multi_init.text = "批量初始化" # 添加自定义属性text def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == "POST":
print(request.POST) # 获取post数据 # 根据排序列表进行排序
queryset = self.model_class.objects.all().order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

刷新页面,选择批量删除,勾选几条数据。点击提交

查看Pycharm控制台输出:

<QueryDict: {'csrfmiddlewaretoken': ['FtQPfzOYfkiOxGx80QYcIFxqT9wqSpgwWYO1M9rR9lUzkLwDgO6ud260AfIUkJnK'], 'pk': ['', '', ''], 'action': ['multi_delete']}>

获取得到数据了!

接下来需要根据获取的方法名来执行方法。怎么判断呢?使用if?如果有多个方法呢?

这个时候,应该使用反射

修改stark-->server-->stark.py,使用反射执行对应的方法。修改那2个方法

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法 def multi_delete(self,request): # 批量删除
print('批量删除') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == "POST":
action_name = request.POST.get('action')
getattr(self,action_name)(request) # 根据排序列表进行排序
queryset = self.model_class.objects.all().order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

刷新页面,再次选择批量删除,勾选几条数据,点击提交!

查看Pycharm控制台输出: 批量删除

假如有不法分子,修改html呢?

为了避免攻击,使用字典判断,因为字典查询快

修改stark-->server-->stark.py,增加方法get_action_dict

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法 def multi_delete(self,request): # 批量删除
print('批量删除')
return HttpResponse('批量删除') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response # 根据排序列表进行排序
queryset = self.model_class.objects.all().order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

刷新页面,点击匹配初始化,还是会跳转到当前页面。因为此方法,没有返回值。

点击批量删除,页面效果如下:

修改stark-->server-->stark.py,修改multi_delete方法

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法 def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response # 根据排序列表进行排序
queryset = self.model_class.objects.all().order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

刷新页面,选中一条数据,点击提交

发现少了一条数据

访问其它页面,比如: http://127.0.0.1:8000/stark/app01/userinfo/list/

发现下拉框是空的,那么它就不应该显示!

修改stark-->templates-->stark-->changelist.html,添加if判断

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
<form class="form-inline" method="post">
{% csrf_token %}
{% if action_list %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for item in action_list %}
<option value="{{ item.name }}">{{ item.text }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
{% endif %} <table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table>
</form>
</div> {% endblock %}

刷新页面,效果如下:

多选框,没有了!

其他页面,想使用批量操作,定义action_list变量,就可以了!

二、快速搜索

搜索什么内容,取哪个字段搜,都是可以定制的!

修改stark-->server-->stark.py,增加search_list变量。定义name和tel可以搜索!

增加方法get_search_list方法

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法
search_list = ['name','tel'] # 固定搜索字段 def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def get_search_list(self): # 获取搜索字段
val = []
val.extend(self.search_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response ### 处理搜索 ###
from django.db.models import Q
search_list = self.get_search_list() # ['name','tel']
q = request.GET.get('q', "") # 搜索条件
con = Q()
con.connector = "OR" # 以OR作为连接符
if q: # 判断条件不为空
for field in search_list:
# 合并条件进行查询, __contains表示使用like查询
con.children.append(('%s__contains' % field, q)) # 根据排序列表进行排序
queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

访问url,要带上q参数

http://127.0.0.1:8000/stark/app01/depart/list/?q=总

效果如下:

去掉参数,有2条数据

这样体验不好,要用户输入才行!

修改stark-->server-->stark.py,给changelist.html传入参数q

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法
search_list = ['name','tel'] # 固定搜索字段 def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def get_search_list(self): # 获取搜索字段
val = []
val.extend(self.search_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response ### 处理搜索 ###
from django.db.models import Q
search_list = self.get_search_list() # ['name','tel']
q = request.GET.get('q', "") # 搜索条件
con = Q()
con.connector = "OR" # 以OR作为连接符
if q: # 判断条件不为空
for field in search_list:
# 合并条件进行查询, __contains表示使用like查询
con.children.append(('%s__contains' % field, q)) # 根据排序列表进行排序
queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

修改 stark-->templates-->stark-->changelist.html,增加输入框

即使页面刷新,input输入框还会保留搜索的关键字!

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{#添加按钮#}
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
{#搜索框#}
<div style="float: right;">
<form method="GET" class="form-inline">
<div class="form-group">
<input class="form-control" type="text" name="q" value="{{ q }}" placeholder="关键字搜索">
<button class="btn btn-primary" type="submit">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
</div>
</form>
</div> <form class="form-inline" method="post">
{% csrf_token %}
{#批量操作#}
{% if action_list %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for item in action_list %}
<option value="{{ item.name }}">{{ item.text }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
{% endif %}
{#使用table展示数据#}
<table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table>
</form>
</div> {% endblock %}

访问url:  http://127.0.0.1:8000/stark/app01/depart/list/

效果如下:

输入关键字534,点击搜索按钮。那么url会自动带上参数,这个是GET本来就有的功能。因为GET参数在url里面!

同时结果只有一条

如果要搜索负责人呢?注意:这个字段涉及到跨表了。那么填写search_list参数时,要带上下划线

修改stark-->server-->stark.py,修改search_list变量

# 固定搜索字段,如果是跨表字段,要按照ORM语法来
search_list = ['name','tel','user__username']

刷新页面,重新制定关键字

上面是为了方便调试,把搜索字段给固定死了。它应该是可以定制的!

修改stark-->server-->stark.py,将search_list变量设置为空

# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = []

修改 app01-->stark.py,指定变量search_list

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url class UserInfoConfig(StarkConfig):
list_display = ['id', 'username'] class DepartModelForm(forms.ModelForm):
class Meta:
model = models.Depart
fields = "__all__" def clean_name(self): # 定义钩子
# print(self.cleaned_data['name'])
return self.cleaned_data['name'] class DepartConfig(StarkConfig):
list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
# model_form_class = DepartModelForm
# 批量操作
action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
# 搜索关键字
# 固定搜索字段,如果是跨表字段,要按照ORM语法来
search_list = ['name', 'tel', 'user__username'] # def get_add_btn(self): # 返回None,表示不显示添加按钮
# pass
# def changelist_view(self, request): # 重写changelist_view方法
# # 渲染自定义的列表页面
# return render(request,'stark/custom_list.html')
# def get_urls(self): # 自定义路由
# info = self.model_class._meta.app_label, self.model_class._meta.model_name
#
# urlpatterns = [
# url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
# ]
# return urlpatterns site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

重启django,刷新页面,效果同上!

如果没有定义search_list变量,那么search_list默认为空,它不应该展示!

修改stark-->server-->stark.py,给changelist.html传入参数search_list

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法
# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = [] def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def get_search_list(self): # 获取搜索字段
val = []
val.extend(self.search_list)
return val def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response ### 处理搜索 ###
from django.db.models import Q
search_list = self.get_search_list() # ['name','tel']
q = request.GET.get('q', "") # 搜索条件
con = Q()
con.connector = "OR" # 以OR作为连接符
if q: # 判断条件不为空
for field in search_list:
# 合并条件进行查询, __contains表示使用like查询
con.children.append(('%s__contains' % field, q)) # 根据排序列表进行排序
queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

修改 stark-->templates-->stark-->changelist.html,增加if 判断

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{#添加按钮#}
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
{#搜索框#}
{% if search_list %}
<div style="float: right;">
<form method="GET" class="form-inline">
<div class="form-group">
<input class="form-control" type="text" name="q" value="{{ q }}" placeholder="关键字搜索">
<button class="btn btn-primary" type="submit">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
</div>
</form>
</div>
{% endif %} <form class="form-inline" method="post">
{% csrf_token %}
{#批量操作#}
{% if action_list %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for item in action_list %}
<option value="{{ item.name }}">{{ item.text }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
{% endif %}
{#使用table展示数据#}
<table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table>
</form>
</div> {% endblock %}

修改app01-->stark.py,注释掉search_list变量

# 固定搜索字段,如果是跨表字段,要按照ORM语法来
# search_list = ['name', 'tel', 'user__username']

访问url:  http://127.0.0.1:8000/stark/app01/depart/list/

搜索框就没有了!

在changelist_view中,处理搜索的代码,可以封装成方法。

修改stark-->server-->stark.py,增加get_search_condition方法

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法
# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = [] def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def get_search_list(self): # 获取搜索字段
val = []
val.extend(self.search_list)
return val def get_search_condition(self, request): # 根据关键字,组合ORM查询语句
search_list = self.get_search_list() # ['name','tel']
q = request.GET.get('q', "") # 搜索条件
con = Q()
con.connector = "OR" # 以OR作为连接符
if q: # 判断条件不为空
for field in search_list:
# 合并条件进行查询, __contains表示使用like查询
con.children.append(('%s__contains' % field, q)) return search_list, q, con def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response ### 处理搜索 ###
search_list, q, con = self.get_search_condition(request) # 根据排序列表进行排序
queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self,func):
pass def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
url(r'^add/$', self.add_view, name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name)
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

修改app01-->stark.py,开启search_list变量

# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = ['name', 'tel', 'user__username']

重启django,刷新页面。测试搜索功能,效果同上!

三、保留原搜索条件

在内容前戏部分,讲到了 保留原搜索条件。关键点在于:搜索到结果后,跳转其他页面时,url要带上原搜索条件。

比如页面默认展示的是20条数据。搜索了2个用户,并编辑保存。跳转的页面应该也还是之前2个客户的数据,而不是20数据(返回列表首页)

页面上的a标签,button按钮。这些url是反向生成的,能不能加条件?加上原来的搜索条件?

答案是可以的!

装饰器

查看 stark-->server-->stark.py,StarkConfig类里面的get_urls方法。里面定义了4个视图函数,它们是有request变量的。除此之外,其他函数也需要使用request变量,怎么办?一个一个传?

太麻烦了,使用装饰器就可以解决!

修改 stark-->server-->stark.py,修改StarkConfig类的__init__方法,添加变量request。

增加装饰器wrapper。并定义self.request = request,对request重新赋值!

修改get_urls方法,应用装饰器

import functools
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q
from django.http import QueryDict class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site
# 定义request变量,用于非视图函数使用。
# 在wrapper装饰器中,对这个值重新赋值!
self.request = None
# url中的搜索条件,存在字典中。key为_filter
self.back_condition_key = "_filter" def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法
# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = [] def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def get_search_list(self): # 获取搜索字段
val = []
val.extend(self.search_list)
return val def get_search_condition(self, request): # 根据关键字,组合ORM查询语句
search_list = self.get_search_list() # ['name','tel']
q = request.GET.get('q', "") # 搜索条件
con = Q()
con.connector = "OR" # 以OR作为连接符
if q: # 判断条件不为空
for field in search_list:
# 合并条件进行查询, __contains表示使用like查询
con.children.append(('%s__contains' % field, q)) return search_list, q, con def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response ### 处理搜索 ###
search_list, q, con = self.get_search_condition(request) # 根据排序列表进行排序
queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self, func):
@functools.wraps(func)
def inner(request, *args, **kwargs):
self.request = request
return func(request, *args, **kwargs) return inner def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name)
return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name) if not self.request.GET:
return add_url
param_str = self.request.GET.urlencode() # q=嘉瑞&page=2
new_query_dict = QueryDict(mutable=True)
new_query_dict[self.back_condition_key] = param_str
add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk})
return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk})
return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

重启django,刷新页面,效果同上!

修改 stark-->server-->stark.py,修改reverse_add_url方法,增加搜索条件

其它3个方法reverse_del_url,reverse_edit_url,reverse_list_url也同样需要添加

import functools
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q
from django.http import QueryDict class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site
# 定义request变量,用于非视图函数使用。
# 在wrapper装饰器中,对这个值重新赋值!
self.request = None
# url中的搜索条件,存在字典中。key为_filter
self.back_condition_key = "_filter" def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法
# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = [] def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def get_search_list(self): # 获取搜索字段
val = []
val.extend(self.search_list)
return val def get_search_condition(self, request): # 根据关键字,组合ORM查询语句
search_list = self.get_search_list() # ['name','tel']
q = request.GET.get('q', "") # 搜索条件
con = Q()
con.connector = "OR" # 以OR作为连接符
if q: # 判断条件不为空
for field in search_list:
# 合并条件进行查询, __contains表示使用like查询
con.children.append(('%s__contains' % field, q)) return search_list, q, con def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response ### 处理搜索 ###
search_list, q, con = self.get_search_condition(request) # 根据排序列表进行排序
queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self, func):
@functools.wraps(func)
def inner(request, *args, **kwargs):
self.request = request
return func(request, *args, **kwargs) return inner def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name) # 获取当前请求的_filter参数,也就是跳转之前的搜索条件
origin_condition = self.request.GET.get(self.back_condition_key)
if not origin_condition: # 如果没有获取到
return list_url # 返回列表页面 # 列表地址和搜索条件拼接
list_url = "%s?%s" % (list_url, origin_condition,) return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name) if not self.request.GET: # 判断get参数为空
return add_url # 返回原url
# request.GET的数据类型为QueryDict
# 对QueryDict做urlencode编码
param_str = self.request.GET.urlencode() # 比如q=xiao&age=20
# 允许对QueryDict做修改
new_query_dict = QueryDict(mutable=True)
# 添加键值对. _filter = param_str
new_query_dict[self.back_condition_key] = param_str
# 添加url和搜索条件做拼接
add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
# 返回最终url
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk}) if not self.request.GET:
return edit_url
param_str = self.request.GET.urlencode()
new_query_dict = QueryDict(mutable=True)
new_query_dict[self.back_condition_key] = param_str
edit_url = "%s?%s" % (edit_url, new_query_dict.urlencode(),) return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk}) if not self.request.GET:
return del_url
param_str = self.request.GET.urlencode()
new_query_dict = QueryDict(mutable=True)
new_query_dict[self.back_condition_key] = param_str
del_url = "%s?%s" % (del_url, new_query_dict.urlencode(),) return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

重启django项目,访问页面: http://127.0.0.1:8000/stark/app01/depart/list/

输入搜索条件 xiao,点击搜索按钮

查看添加按钮的跳转地址,发下已经添加了搜索条件

修改app01-->stark.py,增加编辑和删除选项

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url class UserInfoConfig(StarkConfig):
list_display = ['id', 'username'] class DepartModelForm(forms.ModelForm):
class Meta:
model = models.Depart
fields = "__all__" def clean_name(self): # 定义钩子
# print(self.cleaned_data['name'])
return self.cleaned_data['name'] class DepartConfig(StarkConfig):
list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user',StarkConfig.display_edit_del]
# model_form_class = DepartModelForm
# 批量操作
action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = ['name', 'tel', 'user__username'] # def get_add_btn(self): # 返回None,表示不显示添加按钮
# pass
# def changelist_view(self, request): # 重写changelist_view方法
# # 渲染自定义的列表页面
# return render(request,'stark/custom_list.html')
# def get_urls(self): # 自定义路由
# info = self.model_class._meta.app_label, self.model_class._meta.model_name
#
# urlpatterns = [
# url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
# ]
# return urlpatterns site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

刷新页面,查看编辑和删除的herf属性

发送链接地址,也加上了搜索条件。

点击编辑按钮,此时的url地址为:

http://127.0.0.1:8000/stark/app01/depart/1/change/?_filter=q%3Dxiao

修改一条数据,点击提交

页面跳转地址,此时之前的搜索条件还在!

http://127.0.0.1:8000/stark/app01/depart/list/?q=xiao

效果如下:

如果有分页功能,这个搜索条件,还要不要?当然要啊!

四、自定义分页

展示数据页面数据过多,一般会采用分页处理。

这里使用的分页,不是django自带的分页器(paginator) ,使用的是自定义分页类。为什么?虽然使用paginator也可以完成。但是我们要做的是,跨框架的组件。即使在flask框架中,也依然能使用!

进入stark应用目录,创建文件夹utils,它表示工具类。在此文件夹下创建文件pagination.py

注意:分页跳转时,它是带有搜索条件的

这里没有使用request.GET.urlencode(),为了做到通用性。其他框架也可以使用!

"""
分页组件
"""
from urllib.parse import urlencode class Pagination(object):
def __init__(self, current_page, all_count, base_url,query_params, per_page=10, pager_page_count=11):
"""
分页初始化
:param current_page: 当前页码
:param per_page: 每页显示数据条数
:param all_count: 数据库中总条数
:param base_url: 基础URL
:param query_params: QueryDict对象,内部含所有当前URL的原条件
:param pager_page_count: 页面上最多显示的页码数量
"""
self.base_url = base_url
try:
self.current_page = int(current_page)
if self.current_page <= 0: # 当前页码数不能小于等于0
raise Exception()
except Exception as e:
self.current_page = 1
self.query_params = query_params
self.per_page = per_page
self.all_count = all_count
self.pager_page_count = pager_page_count
pager_count, b = divmod(all_count, per_page)
if b != 0:
pager_count += 1
self.pager_count = pager_count half_pager_page_count = int(pager_page_count / 2)
self.half_pager_page_count = half_pager_page_count @property
def start(self):
"""
数据获取值起始索引
:return:
"""
return (self.current_page - 1) * self.per_page @property
def end(self):
"""
数据获取值结束索引
:return:
"""
return self.current_page * self.per_page def page_html(self):
"""
生成HTML页码
:return:
"""
# 如果数据总页码pager_count<11 pager_page_count
if self.pager_count < self.pager_page_count:
pager_start = 1
pager_end = self.pager_count
else:
# 数据页码已经超过11
# 判断: 如果当前页 <= 5 half_pager_page_count
if self.current_page <= self.half_pager_page_count:
pager_start = 1
pager_end = self.pager_page_count
else:
# 如果: 当前页+5 > 总页码
if (self.current_page + self.half_pager_page_count) > self.pager_count:
pager_end = self.pager_count
pager_start = self.pager_count - self.pager_page_count + 1
else:
pager_start = self.current_page - self.half_pager_page_count
pager_end = self.current_page + self.half_pager_page_count page_list = [] if self.current_page <= 1:
prev = '<li><a href="#">上一页</a></li>'
else:
self.query_params['page'] = self.current_page - 1
prev = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url,self.query_params.urlencode())
page_list.append(prev)
for i in range(pager_start, pager_end + 1):
self.query_params['page'] = i
if self.current_page == i:
tpl = '<li class="active"><a href="%s?%s">%s</a></li>' % (
self.base_url, self.query_params.urlencode(), i,)
else:
tpl = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.query_params.urlencode(), i,)
page_list.append(tpl) if self.current_page >= self.pager_count:
nex = '<li><a href="#">下一页</a></li>'
else:
self.query_params['page'] = self.current_page + 1
nex = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.query_params.urlencode(),)
page_list.append(nex)
page_str = "".join(page_list)
return page_str

打开表app01_depart,添加几条数据

修改app01-->stark.py,增加id显示

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url class UserInfoConfig(StarkConfig):
list_display = ['id', 'username'] class DepartModelForm(forms.ModelForm):
class Meta:
model = models.Depart
fields = "__all__" def clean_name(self): # 定义钩子
# print(self.cleaned_data['name'])
return self.cleaned_data['name'] class DepartConfig(StarkConfig):
list_display = [StarkConfig.display_checkbox,'id','name', 'tel', 'user',StarkConfig.display_edit_del]
# model_form_class = DepartModelForm
# 批量操作
action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = ['name', 'tel', 'user__username'] # def get_add_btn(self): # 返回None,表示不显示添加按钮
# pass
# def changelist_view(self, request): # 重写changelist_view方法
# # 渲染自定义的列表页面
# return render(request,'stark/custom_list.html')
# def get_urls(self): # 自定义路由
# info = self.model_class._meta.app_label, self.model_class._meta.model_name
#
# urlpatterns = [
# url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
# ]
# return urlpatterns site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

访问url:  http://127.0.0.1:8000/stark/app01/depart/list/

效果如下:

修改 stark-->server-->stark.py,处理分页,并传入参数page给changelist.html

import functools
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q
from django.http import QueryDict class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site
# 定义request变量,用于非视图函数使用。
# 在wrapper装饰器中,对这个值重新赋值!
self.request = None
# url中的搜索条件,存在字典中。key为_filter
self.back_condition_key = "_filter" def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法
# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = [] def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def get_search_list(self): # 获取搜索字段
val = []
val.extend(self.search_list)
return val def get_search_condition(self, request): # 根据关键字,组合ORM查询语句
search_list = self.get_search_list() # ['name','tel']
q = request.GET.get('q', "") # 搜索条件
con = Q()
con.connector = "OR" # 以OR作为连接符
if q: # 判断条件不为空
for field in search_list:
# 合并条件进行查询, __contains表示使用like查询
con.children.append(('%s__contains' % field, q)) return search_list, q, con def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response ### 处理搜索 ###
search_list, q, con = self.get_search_condition(request)
# ##### 处理分页 #####
from stark.utils.pagination import Pagination
# 总条数
total_count = self.model_class.objects.filter(con).count()
# 复制GET参数
query_params = request.GET.copy()
# 允许编辑
query_params._mutable = True
# 使用分页类Pagination,传入参数。每页显示3条
page = Pagination(request.GET.get('page'), total_count, request.path_info, query_params, per_page=3) # 根据排序列表进行排序,以及分页功能
queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())[page.start:page.end]
### 批量操作 ###
action_list = self.get_action_list()
# 获取函数名以及text属性
action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
# print(action_list)
### 添加按钮 ###
add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列
header_list = [] # 定义头部,用来显示verbose_name
if list_display:
for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
# 执行函数,默认显示中文
verbose_name = name_or_func(self,header=True)
else:
# 获取指定字段的verbose_name
verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name)
else:
# 如果list_display为空,添加表名
header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset:
# 这里的row是对象,它表示表里面的一条数据
row_list = [] # 展示每一行数据
if not list_display: # 如果不在list_display里面
# 添加对象
row_list.append(row)
body_list.append(row_list)
continue for name_or_func in list_display:
if isinstance(name_or_func,FunctionType):
val = name_or_func(self,row=row) # 执行函数获取,传递row对象
else:
# 使用反射获取对象的值
val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数
return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list,'page':page}) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self, func):
@functools.wraps(func)
def inner(request, *args, **kwargs):
self.request = request
return func(request, *args, **kwargs) return inner def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name) # 获取当前请求的_filter参数,也就是跳转之前的搜索条件
origin_condition = self.request.GET.get(self.back_condition_key)
if not origin_condition: # 如果没有获取到
return list_url # 返回列表页面 # 列表地址和搜索条件拼接
list_url = "%s?%s" % (list_url, origin_condition,) return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name) if not self.request.GET: # 判断get参数为空
return add_url # 返回原url
# request.GET的数据类型为QueryDict
# 对QueryDict做urlencode编码
param_str = self.request.GET.urlencode() # 比如q=xiao&age=20
# 允许对QueryDict做修改
new_query_dict = QueryDict(mutable=True)
# 添加键值对. _filter = param_str
new_query_dict[self.back_condition_key] = param_str
# 添加url和搜索条件做拼接
add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
# 返回最终url
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk}) if not self.request.GET:
return edit_url
param_str = self.request.GET.urlencode()
new_query_dict = QueryDict(mutable=True)
new_query_dict[self.back_condition_key] = param_str
edit_url = "%s?%s" % (edit_url, new_query_dict.urlencode(),) return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk}) if not self.request.GET:
return del_url
param_str = self.request.GET.urlencode()
new_query_dict = QueryDict(mutable=True)
new_query_dict[self.back_condition_key] = param_str
del_url = "%s?%s" % (del_url, new_query_dict.urlencode(),) return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

修改 stark-->templates-->stark-->changelist.html,增加分页标签

{% extends 'stark/layout.html' %}

{% block content %}
<h1>列表页面</h1>
<div>
{#添加按钮#}
{% if add_btn %}
<div style="margin: 5px 0;">
{{ add_btn }}
</div>
{% endif %}
{#搜索框#}
{% if search_list %}
<div style="float: right;">
<form method="GET" class="form-inline">
<div class="form-group">
<input class="form-control" type="text" name="q" value="{{ q }}" placeholder="关键字搜索">
<button class="btn btn-primary" type="submit">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
</div>
</form>
</div>
{% endif %} <form class="form-inline" method="post">
{% csrf_token %}
{#批量操作#}
{% if action_list %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for item in action_list %}
<option value="{{ item.name }}">{{ item.text }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
{% endif %}
{#使用table展示数据#}
<table class="table table-bordered">
<thead>
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for row_list in body_list %}
<tr>
{% for col in row_list %}
<td>{{ col }}</td>
{% endfor %} </tr>
{% endfor %}
</tbody>
</table>
{#分页展示#}
<nav aria-label="Page navigation">
<ul class="pagination">
{{ page.page_html|safe }}
</ul>
</nav>
</form>
</div> {% endblock %}

基本测试

重启django,刷新页面,效果如下:

测试搜索条件

五、拆分代码

上面的 stark-->server-->stark.py,代码太冗长。不方便扩展功能!

要用面向对象的封装特性,来做代码拆分。

首先拆分changelist_view方法的render,它传了很多参数!代码太长!

修改stark-->server-->stark.py,添加ChangeList类。将changelist_view中的相关变量移植过来

import functools
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q
from django.http import QueryDict class ChangeList(object):
"""
封装列表页面需要的所有功能
"""
def __init__(self,config,queryset,q,search_list,page):
### 处理搜索 ###
self.q = q # 搜索条件
self.search_list = search_list # 搜索字段
self.page = page # 分页
# 配置参数
self.config = config
# 批量操作
self.action_list = [{'name': func.__name__, 'text': func.text} for func in config.get_action_list()]
# 添加按钮
self.add_btn = config.get_add_btn()
# ORM执行结果
self.queryset = queryset
# 显示的列
self.list_display = config.get_list_display() class StarkConfig(object):
def __init__(self,model_class,site):
self.model_class = model_class
self.site = site
# 定义request变量,用于非视图函数使用。
# 在wrapper装饰器中,对这个值重新赋值!
self.request = None
# url中的搜索条件,存在字典中。key为_filter
self.back_condition_key = "_filter" def display_checkbox(self,row=None,header=False): # 显示复选框
if header:
# 输出中文
return "选择"
# 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk) def display_edit(self, row=None, header=False):
if header:
return "编辑" return mark_safe(
'<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row)) def display_del(self, row=None, header=False):
if header:
return "删除" return mark_safe(
'<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False):
if header:
return "操作"
tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
""" % (self.reverse_edit_url(row), self.reverse_del_url(row),)
return mark_safe(tpl) def multi_delete(self, request): # 批量删除
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" # 添加自定义属性text def multi_init(self,request): # 批量初始化
print('批量初始化') multi_init.text = "批量初始化" # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义
list_display = [] # 定义显示的列,由用户自定义
model_form_class = None # form组件需要的model_class
action_list = [] # 批量操作方法
# 搜索字段,如果是跨表字段,要按照ORM语法来
search_list = [] def get_order_by(self): # 获取排序列表
return self.order_by def get_list_display(self): # 获取显示的列
return self.list_display def get_add_btn(self): # 显示添加按钮
return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url()) def get_model_form_class(self):
"""
获取ModelForm类
:return:
"""
if self.model_form_class:
return self.model_form_class class AddModelForm(forms.ModelForm):
class Meta:
model = self.model_class
fields = "__all__" return AddModelForm def get_action_list(self): # 获取批量操作方法
val = [] # 空列表
# 扩展列表的元素
val.extend(self.action_list)
return val def get_action_dict(self): # 获取匹配操作字典
val = {}
for item in self.action_list:
# 以方法名为key
val[item.__name__] = item
return val def get_search_list(self): # 获取搜索字段
val = []
val.extend(self.search_list)
return val def get_search_condition(self, request): # 根据关键字,组合ORM查询语句
search_list = self.get_search_list() # ['name','tel']
q = request.GET.get('q', "") # 搜索条件
con = Q()
con.connector = "OR" # 以OR作为连接符
if q: # 判断条件不为空
for field in search_list:
# 合并条件进行查询, __contains表示使用like查询
con.children.append(('%s__contains' % field, q)) return search_list, q, con def changelist_view(self, request):
"""
所有URL查看列表页面
:param request:
:return:
"""
if request.method == 'POST':
action_name = request.POST.get('action')
action_dict = self.get_action_dict()
if action_name not in action_dict:
return HttpResponse('非法请求') response = getattr(self, action_name)(request)
if response:
return response ### 处理搜索 ###
search_list, q, con = self.get_search_condition(request)
# ##### 处理分页 #####
from stark.utils.pagination import Pagination
# 总条数
total_count = self.model_class.objects.filter(con).count()
# 复制GET参数
query_params = request.GET.copy()
# 允许编辑
query_params._mutable = True
# 使用分页类Pagination,传入参数。每页显示3条
page = Pagination(request.GET.get('page'), total_count, request.path_info, query_params, per_page=3) # 根据排序列表进行排序,以及分页功能
queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())[page.start:page.end] cl = ChangeList(self, queryset, q, search_list, page)
context = {
'cl': cl
} # 注意:要传入参数
return render(request,'stark/changelist.html',context) def add_view(self, request):
"""
所有的添加页面,都在此方法处理
使用ModelForm实现
:param request:
:return:
"""
# 添加数据,使用ModelForm
AddModelForm = self.get_model_form_class() if request.method == "GET":
form = AddModelForm()
return render(request,'stark/change.html',{'form':form}) form = AddModelForm(request.POST) # 接收POST数据
if form.is_valid(): # 验证数据
form.save() # 自动保存数据
# 反向生成url,跳转到列表页面
return redirect(self.reverse_list_url())
# 渲染页面,此时会保存表单数据
return render(request, 'stark/change.html', {'form': form}) def change_view(self, request, pk):
"""
所有编辑页面
:param request:
:param pk:
:return:
"""
# 查看单条数据
obj = self.model_class.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('数据不存在')
# 获取model_form类
ModelFormClass = self.get_model_form_class()
if request.method == 'GET':
# instance表示生成默认值
form = ModelFormClass(instance=obj)
# 渲染页面,添加和修改可以共用一个一个模板文件
return render(request, 'stark/change.html', {'form': form})
# instance = obj 表示指定给谁做修改
form = ModelFormClass(data=request.POST, instance=obj)
if form.is_valid():
form.save() # 修改数据
# 跳转到列表页面
return redirect(self.reverse_list_url())
return render(request, 'stark/change.html', {'form': form}) def delete_view(self, request, pk):
"""
所有删除页面
:param request:
:param pk:
:return:
"""
if request.method == "GET":
# cancel_url表示用户点击取消时,跳转到列表页面
return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
# 定位单条数据,并删除!
self.model_class.objects.filter(pk=pk).delete()
return redirect(self.reverse_list_url()) def wrapper(self, func):
@functools.wraps(func)
def inner(request, *args, **kwargs):
self.request = request
return func(request, *args, **kwargs) return inner def get_urls(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
url(r'^(?P<pk>\d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
url(r'^(?P<pk>\d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
] extra = self.extra_url()
if extra: # 判断变量不为空
# 扩展路由
urlpatterns.extend(extra) # print(urlpatterns)
return urlpatterns def extra_url(self): # 额外的路由,由调用者重构
pass def reverse_list_url(self): # 反向生成访问列表的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
list_url = reverse(name) # 获取当前请求的_filter参数,也就是跳转之前的搜索条件
origin_condition = self.request.GET.get(self.back_condition_key)
if not origin_condition: # 如果没有获取到
return list_url # 返回列表页面 # 列表地址和搜索条件拼接
list_url = "%s?%s" % (list_url, origin_condition,) return list_url def reverse_add_url(self): # 反向生成添加url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
name = '%s:%s_%s_add' % (namespace, app_label, model_name)
add_url = reverse(name) if not self.request.GET: # 判断get参数为空
return add_url # 返回原url
# request.GET的数据类型为QueryDict
# 对QueryDict做urlencode编码
param_str = self.request.GET.urlencode() # 比如q=xiao&age=20
# 允许对QueryDict做修改
new_query_dict = QueryDict(mutable=True)
# 添加键值对. _filter = param_str
new_query_dict[self.back_condition_key] = param_str
# 添加url和搜索条件做拼接
add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
# 返回最终url
return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url
app_label = self.model_class._meta.app_label # app名
model_name = self.model_class._meta.model_name # 表名
namespace = self.site.namespace # 命名空间
# 拼接字符串,这里为change
name = '%s:%s_%s_change' % (namespace, app_label, model_name)
# 反向生成url,传入参数pk=row.pk
edit_url = reverse(name, kwargs={'pk': row.pk}) if not self.request.GET:
return edit_url
param_str = self.request.GET.urlencode()
new_query_dict = QueryDict(mutable=True)
new_query_dict[self.back_condition_key] = param_str
edit_url = "%s?%s" % (edit_url, new_query_dict.urlencode(),) return edit_url def reverse_del_url(self, row): # 反向生成删除行内容的url
app_label = self.model_class._meta.app_label
model_name = self.model_class._meta.model_name
namespace = self.site.namespace
# 注意:这里为del
name = '%s:%s_%s_del' % (namespace, app_label, model_name)
del_url = reverse(name, kwargs={'pk': row.pk}) if not self.request.GET:
return del_url
param_str = self.request.GET.urlencode()
new_query_dict = QueryDict(mutable=True)
new_query_dict[self.back_condition_key] = param_str
del_url = "%s?%s" % (del_url, new_query_dict.urlencode(),) return del_url @property
def urls(self):
return self.get_urls() class AdminSite(object):
def __init__(self):
self._registry = {}
self.app_name = 'stark'
self.namespace = 'stark' def register(self,model_class,stark_config=None):
# not None的结果为Ture
if not stark_config:
# 也就是说,当其他应用调用register时,如果不指定stark_config参数
# 那么必然执行下面这段代码!
# stark_config和StarkConfig是等值的!都能实例化
stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class
# self指的是AdminSite类
self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典
"""
{
app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
app02.models.Role:RoleConfig(app02.models.Role)
}
""" # for k, v in self._registry.items():
# print(k,v) def get_urls(self):
urlpatterns = [] for k, v in self._registry.items():
# k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
# k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
app_label = k._meta.app_label
model_name = k._meta.model_name
urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property
def urls(self):
# 调用get_urls方法
# self.app_name和self.namespace值是一样的,都是stark
return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类

inclusion_tag+yield

列表页面中的table表格数据,应该使用inclusion_tag+yield

进入stark应用目录,创建目录templatetags,目录名必须是这个!在此目录新建文件stark.py

from django.template import Library
from types import FunctionType register = Library() def header_list(cl):
"""
表头
:param cl:
:return:
"""
if cl.list_display:
for name_or_func in cl.list_display:
if isinstance(name_or_func, FunctionType):
verbose_name = name_or_func(cl.config, header=True)
else:
verbose_name = cl.config.model_class._meta.get_field(name_or_func).verbose_name
yield verbose_name
else:
yield cl.config.model_class._meta.model_name def body_list(cl):
"""
表格内容
:param cl:
:return:
"""
for row in cl.queryset:
row_list = []
if not cl.list_display:
row_list.append(row)
yield row_list
continue
for name_or_func in cl.list_display:
if isinstance(name_or_func, FunctionType):
val = name_or_func(cl.config, row=row)
else:
val = getattr(row, name_or_func)
row_list.append(val)
yield row_list @register.inclusion_tag('stark/table.html')
def table(cl): return {'header_list':header_list(cl),'body_list':body_list(cl)}

修改 stark-->templates-->stark-->custom_list.html,使用inclusion_tag

{% extends 'stark/layout.html' %}
{% load stark %} {% block content %}
<h1>列表页面</h1>
<div>
{#添加按钮#}
{% if cl.add_btn %}
<div style="margin: 5px 0;">
{{ cl.add_btn }}
</div>
{% endif %}
{#搜索框#}
{% if cl.search_list %}
<div style="float: right;">
<form method="GET" class="form-inline">
<div class="form-group">
<input class="form-control" type="text" name="q" value="{{ cl.q }}" placeholder="关键字搜索">
<button class="btn btn-primary" type="submit">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
</div>
</form>
</div>
{% endif %} <form class="form-inline" method="post">
{% csrf_token %}
{#批量操作#}
{% if cl.action_list %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for item in cl.action_list %}
<option value="{{ item.name }}">{{ item.text }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
{% endif %}
{#使用table展示数据#}
{% table cl %}
{#分页展示#}
<nav aria-label="Page navigation">
<ul class="pagination">
{{ cl.page.page_html|safe }}
</ul>
</nav>
</form>
</div> {% endblock %}

务必重启django,因为必须重启,inclusion_tag才会生效!

访问url: http://127.0.0.1:8000/stark/app01/depart/list

效果如下:

总结:

1. 批量操作[扩展]
- 反射
- __name__
- 一切皆对象
def multi_delete(self,request):
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" 2. 搜索[扩展]
- Q
- __contains 3. 保留原搜索条件
- QueryDict,request.GET/request.POST
- urlencode()
- _mutable = True
- 深拷贝
- urllib.parse.urlencode 4. 分页
- 分页组件
- 保留原条件 5. 拆分
- ChangeList类封装
- inclusion_tag
- 生成器

完整代码,请参数github:

https://github.com/987334176/luffy_stark/archive/v1.2.zip

python 全栈开发,Day115(urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码)的更多相关文章

  1. python全栈开发day115、116-websocket、websocket原理、websocket加解密、简单问答机器人实现

    1.websocket 1.websocket 与轮询 轮询: 不断向服务器发起询问,服务器还不断的回复 浪费带宽,浪费前后端资源 保证数据的实时性 长轮询: 1.客户端向服务器发起消息,服务端轮询, ...

  2. python全栈开发day64-模板-变量和(.)的使用,filters和自定义filter

    一.上周内容回顾 day64 内容回顾: 1. 所有的django命令 1. 安装 pip install django==1.11.14 pip install -i 源 django==1.11. ...

  3. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

  4. python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)

    昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...

  5. 学习笔记之Python全栈开发/人工智能公开课_腾讯课堂

    Python全栈开发/人工智能公开课_腾讯课堂 https://ke.qq.com/course/190378 https://github.com/haoran119/ke.qq.com.pytho ...

  6. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  7. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  8. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  9. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

随机推荐

  1. PostgreSQL操作-psql基本命令

    一.建立数据库连接----------------接入PostgreSQL数据库: psql -h IP地址 -p 端口 -U 数据库名 之后会要求输入数据库密码 二.访问数据库 1.列举数据库:\l ...

  2. 5、JDBC-元信息

    DatabaseMetaData:描述数据库的元数据对象 获取所有数据库 import org.junit.jupiter.api.AfterEach; import org.junit.jupite ...

  3. C# 优化程序的四十七种方法

    一.用属性代替可访问的字段 1..NET数据绑定只支持数据绑定,使用属性可以获得数据绑定的好处: 2.在属性的get和set访问器重可使用lock添加多线程的支持. 二.readonly(运行时常量) ...

  4. Calendar 日历类的时间操作

    我们经常会涉及到对时间的处理,例如登陆网站,我们会看到网站首页显示XXX,欢迎您!今天是XXXX年....某些网站会记录下用户登陆的时间,比如银行的一些网站,对于这些经常需要处理的问题,Java中提供 ...

  5. adb 安装apk到只当设备

    1.adb devices 2,选择指定的设备,如上面的那个 然后:adb -s 292be8597d94 install *apk

  6. JAVA单链表的实现-不带头结点且没有尾指针

    本程序采用JAVA语言实现了线性表的链式实现.首先定义了线性表的接口ListInterface,然后LList类实现了ListInterface完成了链表的实现. 本实现中,链表是不带表头结点的,且有 ...

  7. 大量界面刷新时手动Dispose也是有必要的

    在winform窗体上拖一个flowLayoutPane,一个Button,项目中再创建一个用户控件UcControl,用户控件上放几十个子控件 private void button1_Click( ...

  8. @ImportResource 导入Spring 的xml配置文件

    在配置类尚标注此注解,等同于spring配置文件中的 <import resource="beans.xml"/> Spring Boot里面没有Spring的配置文件 ...

  9. Centos 6.5 安装Python 3.7

    文档下载地址: https://files.cnblogs.com/files/flashBoxer/Centos6.5%E5%AE%89%E8%A3%85Python3.7.xml

  10. 2017-2018-2 20155303『网络对抗技术』Exp8:Web基础

    2017-2018-2 『网络对抗技术』Exp8:Web基础 --------CONTENTS-------- 一.原理与实践说明 1.实践具体要求 2.基础问题回答 二.实践过程记录 1.Web前端 ...