配置路由

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

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)

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

list_display

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

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,此时页面就能显示数据了

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

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,页面效果:

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,表头和表单都有数据了。

 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,

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

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>

此时访问添加页面,效果

如果想让字段显示中文怎么办。在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 .......

此时去页面访问,

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方法中的代码:

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/,然后就会有这样的提示

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

查不到__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管理工具(stark组件)

    自定义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 组件

    摘要:学习 Django admin 组件,仿照源码的逻辑,自定义了一个简易的 stark 组件,实现类似 admin 的功能. 可自动生成 url 路由,对于model 有与之相应的配置类对象,可进 ...

  6. Django admin 组件 原理分析与扩展使用 之 sites.py (一)

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

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

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

  8. Django admin组件使用

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

  9. Django admin组件源码流程

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

随机推荐

  1. Java 学习体系结构

    一. JavaWEB 阶段课程体系结构 java se基础学习 二 .JavaWEB 阶段课程体系结构 第一阶段:前端开发阶段 1 HTML   2 CSS JS    3JS    4 JQuery ...

  2. MVC EF 移除建表时自动加上s的复数形式

    移除建表时自动加上s的复数形式 public class DBContext : DbContext { public DBContext() : base("name=DBContext& ...

  3. [LOJ3087][GXOI/GZOI2019]旅行者——堆优化dijkstra

    题目链接: [GXOI/GZOI2019]旅行者 我们考虑每条边的贡献,对每个点求出能到达它的最近的感兴趣的城市(设为$f[i]$,最短距离设为$a[i]$)和它能到达的离它最近的感兴趣的城市(设为$ ...

  4. CF1157C1-Increasing Subsequence (easy version)题解

    原题地址 题目大意:

  5. 20165223《网络对抗技术》Exp1 PC平台逆向破解

    目录--PC平台逆向破解 1 逆向及BOF基础实践说明 1.1 实践内容 1.2 实践要求 1.3 基础知识 2 实验步骤 2.1 直接修改程序机器指令,改变程序执行流程 2.2 通过构造输入参数,造 ...

  6. Vue状态管理之Vuex

    Vuex是专为Vue.js设计的状态管理模式.采用集中存储组件状态它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 1.首先让我们从一个vue的计数应用开始 ...

  7. python学习笔记之元祖

    #元祖 只读列表,可循环查询,可切片.#儿子不能改,孙子可能可以改. tu = (1,2,3,'alex',[2,3,4,'taibai'],'egon') print(tu[3]) print(tu ...

  8. centos7 部署安装gitlab服务器

    概念: git 是一种版本控制系统,是一个命令,是一种工具 gitlib 是用于实现git功能的开发库 github 是一个基于git实现的在线代码托管仓库,包含一个网站界面,向互联网开放 gitla ...

  9. ubuntu 默认 root 密码

    安装完Ubuntu后忽然意识到没有设置root密码,不知道密码自然就无法进入根用户下.到网上搜了一下,原来是这麽回事.Ubuntu的默认root密码是随机的,即每次开机都有一个新的root密码.我们可 ...

  10. Linux Centos6.9下安装部署VNC的实操详述

    VNC (Virtual Network Console)是虚拟网络控制台的缩写.它 是一款优秀的远程控制工具软件,由著名的AT&T的欧洲研究实验室开发的.VNC 是在基于 UNIX和 Lin ...