自定义admin组件

时间:2022-06-26 16:54:09

配置路由

1 新建一个项目, 创建一个app01和stark应用,stark创建一个service包,并在service下创建stark.py。然后注册app

自定义admin组件

2 仿照site.py的注册代码,写stark.py代码:

class ModelStark(object):
def __init__(self, model, site):
self.model = model
self.site = site class StarkSite(object):
def __init__(self):
self._registry = {} def register(self, model, stark_class=None):
if not stark_class:
stark_class = ModelStark
self._registry[model] = stark_class(model, self) site = StarkSite()

3 stark应用下的app.py代码:

from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules class StarkConfig(AppConfig):
name = 'stark' def ready(self):
autodiscover_modules('stark')

4 app01 下model.py:

class UserInfo(models.Model):
name=models.CharField(max_length=32)
age=models.IntegerField() def __str__(self):
return self.name class Book(models.Model):
title=models.CharField(max_length=32) def __str__(self):
return self.title

5 在app01下stark.py的注册模型:

from stark.service.stark import site, ModelStark
from .models import * site.register(Book)
site.register(UserInfo) print("_registry", site._registry)

6 在项目的urls.py写路由。

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

7 在service下stark.py写整套urls路由

from django.conf.urls import url
from django.shortcuts import HttpResponse,render class ModelStark(object):
def __init__(self, model, site):
self.model = model
self.site = site class StarkSite(object):
def __init__(self):
self._registry = {} def register(self, model, stark_class=None):
if not stark_class:
stark_class = ModelStark
self._registry[model] = stark_class(model, self) def add(self, request):
return HttpResponse("add") def delete(self, request, id):
return HttpResponse("delete") def change(self, request, id):
return HttpResponse("change") def list_view(self, request):
return HttpResponse("list_view") def get_urls2(self):
temp = []
# 添加每个app/model的增删改查url
temp.append(url(r'^add/', self.add))
temp.append(url(r'^(\d+)/delete/', self.delete))
temp.append(url(r'^(\d+)/change/', self.change))
temp.append(url(r'^$', self.list_view))
return temp @property
def urls2(self):
return self.get_urls2(), None, None def get_urls(self):
temp = []
for model, stark_class_obj in self._registry.items():
model_name = model._meta.model_name
app_label = model._meta.app_label
# 添加路由
# url(r'app01/user/',)
temp.append(url(r'^%s/%s/' % (app_label, model_name), self.urls2))
return temp @property
def urls(self):
return self.get_urls(), None, None site = StarkSite()

此时运行项目,就会有stark开头的8条路由。但是每个模型的增删改查的返回数据一样,我们要做到根据不同的app和model返回对应的数据,因此要把增删改查的路由重新划分。

8 在service下stark.py修改urls路由,此时的代码:

from django.conf.urls import url
from django.shortcuts import HttpResponse,render class ModelStark(object):
def __init__(self, model, site):
self.model = model
self.site = site def add(self, request):
return HttpResponse("add") def delete(self, request, id):
return HttpResponse("delete") def change(self, request, id):
return HttpResponse("change") def list_view(self, request):
return HttpResponse("list_view") def get_urls2(self):
temp = []
# 添加每个app/model的增删改查url
temp.append(url(r'^add/', self.add))
temp.append(url(r'^(\d+)/delete/', self.delete))
temp.append(url(r'^(\d+)/change/', self.change))
temp.append(url(r'^$', self.list_view))
return temp @property
def urls2(self):
return self.get_urls2(), None, None class StarkSite(object):
def __init__(self):
self._registry = {} def register(self, model, stark_class=None):
if not stark_class:
stark_class = ModelStark
self._registry[model] = stark_class(model, self) def get_urls(self):
temp = []
for model, stark_class_obj in self._registry.items():
model_name = model._meta.model_name
app_label = model._meta.app_label
# 添加路由
# url(r'app01/user/',)
temp.append(url(r'^%s/%s/' % (app_label, model_name), stark_class_obj.urls2))
return temp @property
def urls(self):
return self.get_urls(), None, None site = StarkSite()

因为每个app和模型类的数据不一样以及各自定制的显示方式不一样,所以对于增删改查就要分开对待,因此就把增删改查放到ModelStark类中,既然四个视图函数都放到ModelStark中了,把调用他们的get_urls2也放进去,这样方便调用,其实就是把self和调用对象保持一致。get_urls2都放进去了,urls2也顺便放进去吧,正好他们是一套。

把他们放到ModelStark的目的就是根据不同的app和model以及他们在注册时定制的配置类显示对应的数据和展示方式。下面的增删改查都会在ModelStark类中进行配置,并且有一个对象会一致被调用:stark_class_obj

假设app01 下stark.py为Book模型定制一个配置类,Userinfo不配置:

class BookConfig(ModelStark):
pass site.register(Book, BookConfig)
site.register(UserInfo)

自定义admin组件

此时的路由算是配置好了,后面再设置反向解析,下面开始配置视图。

list_display

首先先看下ModelStark类中的self.model

自定义admin组件

1 向UserIfo表中,填充一些数据。并写Userinfo配置类:

class UserConfig(ModelStark):
list_display = ["name", "age"]

在service/stark.py的ModelStark类中添加代码:

class ModelStark(object):
list_display = [] ..... def list_view(self, request):
print(self.model) # UserInfo
print(self.list_display) # ["name", "age"]
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....]
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
for obj in data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.list_display: # 获取每一个要展示的字段 ["name", "age"]
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
temp.append(val)
new_data_list.append(temp)
return render(request, 'list.html', locals()) ........

添加list.html文件,代码:

<body>
<h3>数据列表</h3>
<div class="container">
<div class="row">
<div class="col-md-9">
<table class="table table-bordered table-striped">
<thead></thead>
<tbody>
{% for data_list in new_data_list %}
<tr>
{% for data in data_list %}
<td>{{ data }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </body>

访问/strak/app01/userinfo,此时页面就能显示数据了

自定义admin组件

2 此时想在每一列的后面放在编辑按钮。

在app01/strak.py中给添加一个方法,使每一条数据都有一个编辑按钮。

from django.utils.safestring import mark_safe
........ class UserConfig(ModelStark): def edit(self):
user_id = obj.id
return mark_safe("<a>编辑</a>") list_display = ["name", "age", edit] ......

在service/stark.py的list_view中修改代码:

 def list_view(self, request):
print(self.model) # UserInfo
print(self.list_display) # ["name", "age"]
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....]
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
for obj in data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.list_display: # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self)
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
temp.append(val)
new_data_list.append(temp)
return render(request, 'list.html', locals())

此时访问/strak/app01/userinfo

自定义admin组件

3 此时每一列的后面都有一个编辑连接,但是并不能跳转到对应编辑页面,因此修改url,修改app01/strak.py中的edit方法。

    def edit(self, obj):
user_id = obj.id
return mark_safe("<a href='/stark/app01/userinfo/%s/change/'>编辑</a>" % user_id)

edit方法需要一个obj参数来获取用户id,在service/stark.py的list_view中给它传递,

    def list_view(self, request):
print(self.model) # UserInfo
print(self.list_display) # ["name", "age"]
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....]
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
for obj in data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.list_display: # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self, obj) # 给自定义方法传递参数
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
temp.append(val)
new_data_list.append(temp)
return render(request, 'list.html', locals())

此时访问/strak/app01/userinfo,发现每一个编辑按钮都能跳到对应的编辑页面。

4 但是这样写url地址并不是最完美的,然而这样也行,为了更加完美,那就使用反向解析。

修改service/stark.py中get_urls2:

    def get_urls2(self):
temp = [] # 添加每个app/model的增删改查url
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add, name="%s_%s_add" % (app_label, model_name)))
temp.append(url(r'^(\d+)/delete/', self.delete, name="%s_%s_delete" % (app_label, model_name)))
temp.append(url(r'^(\d+)/change/', self.change, name="%s_%s_change" % (app_label, model_name)))
temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name)))
return temp

修改app01/strak.py中的edit:

from django.core.urlresolvers import reverse

class UserConfig(ModelStark):

    def edit(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>编辑</a>" % _url)

此时再访问/strak/app01/userinfo,发现每一个编辑按钮都能跳到对应的编辑页面。

5 既然编辑都完成了,那就再添加一个删除和checkbox,简直易如反掌。

修改app01/strak.py:

class UserConfig(ModelStark):

    def edit(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>编辑</a>" % _url) def deletes(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>删除</a>" % _url) def checkbox(self, obj): return mark_safe("<input type='checkbox'>") list_display = [ checkbox, "name", "age", edit, deletes]

此时访问/strak/app01/userinfo,页面效果:

自定义admin组件

5 但是,如果某个模型类没有定制自己的配置类,也能展示自己的默认字段,并且也有复选框、编辑和删除功能。

把app01/strak.py中的edit、delete、checkbox三个方法全部剪切放到service/stark.py的ModelStark类中,然后把list_display改为list_display = ["__str__"]。为了保证每个模型字段和checkbox、编辑、删除的展示顺序,定义一个new_list_display方法,动态的获取所有的展示字段。具体代码:

class ModelStark(object):
list_display = ["__str__"] def __init__(self, model, site):
self.model = model
self.site = site def edit(self, obj):
"""编辑按钮"""
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>编辑</a>" % _url) def deletes(self, obj):
"""删除按钮"""
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>删除</a>" % _url) def checkbox(self, obj):
"""复选框"""
return mark_safe("<input type='checkbox'>") def add(self, request):
return HttpResponse("add") def delete(self, request, id):
return HttpResponse("delete") def change(self, request, id):
return HttpResponse("change") def new_list_display(self):
temp = []
temp.append(ModelStark.checkbox)
temp.extend(self.list_display)
temp.append(ModelStark.edit)
temp.append(ModelStark.deletes)
return temp def list_view(self, request):
"""列表展示页"""
print(self.model) # UserInfo
print(self.list_display) # ["name", "age"]
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....]
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
for obj in data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self, obj) # 给自定义方法传递参数
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
temp.append(val)
new_data_list.append(temp)
return render(request, 'list.html', locals()) ..........

此时访问/strak/app01/userinfo和/strak/app01/book,都能展示复选框、默认字段、编辑、删除。

现在表单数据有了,但是表头还没有,那获取表头数据。如果是复选框列,也在表头发一个复选框;如果是编辑或者删除,表头就显示操作;如果是其他就显示字段名称。

6 修改service/stark.py中checkbox、edit、deletes方法,判断获取的是表头还是表单

    def edit(self, obj=None, header=False):
"""编辑按钮"""
if header: # 判断是不是表头
return "操作"
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>编辑</a>" % _url) def deletes(self, obj=None, header=False):
"""删除按钮"""
if header: # 判断是不是表头
return "操作"
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>删除</a>" % _url) def checkbox(self, obj=None, header=False):
"""复选框"""
if header: # 判断是不是表头
return mark_safe("<input id='all_select' type='checkbox'>")
return mark_safe("<input class='select' type='checkbox'>")

7 修改service/stark.py中view_list方法,添加获取表头的代码;

# 获取表头信息
# 定义一个列表,格式:["复选框", name , age, "操作"....]
head_list = []
for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......]
if callable(field):
val = field(self, header=True)
head_list.append(val)
else:
if field == '__str__':
val = self.model._meta.model_name.upper() # 返回模型类的名称
else:
val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称
head_list.append(val)

此时ModelStark类的代码:

class ModelStark(object):
list_display = ["__str__"] def __init__(self, model, site):
self.model = model
self.site = site def edit(self, obj=None, header=False):
"""编辑按钮"""
if header: # 判断是不是表头
return "操作"
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>编辑</a>" % _url) def deletes(self, obj=None, header=False):
"""删除按钮"""
if header: # 判断是不是表头
return "操作"
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.id,))
return mark_safe("<a href='%s'>删除</a>" % _url) def checkbox(self, obj=None, header=False):
"""复选框"""
if header: # 判断是不是表头
return mark_safe("<input id='all_select' type='checkbox'>")
return mark_safe("<input class='select' type='checkbox'>") def add(self, request):
return HttpResponse("add") def delete(self, request, id):
return HttpResponse("delete") def change(self, request, id):
return HttpResponse("change") def new_list_display(self):
temp = []
temp.append(ModelStark.checkbox)
temp.extend(self.list_display)
temp.append(ModelStark.edit)
temp.append(ModelStark.deletes)
return temp def list_view(self, request):
"""列表展示页"""
print(self.model) # UserInfo
print(self.list_display) # ["name", "age"]
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头信息
# 定义一个列表,格式:["复选框", name , age, "操作"....]
head_list = []
for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......]
if callable(field):
val = field(self, header=True)
head_list.append(val)
else:
if field == '__str__':
val = self.model._meta.model_name.upper() # 返回模型类的名称
else:
val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称
head_list.append(val) # 获取表单信息
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
for obj in data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self, obj) # 给自定义方法传递参数
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
temp.append(val)
new_data_list.append(temp)
return render(request, 'list.html', locals()) def get_urls2(self):
temp = [] # 添加每个app/model的增删改查url
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add, name="%s_%s_add" % (app_label, model_name)))
temp.append(url(r'^(\d+)/delete/', self.delete, name="%s_%s_delete" % (app_label, model_name)))
temp.append(url(r'^(\d+)/change/', self.change, name="%s_%s_change" % (app_label, model_name)))
temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name)))
return temp @property
def urls2(self):
return self.get_urls2(), None, None

8 修改list.html的代码,并添加复选框的点击事件:

<body>
<h3>数据列表</h3>
<div class="container">
<div class="row">
<div class="col-md-9">
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for head_name in head_list %}
<td>{{ head_name }}</td>
{% endfor %} </tr>
</thead>
<tbody>
{% for data_list in new_data_list %}
<tr class="data-list">
{% for data in data_list %}
<td>{{ data }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div>
<script>
// 全选事件
$("#all_select").click(function () {
if ($(this).prop("checked")){
$(".select").prop("checked", true)
}
else {
$(".select").prop("checked", false)
}
});
// 每列复选框的点击事件
$(".data-list").find(":checkbox").change(function () {
all_len = $(".data-list").length;
checked_len = $(".data-list").find(":checked").length;
if (checked_len < all_len){
$("#all_select").prop("checked", false)
}
else{
$("#all_select").prop("checked", true)
}
})
</script>
</body>

此时访问/strak/app01/userinfo和/strak/app01/book,表头和表单都有数据了。

自定义admin组件

 list_display_links

首先判断模型类有没有配置list_display_links,如果没有就显示编辑列,如果指定了可点击的字段,那就把这个字段变成可点击的a标签,再把编辑列去掉。

在ModelStart类中,添加类属性list_display_links=[],然后修改new_list_display方法,

1 修改list_viwe中获取表单数据的代码,ModelStart类的部分代码:

class ModelStark(object):
list_display = ["__str__"]
list_display_links = [] ...... def new_list_display(self):
temp = []
temp.append(ModelStark.checkbox)
temp.extend(self.list_display)
if not self.list_display_links: # 判断是否指定了可点击的列
temp.append(ModelStark.edit)
temp.append(ModelStark.deletes)
return temp def list_view(self, request):
"""列表展示页"""
# print(self.model) # UserInfo
# print(self.list_display) # ["name", "age"]
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头信息
# 定义一个列表,格式:["复选框", name , age, "操作"....]
head_list = []
for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......]
if callable(field):
val = field(self, header=True)
head_list.append(val)
else:
if field == '__str__':
val = self.model._meta.model_name.upper() # 返回模型类的名称
else:
val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称
head_list.append(val) # 获取表单信息
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
# print(ModelStark.list_display_links)
for obj in data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self, obj) # 给自定义方法传递参数
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
if field in self.list_display_links: # 判断字段是否在list_display_links中,
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,))
val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
temp.append(val)
new_data_list.append(temp)
return render(request, 'list.html', locals()) ......

2 在app01/strak.py中修改Userinfo的配置类:

class UserConfig(ModelStark):

    list_display = ["name", "age"]
list_display_links = ["name"]

访问/strak/app01/userinfo,

自定义admin组件

效果有了,但是下面的代码在很多地方重复使用:

model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.id,))
val = mark_safe("<a href='%s'>%s</a>" % (_url, val))

3 这些代码都是在获取url,因此直接封装四个获取url的方法:get_change_url,get_delete_url,get_add_url,get_list_url。

    """获取编辑的url"""
def get_change_url(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,))
return _url """获取删除的url"""
def get_delete_url(self, obj): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))
return _url """获取添加的url"""
def get_add_url(self): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_add" % (app_label, model_name))
return _url """获取列表的url"""
def get_list_url(self, obj): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_list" % (app_label, model_name))
return _url

4 修改edit、deletes、checkbox的内部代码:

    """编辑按钮"""
def edit(self, obj=None, header=False):
if header: # 判断是不是表头
return "操作"
_url = self.get_change_url(obj)
return mark_safe("<a href='%s'>编辑</a>" % _url) """删除按钮"""
def deletes(self, obj=None, header=False):
if header: # 判断是不是表头
return "操作"
_url = self.get_delete_url(obj)
return mark_safe("<a href='%s'>删除</a>" % _url) """复选框"""
def checkbox(self, obj=None, header=False):
if header: # 判断是不是表头
return mark_safe("<input id='all_select' type='checkbox'>")
return mark_safe("<input class='select' type='checkbox'>")

5 修改list_view的获取list_display_links的字段的部分代码,list_view的代码:

"""列表展示页"""
def list_view(self, request):
# print(self.model) # UserInfo
# print(self.list_display) # ["name", "age"]
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头信息
# 定义一个列表,格式:["复选框", name , age, "操作"....]
head_list = []
for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......]
if callable(field):
val = field(self, header=True)
head_list.append(val)
else:
if field == '__str__':
val = self.model._meta.model_name.upper() # 返回模型类的名称
else:
val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称
head_list.append(val) # 获取表单信息
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
# print(ModelStark.list_display_links)
for obj in data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self, obj) # 给自定义方法传递参数
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
if field in self.list_display_links: # 判断字段是否在list_display_links中,
_url = self.get_change_url(obj)
val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
temp.append(val)
new_data_list.append(temp)
return render(request, 'list.html', locals())

6 同时整理下方法的命名,此时ModelStark类的全部代码:

class ModelStark(object):
list_display = ["__str__"]
list_display_links = [] def __init__(self, model, site):
self.model = model
self.site = site """编辑按钮"""
def edit(self, obj=None, header=False):
if header: # 判断是不是表头
return "操作"
_url = self.get_change_url(obj)
return mark_safe("<a href='%s'>编辑</a>" % _url) """删除按钮"""
def deletes(self, obj=None, header=False):
if header: # 判断是不是表头
return "操作"
_url = self.get_delete_url(obj)
return mark_safe("<a href='%s'>删除</a>" % _url) """复选框"""
def checkbox(self, obj=None, header=False):
if header: # 判断是不是表头
return mark_safe("<input id='all_select' type='checkbox'>")
return mark_safe("<input class='select' type='checkbox'>") """获取编辑的url"""
def get_change_url(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,))
return _url """获取删除的url"""
def get_delete_url(self, obj): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))
return _url """获取添加的url"""
def get_add_url(self): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_add" % (app_label, model_name))
return _url """获取列表的url"""
def get_list_url(self, obj): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_list" % (app_label, model_name))
return _url def new_list_display(self):
temp = []
temp.append(ModelStark.checkbox)
temp.extend(self.list_display)
if not self.list_display_links: # 判断是否指定了可点击的列
temp.append(ModelStark.edit)
temp.append(ModelStark.deletes)
return temp def add_view(self, request):
return HttpResponse("add") def delete_view(self, request, id):
return HttpResponse("delete") def change_view(self, request, id):
return HttpResponse("change") """列表展示页"""
def list_view(self, request):
# print(self.model) # UserInfo
# print(self.list_display) # ["name", "age"]
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....] # 获取表头信息
# 定义一个列表,格式:["复选框", name , age, "操作"....]
head_list = []
for field in self.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......]
if callable(field):
val = field(self, header=True)
head_list.append(val)
else:
if field == '__str__':
val = self.model._meta.model_name.upper() # 返回模型类的名称
else:
val = self.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称
head_list.append(val) # 获取表单信息
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
# print(ModelStark.list_display_links)
for obj in data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self, obj) # 给自定义方法传递参数
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
if field in self.list_display_links: # 判断字段是否在list_display_links中,
_url = self.get_change_url(obj)
val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
temp.append(val)
new_data_list.append(temp)
return render(request, 'list.html', locals()) def get_urls2(self):
temp = [] # 添加每个app/model的增删改查url
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add_view, name="%s_%s_add" % (app_label, model_name)))
temp.append(url(r'^(\d+)/delete/', self.delete_view, name="%s_%s_delete" % (app_label, model_name)))
temp.append(url(r'^(\d+)/change/', self.change_view, name="%s_%s_change" % (app_label, model_name)))
temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name)))
return temp @property
def urls2(self):
return self.get_urls2(), None, None

增加

现在查的页面已经有了,把增删改的功能也做了。先做增加的。为了表单的复杂,把app01的model.py的模型类都删了,把stark.py里面的注册代码和配置类代码也删了。

1 把下面的模型类代码放到app01的model.py里面,然后执行迁移。

from django.db import models

# Create your models here.

class Author(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
age=models.IntegerField() # 与AuthorDetail建立一对一的关系
authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE) def __str__(self):
return self.name class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True)
birthday=models.DateField()
telephone=models.BigIntegerField()
addr=models.CharField( max_length=64) def __str__(self):
return self.telephone class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
email=models.EmailField()
def __str__(self):
return self.name class Book(models.Model): nid = models.AutoField(primary_key=True)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2) # 与Publish建立一对多的关系,外键字段建立在多的一方
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors=models.ManyToManyField(to='Author',)
def __str__(self):
return self.title

2 app01的stark.py里面的代码:

from stark.service.stark import site, ModelStark
from .models import * class BookConfig(ModelStark):
list_display = ["title", "price", "publishDate"] site.register(Author)
site.register(Publish)
site.register(AuthorDetail)
site.register(Book,BookConfig)

在list.html里面添加一个跳转到添加数据页面的连接或按钮,并且在list_view里把添加的url传到list.html。

3 使用ModelForm来做表单的处理。ModelStark类中的add_view方法:

 def add_view(self, request):
class ModelFormDemo(ModelForm):
class Meta:
model = self.model
fields = "__all__"
form_obj = ModelFormDemo()
return render(request, 'add_view.html', locals())

4 添加add_view.html文件,代码:

    <style>

          input,select {
display: block;
width: 100%;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
}
</style>
</head>
<body>
<h3>添加数据</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form_obj %}
<div>
<label for="">{{ field.label }}</label>
{{ field }} <span style="color: red" class=" error pull-right">{{ field.errors.0 }}</span>
</div>
{% endfor %} <button type="submit" class="btn btn-default pull-right">提交</button>
</form>
</div>
</div>
</div>
</body>

此时访问添加页面,效果
自定义admin组件

如果想让字段显示中文怎么办。在add_view的ModelFormDemo里面加label? 但是我们并不知道此时访问的是那张数据表,所以不能写死。。那怎么办?让用户自己定义,用户未定义就用默认的。

5 在ModelStark类中定义一个类属性:model_class = None。 定义一个获取用户定义的ModelFormDemo类的方法。然后修改add_view方法。

class ModelStark(object):
list_display = ["__str__"]
list_display_links = []
model_class = None ......... # 获取定义的ModelFormDemo类
def get_modelform_class(self):
if not self.model_class: # 如果用户为定义,返回默认的ModelFormDemo类名
class ModelFormDemo(ModelForm):
class Meta:
model = self.model
fields = "__all__"
return ModelFormDemo
else: # 返回用户定义的ModelFormDemo类名
return self.model_class # 添加视图
def add_view(self, request):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
form_obj = ModelFormDemo()
return render(request, 'add_view.html', locals()) .......

6 现在去app01下的stark.py中定制一个ModelForm类:

from django.forms import ModelForm

class ModelFormDemo(ModelForm):
class Meta:
model = Book
fields = "__all__"
labels = {
"title": "书籍名称",
"price": "价格"
} class BookConfig(ModelStark):
list_display = ["title", "price", "publishDate"]
model_class = ModelFormDemo .......

此时去页面访问,

自定义admin组件

OK,现在就做post请求。

7 add_view.py

    def add_view(self, request):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
if request.method == "POST":
form_obj = ModelFormDemo(request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
else:
return render(request, 'add_view.html', locals()) form_obj = ModelFormDemo()
return render(request, 'add_view.html', locals())

现在就可以去页面添加数据了。如果进入添加页面时报错没有__str__字段, 在模型类的 __str__方法中将返回值强转str就好了。

ok添加做好了,接下来编辑。

编辑

添加和编辑使用的表单一样,因此两个页面都导入表单的html代码。创建form.html。

1 form.html:

<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form_obj %}
<div>
<label for="">{{ field.label }}</label>
{{ field }} <span style="color: red" class=" error pull-right">{{ field.errors.0 }}</span>
</div>
{% endfor %} <button type="submit" class="btn btn-default pull-right">提交</button>
</form>
</div>
</div>
</div>

2 add_view.html

    <style>

          input,select {
display: block;
width: 100%;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
}
</style>
</head>
<body>
<h3>添加数据</h3>
{% include 'form.html' %}
</body>

3 edit.html

        <style>

          input,select {
display: block;
width: 100%;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
}
</style>
</head>
<body>
<h3>修改数据</h3>
{% include 'form.html' %}
</body>

4 ok,页面完成了,写编辑的视图函数。修改change_view方法:

    def change_view(self, request, id):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
edit_obj = self.model.objects.get(pk=id)
if request.method == "POST":
form_obj = ModelFormDemo(request.POST, instance=edit_obj)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
else:
return render(request, 'edit_view.html', locals()) form_obj = ModelFormDemo(instance=edit_obj)
return render(request, 'edit_view.html', locals())

现在就可以去页面修改数据了。修改做好了,接下来删除。

删除

1 创建delete_view.html

<body>
<form method="post">
{% csrf_token %}
<input class="btn btn-danger" type="submit" value="确定删除"> <a class="btn btn-warning" href="{{ list_url }}">取消</a>
</form> </body>

2 修改delete_view方法

    def delete_view(self, request, id):
list_url = self.get_list_url()
if request.method == "POST":
self.model.objects.get(pk=id).delete()
return redirect(list_url)
return render(request, 'delete_view.html', locals())

增删改现在算是大功告成。下面继续查询,因为admin的查询姿势有很多,所以如果继续在list_view方法里写代码会显得比较乱,因此把查询封装在一个方法里面。

3 定义ShowList类:

class ShowList(object):
def __init__(self, config, data_list):
self.config = config
self.data_list = data_list def show_header(self):
# 获取表头信息
# 定义一个列表,格式:["复选框", name , age, "操作"....]
head_list = []
for field in self.config.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......]
if callable(field):
val = field(self.config, header=True)
head_list.append(val)
else:
if field == '__str__':
val = self.config.model._meta.model_name.upper() # 返回模型类的名称
else:
val = self.config.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称
head_list.append(val)
return head_list def show_body(self):
# 获取表单信息
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
# print(ModelStark.list_display_links)
for obj in self.data_list: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.config.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self.config, obj) # 给自定义方法传递参数
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
if field in self.config.list_display_links: # 判断字段是否在list_display_links中,
_url = self.config.get_change_url(obj)
val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
temp.append(val)
new_data_list.append(temp)
return new_data_list

4 修改ModelStark类的list_view,此时ModelStark类的代码:

class ModelStark(object):
list_display = ["__str__"]
list_display_links = []
model_class = None def __init__(self, model, site):
self.model = model
self.site = site """编辑按钮"""
def edit(self, obj=None, header=False):
if header: # 判断是不是表头
return "操作"
_url = self.get_change_url(obj)
return mark_safe("<a href='%s'>编辑</a>" % _url) """删除按钮"""
def deletes(self, obj=None, header=False):
if header: # 判断是不是表头
return "操作"
_url = self.get_delete_url(obj)
return mark_safe("<a href='%s'>删除</a>" % _url) """复选框"""
def checkbox(self, obj=None, header=False):
if header: # 判断是不是表头
return mark_safe("<input id='all_select' type='checkbox'>")
return mark_safe("<input class='select' type='checkbox'>") """获取编辑的url"""
def get_change_url(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,))
return _url """获取删除的url"""
def get_delete_url(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))
return _url """获取添加的url"""
def get_add_url(self): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_add" % (app_label, model_name))
return _url """获取列表的url"""
def get_list_url(self): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_list" % (app_label, model_name))
return _url # 获取被指定的所有字段
def new_list_display(self):
temp = []
temp.append(ModelStark.checkbox)
temp.extend(self.list_display)
if not self.list_display_links: # 判断是否指定了可点击的列
temp.append(ModelStark.edit)
temp.append(ModelStark.deletes)
return temp # 获取定义的ModelFormDemo类
def get_modelform_class(self):
if not self.model_class: # 如果用户为定义,返回默认的ModelFormDemo类名
class ModelFormDemo(ModelForm):
class Meta:
model = self.model
fields = "__all__"
return ModelFormDemo
else: # 返回用户定义的ModelFormDemo类名
return self.model_class # 添加视图
def add_view(self, request):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
if request.method == "POST":
form_obj = ModelFormDemo(request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
else:
return render(request, 'add_view.html', locals()) form_obj = ModelFormDemo()
return render(request, 'add_view.html', locals()) def delete_view(self, request, id):
list_url = self.get_list_url()
if request.method == "POST":
self.model.objects.get(pk=id).delete()
return redirect(list_url)
return render(request, 'delete_view.html', locals()) def change_view(self, request, id):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
edit_obj = self.model.objects.get(pk=id)
if request.method == "POST":
form_obj = ModelFormDemo(request.POST, instance=edit_obj)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
else:
return render(request, 'edit_view.html', locals()) form_obj = ModelFormDemo(instance=edit_obj)
return render(request, 'edit_view.html', locals()) """列表展示页"""
def list_view(self, request):
# 获取userinfo 的数据
data_list = self.model.objects.all() # ["obj1", "obj2",.....]
# 获取表头
show_list = ShowList(self, data_list)
head_list = show_list.show_header()
# 获取表体
new_data_list = show_list.show_body()
# 获取添加的url
add_url = self.get_add_url()
return render(request, 'list.html', locals()) def get_urls2(self):
temp = [] # 添加每个app/model的增删改查url
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add_view, name="%s_%s_add" % (app_label, model_name)))
temp.append(url(r'^(\d+)/delete/', self.delete_view, name="%s_%s_delete" % (app_label, model_name)))
temp.append(url(r'^(\d+)/change/', self.change_view, name="%s_%s_change" % (app_label, model_name)))
temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name)))
return temp @property
def urls2(self):
return self.get_urls2(), None, None

分页

1 在stark app下创建一个utils包,然后创建一个page.py,代码:

class Pagination(object):
def __init__(self, current_page, all_count, base_url,params, per_page_num=8, pager_count=11, ):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param base_url: 分页中显示的URL前缀
:param pager_count: 最多显示的页码个数
""" try:
current_page = int(current_page)
except Exception as e:
current_page = 1 if current_page < 1:
current_page = 1 self.current_page = current_page self.all_count = all_count
self.per_page_num = per_page_num self.base_url = base_url # 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager self.pager_count = pager_count # 最多显示页码数
self.pager_count_half = int((pager_count - 1) / 2) import copy
params = copy.deepcopy(params)
params._mutable = True
self.params = params # self.params : {"page":77,"title":"python","nid":1} @property
def start(self):
return (self.current_page - 1) * self.per_page_num @property
def end(self):
return self.current_page * self.per_page_num def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示(11-1)/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1 # 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_start = self.all_pager - self.pager_count + 1
pager_end = self.all_pager + 1 else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1 page_html_list = []
self.params["page"] = 1
first_page = '<li><a href="%s?%s">首页</a></li>' % (self.base_url, self.params.urlencode(),)
page_html_list.append(first_page) if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
self.params["page"] = self.current_page - 1
prev_page = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url, self.params.urlencode(),) page_html_list.append(prev_page) for i in range(pager_start, pager_end): self.params["page"] = i
if i == self.current_page:
temp = '<li class="active"><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
else:
temp = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
page_html_list.append(temp) if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
self.params["page"] = self.current_page + 1
next_page = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.params.urlencode(),)
page_html_list.append(next_page) self.params["page"] = self.all_pager
last_page = '<li><a href="%s?%s">尾页</a></li>' % (self.base_url, self.params.urlencode(),)
page_html_list.append(last_page) return ''.join(page_html_list)

2 在ShowList类中生成分页对象和每一页的数据。

class ShowList(object):
def __init__(self, config, data_list, request):
self.config = config
self.data_list = data_list
self.request = request
# 分页
data_count = self.data_list.count() # 获取数据总数量
current_page = self.request.GET.get("page", 1) # 获取当前页码
base_url = self.request.path # 获取url(不带参数)
# 生成分页对象
self.paginator = Pagination(current_page, data_count, base_url, self.request.GET, per_page_num=1, pager_count=11)
# 当前页的数据列表
self.page_data = self.data_list[self.paginator.start: self.paginator.end] 。。。。。。。
# list_view传入request

3 然后修改get_body方法中的代码:

自定义admin组件

4 修改list_view.html,插入页码列表

<div class="container">
<div class="row">
<div class="col-md-9">
<a href="{{ add_url }}" class="btn btn-primary">添加数据</a>
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for head_name in head_list %}
<td>{{ head_name }}</td>
{% endfor %} </tr>
</thead>
<tbody>
{% for data_list in new_data_list %}
<tr class="data-list">
{% for data in data_list %}
<td>{{ data }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<nav>
<ul class="pagination">
{{ show_list.paginator.page_html|safe }}
</ul>
</nav>
</div> </div>
</div>

Search

1 在ModelStark类中添加类属性search_fields = [],然后添加一个get_search方法,根据search关键字进行模糊查询。

    def get_search(self, request):
key_word = request.GET.get("q", "")
search_connection = Q()
if key_word:
search_connection.connector = 'or'
for field in self.search_fields:
search_connection.children.append((field+"__contains", key_word))
return key_word, search_connection

2 修改list_view方法的代码

    def list_view(self, request):
# 获取search的key_word,Q对象
key_word, search_connection = self.get_search(request)
# 获取userinfo 的数据,并进行search过滤
data_list = self.model.objects.all().filter(search_connection) # 获取表头
show_list = ShowList(self, data_list, request)
head_list = show_list.show_header()
# 获取表体
new_data_list = show_list.show_body()
# 获取添加的url
add_url = self.get_add_url()
return render(request, 'list.html', locals())

3 修改list_view.html

<div class="container">
<div class="row">
<div class="col-md-9">
<a href="{{ add_url }}" class="btn btn-primary">添加数据</a>
<form method="get" class="pull-right">
{% if show_list.config.search_fields %}
<input type="text" name="q" value="{{ key_word }}">
<input type="submit" class="btm btn-primary" value="搜索">
{% endif %}
</form> <table class="table table-bordered table-striped">
<thead>
<tr>
{% for head_name in head_list %}
<td>{{ head_name }}</td>
{% endfor %} </tr>
</thead>
<tbody>
{% for data_list in new_data_list %}
<tr class="data-list">
{% for data in data_list %}
<td>{{ data }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<nav>
<ul class="pagination">
{{ show_list.paginator.page_html|safe }}
</ul>
</nav>
</div> </div>
</div>

在app01的stark.py中定义了search_fields后,就能根据设定的字段进行search查询了。

action

1 在ModelStark类中添加类属性actions = [],在ShowList类中定义get_actions_list方法,获取用户定制的所有action操作。

......
class ShowList(object):
def __init__(self, config, data_list, request):
........
# actions
self.actions = self.config.actions # 获取action操作
def get_actions_list(self):
temp = []
for action in self.actions:
temp.append({
"name": action.__name__,
"desc": action.short_description
})
return temp .......

2 修改ModelStark类中的checkbox方法:

 """复选框"""
def checkbox(self, obj=None, header=False):
if header: # 判断是不是表头
return mark_safe("<input id='all_select' type='checkbox'>")
return mark_safe("<input class='select' name='selected_id' type='checkbox' value='%s'>" % obj.pk)

3 修改list_view.html,添加下拉框

<div class="container">
<div class="row">
<div class="col-md-9">
<a href="{{ add_url }}" class="btn btn-primary">添加数据</a>
<form method="get" class="pull-right">
{% if show_list.config.search_fields %}
<input type="text" name="q" value="{{ key_word }}">
<input type="submit" class="btm btn-primary" value="搜索">
{% endif %}
</form> <form method="post">
{% csrf_token %}
<select name="action" style="width: 200px;padding: 5px 8px;display: inline-block">
<option>------</option>
{% for action in show_list.get_actions_list %}
<option value="{{ action.name }}">{{ action.desc }}</option>
{% endfor %} </select><button type="submit" class="btn btn-info">Go</button>
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for head_name in head_list %}
<td>{{ head_name }}</td>
{% endfor %} </tr>
</thead>
<tbody>
{% for data_list in new_data_list %}
<tr class="data-list">
{% for data in data_list %}
<td>{{ data }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</form>
<nav>
<ul class="pagination">
{{ show_list.paginator.page_html|safe }}
</ul>
</nav>
</div> </div>
</div>

4 在app01 stark.py中的BookConfig类中定义一个修改价格的action

class BookConfig(ModelStark):
list_display = ["title", "price", "publishDate"]
model_class = ModelFormDemo
search_fields = ["title", "price"] def edit_price_action(self, request, queryset):
queryset.update(price=111)
edit_price_action.short_description = "修改价格"
actions = [edit_price_action]

5 修改ModelStark类中list_view方法。

 def list_view(self, request):
if request.method == "POST":
action_name = request.POST.get("action") # 获取执行的action名称
id_list = request.POST.getlist("selected_id") # 获取被选中的id
action_func = getattr(self, action_name) # 反射获取函数
queryset = self.model.objects.filter(pk__in=id_list) # 过滤被选中的查询集
action_func(request, queryset) # 执行action
return redirect(self.get_list_url()) 。。。。。。。

ok,现在就能批量的修改书籍价格。

然而admin的action有一个默认的批量删除,so,下面添加这个功能。

1 在ModelStark类中添加一个delete_action方法

    def delete_action(self, request, queryset):
queryset.delete()
delete_action.short_description = "批量删除"

2 然后再定义一个new_action方法

    def new_actions(self):
temp = []
temp.append(ModelStark.delete_action)
temp.extend(self.actions)
return temp

3 然后修改ShowList的__init__中self.action

self.actions = self.config.new_actions()

现在就能批量删除了。此时ModelStark类的代码:

class ModelStark(object):
list_display = ["__str__"]
list_display_links = []
model_class = None
search_fields = []
actions = []
filter_fields = [] def __init__(self, model, site):
self.model = model
self.site = site # 批量删除
def delete_action(self, request, queryset):
queryset.delete()
delete_action.short_description = "批量删除" """编辑按钮"""
def edit(self, obj=None, header=False):
if header: # 判断是不是表头
return "操作"
_url = self.get_change_url(obj)
return mark_safe("<a href='%s'>编辑</a>" % _url) """删除按钮"""
def deletes(self, obj=None, header=False):
if header: # 判断是不是表头
return "操作"
_url = self.get_delete_url(obj)
return mark_safe("<a href='%s'>删除</a>" % _url) """复选框"""
def checkbox(self, obj=None, header=False):
if header: # 判断是不是表头
return mark_safe("<input id='all_select' type='checkbox'>")
return mark_safe("<input class='select' name='selected_id' type='checkbox' value='%s'>" % obj.pk) """获取编辑的url"""
def get_change_url(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,))
return _url """获取删除的url"""
def get_delete_url(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))
return _url """获取添加的url"""
def get_add_url(self): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_add" % (app_label, model_name))
return _url """获取列表的url"""
def get_list_url(self): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
_url = reverse("%s_%s_list" % (app_label, model_name))
return _url # 获取所有的action
def new_actions(self):
temp = []
temp.append(ModelStark.delete_action)
temp.extend(self.actions)
return temp # 获取被指定的所有字段
def new_list_display(self):
temp = []
temp.append(ModelStark.checkbox)
temp.extend(self.list_display)
if not self.list_display_links: # 判断是否指定了可点击的列
temp.append(ModelStark.edit)
temp.append(ModelStark.deletes)
return temp # 获取定义的ModelFormDemo类
def get_modelform_class(self):
if not self.model_class: # 如果用户为定义,返回默认的ModelFormDemo类名
class ModelFormDemo(ModelForm):
class Meta:
model = self.model
fields = "__all__"
return ModelFormDemo
else: # 返回用户定义的ModelFormDemo类名
return self.model_class # 添加视图
def add_view(self, request):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
if request.method == "POST":
form_obj = ModelFormDemo(request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
else:
return render(request, 'add_view.html', locals()) form_obj = ModelFormDemo()
print(form_obj)
return render(request, 'add_view.html', locals()) # 删除视图
def delete_view(self, request, id):
list_url = self.get_list_url()
if request.method == "POST":
self.model.objects.get(pk=id).delete()
return redirect(list_url)
return render(request, 'delete_view.html', locals()) # 编辑视图
def change_view(self, request, id):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
edit_obj = self.model.objects.get(pk=id)
if request.method == "POST":
form_obj = ModelFormDemo(request.POST, instance=edit_obj)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
else:
return render(request, 'edit_view.html', locals()) form_obj = ModelFormDemo(instance=edit_obj)
return render(request, 'edit_view.html', locals()) # 获取search关键字和search字段
def get_search(self, request):
key_word = request.GET.get("q", "")
search_connection = Q()
if key_word:
key_word = key_word.strip()
search_connection.connector = 'or'
for field in self.search_fields:
search_connection.children.append((field+"__contains", key_word))
return key_word, search_connection """列表展示页"""
def list_view(self, request):
if request.method == "POST":
action_name = request.POST.get("action") # 获取执行的action名称
id_list = request.POST.getlist("selected_id") # 获取被选中的id
action_func = getattr(self, action_name) # 反射获取函数
queryset = self.model.objects.filter(pk__in=id_list) # 过滤被选中的查询集
action_func(request, queryset) # 执行action
return redirect(self.get_list_url()) # 获取search的key_word,Q对象
key_word, search_connection = self.get_search(request)
# 获取userinfo 的数据,并进行search过滤
data_list = self.model.objects.all().filter(search_connection) # 获取表头
show_list = ShowList(self, data_list, request)
head_list = show_list.show_header()
# 获取表体
new_data_list = show_list.show_body()
# 获取添加的url
add_url = self.get_add_url()
return render(request, 'list.html', locals()) def get_urls2(self):
temp = [] # 添加每个app/model的增删改查url
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label temp.append(url(r'^add/', self.add_view, name="%s_%s_add" % (app_label, model_name)))
temp.append(url(r'^(\d+)/delete/', self.delete_view, name="%s_%s_delete" % (app_label, model_name)))
temp.append(url(r'^(\d+)/change/', self.change_view, name="%s_%s_change" % (app_label, model_name)))
temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name)))
return temp @property
def urls2(self):
return self.get_urls2(), None, None

ShowList类的代码;

class ShowList(object):
def __init__(self, config, data_list, request):
self.config = config
self.data_list = data_list
self.request = request
# 分页
data_count = self.data_list.count() # 获取数据总数量
current_page = int(self.request.GET.get("page", 1)) # 获取当前页码
base_url = self.request.path # 获取url(不带参数)
# 生成分页对象
self.paginator = Pagination(current_page, data_count, base_url, self.request.GET, per_page_num=2, pager_count=11)
# 当前页的数据列表
self.page_data = self.data_list[self.paginator.start: self.paginator.end]
# actions
self.actions = self.config.new_actions() # 获取action操作
def get_actions_list(self):
temp = []
for action in self.actions:
temp.append({
"name": action.__name__,
"desc": action.short_description
})
return temp def show_header(self):
# 获取表头信息
# 定义一个列表,格式:["复选框", name , age, "操作"....]
head_list = []
for field in self.config.new_list_display(): # [checkbox,__str__, name,age,edit,deletes......]
if callable(field):
val = field(self.config, header=True)
head_list.append(val)
else:
if field == '__str__':
val = self.config.model._meta.model_name.upper() # 返回模型类的名称
else:
val = self.config.model._meta.get_field(field).verbose_name # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称
head_list.append(val)
return head_list def show_body(self):
# 获取表单信息
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
for obj in self.page_data: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.config.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self.config, obj) # 给自定义方法传递参数
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
if field in self.config.list_display_links: # 判断字段是否在list_display_links中,
_url = self.config.get_change_url(obj)
val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
temp.append(val)
new_data_list.append(temp)
return new_data_list

filter_fields

1 在ModelStark类中定义一个类属性filter_fields=[],然后再ShowList类中定义一个get_filter_linktags方法,代码:

    def get_filter_linktags(self):
link_dict = {} # 定义字段对应的a连接 {"book":["<a>金平..</a>", "<a>"], ...} for filter_field in self.config.filter_fields: # 获取要过滤的字段 ["book", "author",..... ]
url_params = copy.deepcopy(self.request.GET) # 获取参数
current_field_id = self.request.GET.get(filter_field, 0) # 获取当前被选中的字段的id
filter_field_obj = self.config.model._meta.get_field(filter_field) # 获取字段对象
data_list = filter_field_obj.rel.to.objects.all() # 根据字段对象获取该模型类的queryset对象["book1","book2",...]
temp = [] # 定义一个临时列表
for obj in data_list:
url_params[filter_field] = obj.pk
_url = url_params.urlencode()
if int(current_field_id) == obj.pk:
link_tag = "<a href='?%s'>%s</a>" % (_url, str(obj))
else:
link_tag = "<a href='?%s'>%s</a>" % (_url, str(obj))
temp.append(link_tag)
link_dict[filter_field] = temp
return link_dict

2 app01 stark.py的BookConfig类中定义过滤字段

class BookConfig(ModelStark):
list_display = ["title", "price", "publishDate"]
model_class = ModelFormDemo
search_fields = ["title", "price"] def edit_price_action(self, request, queryset):
queryset.update(price=111)
edit_price_action.short_description = "修改价格"
actions = [edit_price_action]
filter_fields = ["authors", "publish"] # 先用多对多和一对多的字段

3 修改list_view.html代码,加一个过滤布局并传数据:

<div class="container">
<div class="row">
<div class="col-md-9">
<a href="{{ add_url }}" class="btn btn-primary">添加数据</a>
<form method="get" class="pull-right">
{% if show_list.config.search_fields %}
<input type="text" name="q" value="{{ key_word }}">
<input type="submit" class="btm btn-primary" value="搜索">
{% endif %}
</form> <form method="post">
{% csrf_token %}
<select name="action" style="width: 200px;padding: 5px 8px;display: inline-block">
<option>------</option>
{% for action in show_list.get_actions_list %}
<option value="{{ action.name }}">{{ action.desc }}</option>
{% endfor %} </select><button type="submit" class="btn btn-info">Go</button>
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for head_name in head_list %}
<td>{{ head_name }}</td>
{% endfor %} </tr>
</thead>
<tbody>
{% for data_list in new_data_list %}
<tr class="data-list">
{% for data in data_list %}
<td>{{ data }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</form>
<nav>
<ul class="pagination">
{{ show_list.paginator.page_html|safe }}
</ul>
</nav>
</div>
<div class="col-md-3">
<div class="filter">
<h3>Filter</h3>
{% for field, link_tag in show_list.get_filter_linktags.items %}
<div class="well">
<p>{{ field|upper }}</p>
{% for tag in link_tag %}
<p>{{ tag|safe }}</p>
{% endfor %}
</div>
{% endfor %} </div>
</div>
</div>
</div>

此时就能显示要过滤的所有字段和对应的数据,而且连接也拼接无误。现在改下a标签的样式。

4 修改get_filter_linktags方法:

    def get_filter_linktags(self):
link_dict = {} # 定义字段对应的a连接 {"book":["<a>金平..</a>", "<a>"], ...} for filter_field in self.config.filter_fields: # 获取要过滤的字段 ["book", "author",..... ]
url_params = copy.deepcopy(self.request.GET) # 获取参数
current_field_id = self.request.GET.get(filter_field, 0) # 获取当前被选中的字段的id
filter_field_obj = self.config.model._meta.get_field(filter_field) # 获取字段对象
data_list = filter_field_obj.rel.to.objects.all() # 根据字段对象获取该模型类的queryset对象["book1","book2",...]
temp = [] # 定义一个临时列表
for obj in data_list:
url_params[filter_field] = obj.pk
_url = url_params.urlencode()
if int(current_field_id) == obj.pk:
link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, str(obj))
else:
link_tag = "<a href='?%s'>%s</a>" % (_url, str(obj))
temp.append(link_tag)
link_dict[filter_field] = temp
return link_dict

5 给list_view.html中的a标签加样式

<style>
.filter a{
color: #999;
}
.active{
color: #23527c!important;
}
</style>

6 现在给每个过滤的字段都加一个all标签。

修改get_filter_linktags方法

    def get_filter_linktags(self):
link_dict = {} # 定义字段对应的a连接 {"book":["<a>金平..</a>", "<a>"], ...} for filter_field in self.config.filter_fields: # 获取要过滤的字段 ["book", "author",..... ]
url_params = copy.deepcopy(self.request.GET) # 获取参数
current_field_id = self.request.GET.get(filter_field, 0) # 获取当前被选中的字段的id
filter_field_obj = self.config.model._meta.get_field(filter_field) # 获取字段对象
data_list = filter_field_obj.rel.to.objects.all() # 根据字段对象获取该模型类的queryset对象["book1","book2",...]
temp = [] # 定义一个临时列表
# all标签
if url_params.get(filter_field): # if GET请求参数中包含当前循环的字段,就把这个参数(字段)删除
del url_params[filter_field]
temp.append("<a href='?%s'>ALL</a>" % url_params.urlencode())
else: # 不存在就说明该字段没有被选中
temp.append("<a class='active' href='#'>ALL</a>")
# 数据标签
for obj in data_list:
url_params[filter_field] = obj.pk
_url = url_params.urlencode()
if int(current_field_id) == obj.pk:
link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, str(obj))
else:
link_tag = "<a href='?%s'>%s</a>" % (_url, str(obj))
temp.append(link_tag)
link_dict[filter_field] = temp
return link_dict

OK 样式有了,url也有了,进行数据过滤。

7 在ModelStark类中添加get_filter_data方法,并修改list_view方法:

# 过滤数据的查询条件
def get_filter_data(self, request):
filter_condition = Q()
for field, pk in request.GET.items():
if field in self.filter_fields:
filter_condition.children.append((field, pk))
return filter_condition """列表展示页"""
def list_view(self, request):
if request.method == "POST":
action_name = request.POST.get("action") # 获取执行的action名称
id_list = request.POST.getlist("selected_id") # 获取被选中的id
action_func = getattr(self, action_name) # 反射获取函数
queryset = self.model.objects.filter(pk__in=id_list) # 过滤被选中的查询集
action_func(request, queryset) # 执行action
return redirect(self.get_list_url()) # 获取search的key_word,Q对象
key_word, search_connection = self.get_search(request)
# 过滤
filter_connection = self.get_filter_data(request)
# 获取userinfo 的数据,并进行search过滤
data_list = self.model.objects.all().filter(search_connection).filter(filter_connection) # 获取表头
show_list = ShowList(self, data_list, request)
head_list = show_list.show_header()
# 获取表体
new_data_list = show_list.show_body()
# 获取添加的url
add_url = self.get_add_url()
return render(request, 'list.html', locals())

8 现在就能进行正常的过滤了,只不过现在的能过滤的字段只能是一对多或者多对多。下面处理普通字段的过滤。

修改get_filter_linktags方法

    def get_filter_linktags(self):
link_dict = {} # 定义字段对应的a连接 {"book":["<a>金平..</a>", "<a>"], ...} for filter_field in self.config.filter_fields: # 获取要过滤的字段 ["book", "author",..... ]
url_params = copy.deepcopy(self.request.GET) # 获取参数
current_field_id = self.request.GET.get(filter_field, 0) # 获取当前被选中的字段的id
filter_field_obj = self.config.model._meta.get_field(filter_field) # 获取字段对象
if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField): # 如果字段对象是一对多或者多对多
data_list = filter_field_obj.rel.to.objects.all() # 根据字段对象获取该模型类的queryset对象["book1","book2",...]
else:
data_list = self.config.model.objects.all().values("pk", filter_field) # 取普通字段的pk和该字段的所有数据
temp = [] # 定义一个临时列表
# all标签
if url_params.get(filter_field): # if GET请求参数中包含当前循环的字段,就把这个参数(字段)删除
del url_params[filter_field]
temp.append("<a href='?%s'>ALL</a>" % url_params.urlencode())
else: # 不存在就说明该字段没有被选中
temp.append("<a class='active' href='#'>ALL</a>")
# 数据标签
for obj in data_list:
# 继续判断,如果是一对多或者多对多,就用对象去获取pk和值
if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
pk = obj.pk
text = str(obj)
url_params[filter_field] = pk # 字段作为键,pk作为值 ?publish=1&authors=2
else:
pk = obj.get("pk")
text = obj.get(filter_field)
url_params[filter_field] = text # 字段作为键,实际数据作为值 ?title="金平没"
_url = url_params.urlencode()
if current_field_id == str(pk) or current_field_id == text:
link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)
else:
link_tag = "<a href='?%s'>%s</a>" % (_url, text)
temp.append(link_tag)
link_dict[filter_field] = temp
return link_dict

现在在app01 stark.py中的BookConfig类中filter_fields添加"title"字段

class BookConfig(ModelStark):
list_display = ["title", "price", "publishDate"]
model_class = ModelFormDemo
search_fields = ["title", "price"] def edit_price_action(self, request, queryset):
queryset.update(price=111)
edit_price_action.short_description = "修改价格"
actions = [edit_price_action]
filter_fields = ["title", "authors", "publish"]

此时页面上的filter中也能显示title字段的所有值,并且也能完成过滤。

9 然而现在页面上不能显示多对多的字段数据,因为多对多的字段有不止一个值,所以页面的显示效果可能会乱,下面做个简单处理。。

修改ShowList中show_body方法。

    def show_body(self):
# 获取表单信息
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
for obj in self.page_data: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.config.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self.config, obj) # 给自定义方法传递参数
else:
field_obj = self.config.model._meta.get_field(field)
if isinstance(field_obj, ManyToManyField):
vals = getattr(obj, field).all() # 获取所有数据
new_temp = []
for i in vals:
new_temp.append(str(i))
val = ",".join(new_temp)
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
if field in self.config.list_display_links: # 判断字段是否在list_display_links中,
_url = self.config.get_change_url(obj)
val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
temp.append(val)
new_data_list.append(temp)
return new_data_list

在app01 stark.py中的BookConfig类中list_display添加"authors"字段

此时访问/stark/app01/book/时, 就能显示authors这一列了。但是如果我们访问其他model的列表页时可能会报错,假如访问stark/app01/author/,然后就会有这样的提示

自定义admin组件

这是因为(以author表为例):如果用户没有给author配置list_display,那么就会使用默认的__str__,但是当程序走到show_body的这里时,

自定义admin组件

查不到__str__的字段对象,因此会报错。解决办法,异常捕获。

修改show_body方法,show_body代码:

    def show_body(self):
# 获取表单信息
# 定义一个新的数据列表 格式:
"""
[
["name", "age"]
["name", "age"]
.......
]
"""
new_data_list = []
for obj in self.page_data: # 获取data_list中的每一个对象
temp = [] # 定义一个内层列表,存储一个对象所有字段的值
for field in self.config.new_list_display(): # 获取每一个要展示的字段 ["name", "age"]
if callable(field): # 判断字段是否可被调用
val = field(self.config, obj) # 给自定义方法传递参数
else:
try:
field_obj = self.config.model._meta.get_field(field)
if isinstance(field_obj, ManyToManyField):
vals = getattr(obj, field).all() # 获取所有数据
new_temp = []
for i in vals:
new_temp.append(str(i))
val = ",".join(new_temp)
else:
val = getattr(obj, field) # field是字符串,利用反射获取对象每个字段的值,
if field in self.config.list_display_links: # 判断字段是否在list_display_links中,
_url = self.config.get_change_url(obj)
val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
except Exception as e:
val = getattr(obj, field)
temp.append(val)
new_data_list.append(temp)
return new_data_list

 pop

当我们在admin添加数据的时候,如果哪个字段和其他表有关联,可以在输入框的后面点击加号去添加关联表的数据。下面做这个功能。

1 修改添加页面的样式,修改add_view.html,因为add_view.html使用form.html,因此在form.html上修改。

<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form_obj %}
<div style="position: relative">
<label for="">{{ field.label }}</label>
{{ field }} <span style="color: red" class=" error pull-right">{{ field.errors.0 }}</span>
<a style="position: absolute;right: -30px;top: 20px;text-decoration: none;cursor: pointer"><span style="font-size: 28px">+</span></a>
</div>
{% endfor %} <button type="submit" class="btn btn-default pull-right">提交</button>
</form>
</div>
</div>
</div>

此时页面上每个表单的后面都有加号按钮,但是一些和其他表没有关联的字段是不应该有加号的,因此应该在后台进行判断。因为使用的是ModelForm组件,因此判断字段的类型是不是ModelChoiceField即可。

2 修改ModelStark类中的add_view

    def add_view(self, request):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
form_obj = ModelFormDemo()
for bfield in form_obj:
if isinstance(bfield.field, ModelChoiceField): # bfield.field 获取的是字段对象;bfield.name 获取的是字段名称,类型是字符串;
bfield.is_related = True
if request.method == "POST":
form_obj = ModelFormDemo(request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect(self.get_list_url())
return render(request, 'add_view.html', locals())

这时候只有publish和author后面有加号。

3 现在是做点击事件,修改form.html,给a标签添加一个click事件,让它跳转到对应的添加页面,因此还需要一个url。

先去add_view把url获取了

    def add_view(self, request):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
form_obj = ModelFormDemo()
for bfield in form_obj:
if isinstance(bfield.field, ModelChoiceField): # bfield.field 获取的是字段对象;bfield.name 获取的是字段名称,类型是字符串;
bfield.is_related = True
# 获取该字段的模型表和模型表的app
# bfield.field.queryset.model 一对多或者多对多字段的关联模型表
relateed_model_name = bfield.field.queryset.model._meta.model_name
relateed_app_label = bfield.field.queryset.model._meta.app_label
_url = reverse("%s_%s_add" % (relateed_app_label, relateed_model_name))
bfield.add_url = _url

4 修改form.html,给a标签添加一个click事件

<div class="container">
<div class="row">
<div class="col-md-6 col-xs-10 col-md-offset-3">
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form_obj %}
<div style="position: relative">
<label for="">{{ field.label }}</label>
{{ field }} <span style="color: red" class=" error pull-right">{{ field.errors.0 }}</span>
{% if field.is_related %}
<a onclick="pop('{{ field.add_url }}')" style="position: absolute;right: -30px;top: 20px;text-decoration: none;cursor: pointer"><span style="font-size: 28px">+</span></a>
{% endif %}
</div>
{% endfor %} <button type="submit" class="btn btn-default pull-right">提交</button>
</form>
</div>
</div>
</div>
<script>
function pop(url) {
window.open(url,"", "width=600,height=400,top=100,left=100")
}
</script>

现在就能点击加号然后跳转到对应的模型添加页面。

Ok  ,现在能跳转到对应的添加页面,但是我们需要知道在添加完数据之后给哪个字段添加数据,并且在提交表单之后要返回添加的数据,而且要把值放到字段对应的select标签里。

解决步骤:(book添加页面为例)

(1)给url加参数,修改add_view方法,在每条url的后面加上一个参数,以pop_id为键,字段名为值,即只修改下面这句代码:

bfield.add_url = _url+"?pop_id=id_%s" % bfield.name         # id_%s 和select标签的id对应

当点击publish后面加号时,会弹出一个publish添加页面的小窗口。然而在添加完数据后会跳到publish的列表页,但是并不希望跳到列表页,而是返回之前的book添加页面。并且返回book的添加页面时,把刚才添加的publish数据放到publish的select里。因此需要一个一个页面作为中间人来处理。这个中间人需要完成的工作:1.执行add_view.html中的js,将publish的添加数据放在publish下拉列表中;2. 关闭publish添加页面的小窗口。

(2) 首先修改add_view方法中对request.POST的处理:

    def add_view(self, request):
ModelFormDemo = self.get_modelform_class() # 取到的是类名
form_obj = ModelFormDemo()
for bfield in form_obj:
if isinstance(bfield.field, ModelChoiceField): # bfield.field 获取的是字段对象;bfield.name 获取的是字段名称,类型是字符串;
bfield.is_related = True
# 获取该字段的模型表和模型表的app
# bfield.field.queryset.model 一对多或者多对多字段的关联模型表
relateed_model_name = bfield.field.queryset.model._meta.model_name
relateed_app_label = bfield.field.queryset.model._meta.app_label
_url = reverse("%s_%s_add" % (relateed_app_label, relateed_model_name))
bfield.add_url = _url+"?pop_id=id_%s" % bfield.name # id_%s 和select标签的id对应
if request.method == "POST":
form_obj = ModelFormDemo(request.POST)
if form_obj.is_valid():
obj = form_obj.save()
pop_id = request.GET.get("pop_id")
if pop_id: # 判断是否为小窗口的添加
ret = {"pk": obj.pk, "value": str(obj), "pop_id": pop_id}
return render(request, 'pop.html', ret)
else:
return redirect(self.get_list_url())
return render(request, 'add_view.html', locals())

(3)添加pop.html文件

(4)修改add_view.html,添加js

<script>
function pop_response(pk, text, field_name) {
// 创建一个option标签
var $option = $("<option>");
$option.val(pk);
$option.text(text);
$option.attr("selected", "selected");
$("#"+field_name).append($option);
}
</script>

(5)修改pop.html,添加js

<script>
window.opener.pop_response("{{ pk }}", "{{ value }}", "{{ pop_id }}");
window.close();
</script>

现在就实现pop了。也算是实现了自定义的admin的增删改查。

自定义admin组件的更多相关文章

  1. admin组件详解

    admin组件详解 先根据admin组件启动流程复习下django项目启动至请求过来发生的事 1将admin组件注册进app 2django项目启动 3在运行到定制的admin时执行其下面的apps文 ...

  2. 自定义admin管理工具&lpar;stark组件&rpar;

    自定义admin管理工具(stark组件) 创建项目 了解了admin的功能后,我们可以开始仿照admin编写我们自己的管理工具stark组件 首先创建一个新的项目,并创建三个app stark就是我 ...

  3. admin源码解析及自定义stark组件

    admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单 ...

  4. 模拟Django的admin自定义stark组件

    1.新建Django项目--新建app:app01和stark--在settings中配置app和数据库--在models.py中新建模型表--完成数据库迁移 2.在stark下的apps.py中: ...

  5. Django admin 组件 原理分析与扩展使用 之 sites&period;py &lpar;一&rpar;

    一 . 前言 Django 提供了admin 组件 为项目提供基本的管理后台功能(对数据表的增删改查). 本篇文章通过 admin源码 简单分析admin 内部原理 ,扩展使用方式,为以后进行定制和自 ...

  6. Django-CRM项目学习(一)-admin组件

    开始今日份整理 1.admin组件使用 1.1 创建django项目以及开启APP01 略 1.2 创建类 使用django自带的sqlite3的小型文件型的数据库 注:使用sqlite3类型的数据库 ...

  7. Django admin组件使用

    ADMIN 组件 介绍 admin 组件实现了更方便的WEB后台数据管理方式 settings.py 中第一个组件就是 : INSTALLED_APPS = [ 'django.contrib.adm ...

  8. Django admin组件源码流程

    admin 组件 Django 自带的用户后台组件 用于用户便携的操作 admin 组件核心 启动 注册 设计url 启动核心代码 每个app 通过 apps.py 扫描 admin.py 文件 并执 ...

  9. Python自定义分页组件

    为了防止XSS即跨站脚本攻击,需要加上 safe # 路由 from django.conf.urls import url from django.contrib import admin from ...

随机推荐

  1. 转载--thinkphp框架的路径问题 - 总结

    转自:http://blog.sina.com.cn/s/blog_827ddd950100ulyv.html TP中有不少路径的便捷使用方法,比如模板中使用的__URL__,__ACTION__等, ...

  2. 安卓手机与电脑无线传输文件(利用ftp服务)

    安卓手机与电脑无线传输文件(利用ftp服务) 手机与电脑无线传输文件,手机开启ftp服务,电脑能够对手机内全部文件进行全方位管理,包括上传.下载.新建.删除等,而且手机和电脑能够双向传输,很方便.手机 ...

  3. leetCode 70&period;Climbing Stairs (爬楼梯) 解题思路和方法

    Climbing Stairs  You are climbing a stair case. It takes n steps to reach to the top. Each time you ...

  4. ubuntu下mysql的常用命令

    首先安装mysql:sudo?apt-get?install?mysql-server?mysql-client? 1.终端启动MySQL:/etc/init.d/mysql start:(stop ...

  5. Windows10 Ubuntu子系统折腾

    UPDATE:(参考文章) 快速解决方案 使用cmder,设置startup参数为: %windir%\system32\bash.exe ~ 这样打开cmder就是默认进入bash了. ------ ...

  6. 201521123001《Java程序设计》第1周学习总结

    1. 本周学习总结 java和我们以前学的C语言的区别大致有: C语言可以直接操作内存,java不能直接操作: C语言的代码不能跨平台,java的代码可以跨平台: C语言有指针,java没有指针: C ...

  7. 一、Python介绍

    Python 是一门什么样的语言? python是一门动态解释性的强类型定义语言. 编程语言主要从以下几个角度为进行分类,编译型和解释型.静态语言和动态语言.强类型定义语言和弱类型定义语言,每个分类代 ...

  8. SQL Server 2016 发送邮件功能

    --1 安装好SQL Server 2016 --2 安装.Net 3.5 由于SQL Server 2016 安装不提示强制安装.NET 3.5 但是还是需要安装,数据库发送邮件会使用.NET 3. ...

  9. 四则运算level2

    package j; import java.util.Scanner; public class Main { public static void main(String[] args) { Sc ...

  10. 不使用if switch 各种大于 小于 判断2个数的大小

    哥们写的代码: dword big; __asm { mov eax,a mov ebx,b cmp eax,ebx jle HOHO big =ebx HOHO: big = eax } 网上搜了一 ...