admin组件

admin参考:https://www.cnblogs.com/yuanchenqi/articles/8323452.html

model参考:https://www.cnblogs.com/yuanchenqi/articles/8963244.html

# admin的参数,以及自定义显示
from django.contrib import admin
from app01.models import *
class BookConfig(admin.ModelAdmin):
list_display = ["nid", "title", "price", "publishDate"] # 定制显示那些列,不能放多对多
list_display_links = ["title"] # 查看链接
# list_filter = ["title", "publishDate", "authors"] # 过滤
list_editable = ["price"] # 编辑
search_fields = ["title", "price"] # 搜索字段
# date_hierarchy = "publishDate"
fields = ("title",) # 限定现实的字段
exclude = ("",) # 不显示哪些字段,和fields相反
ordering = ("nid",) # 排序
def patch_action(self, request, queryset):
queryset.update(publishDate="2019-11-22")
patch_action.short_description = "批量初始化"
actions = [patch_action]
admin.site.register(Book, BookConfig)
admin.site.register(Author)
admin.site.register(AuthorDetail)
admin.site.register(Publish)

url使用:

# url的嵌套
from django.contrib import admin
from django.http import HttpResponse
from django.urls import path
def yuan(request):
return HttpResponse("ok")
def test01(request):
return HttpResponse("test01")
from starkapp.service.stark import site
urlpatterns = [
path('admin/', admin.site.urls),
url(r"^stark/", site.urls),
url("^yuan/", ([
url(r"^test01/", ([
url(r"^test001/", test01),
url(r"^test002/", test01),
], None, None),
url(r"^test02/", test01),
url(r"^test03/", test01),
], None, None)) # 第一个是namespace, 第二个是app_name,
]

单例模式:

# 单例模式 (设计模式所有语言都有)
单例类:就是只允许类生成一个对象,所有的修改对这一个对象的修改,保证数据的统一性
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
class MyClass(Singleton):
a = 1
one = MyClass()
one.a = 2
two = MyClass()
print(two.a) python天生就只吃单例模式的,为什么?
写一个类,实例化一个对象,将其放入一个包中,其他地方使用的时候直接从这个里面导入(这个时候会生成一个.pyc,之后的每次使用这个对象都是从这个.pyc中取,可以理解为缓存),每次导入到的都是同一个对象,(内存地址相同)。


### admin源码:

# 使用
path('admin/', admin.site.urls),
admin.site.register(Book, BookConfig)
def register(self, model_or_iterable, admin_class=None, **options):
admin_class = admin_class or ModelAdmin
if isinstance(model_or_iterable, ModelBase):
model_or_iterable = [model_or_iterable] self._registry[model] = admin_class(model, self)
1.注册admin.py
admin.site.register(Book, BookConfig)
def register(self, model_or_iterable, admin_class=None, **options):
if not admin_class:
return admin_class=ModelAdmin
self._registry[model] = admin_class(model, self)
2.url设计
如何获取app名称和model名称
model._meta.app_label, model._meta.model_name
一次分发
def get_urls():
temp = []
for model, admin_class in admin.site._registry.items():
# url(r"app01/book/", )
temp.append(url(r"^%s/%s" %(model._meta.app_label, model._meta.model_name), yuan))
return temp urlpatterns = [
path('admin/', admin.site.urls),
url(r"^stark/", site.urls),
url("^yuan/", (get_urls(), None, None))
]
二次分发:
def add(request,):
return HttpResponse("add")
def delete(request,id):
return HttpResponse("delete")
def change(request,id):
return HttpResponse("change")
def list_view(request,):
return HttpResponse("list_view")
def get_urls_2():
temp = []
temp.append(url(r"^add/", add))
temp.append(url(r"^(\d+)/delete/", delete)) # 函数自己定义
temp.append(url(r"^(\d+)/change/", change))
temp.append(url(r"^", list_view)) return temp
def get_urls():
temp = []
for model, admin_class in admin.site._registry.items():
temp.append(url(r"^%s/%s/" %(model._meta.app_label, model._meta.model_name), (get_urls_2(), None, None)))
return temp
urlpatterns = [
path('admin/', admin.site.urls),
url(r"^stark/", site.urls),
url("^yuan/", (get_urls(), None, None))
]

stark组件:

查看页面:
表头
表数据
search
action

直接给出参考博客网址:https://www.cnblogs.com/smallmars/p/8657094.html

看不懂的话这里有视频:https://www.bilibili.com/video/av58974459?from=search&seid=4711332916584502531

这篇博客开始部分不好开头,我把开头整理一下,关于老男孩的项目有了规律,就是你发现你现在看的项目没有视频,那么可能是前几期教的,直接去B站搜这个项目名称相关的视频,就可以找,我是直接看博客,看到多级过滤的时候看不下去了才发现的,第九期的视频里面有很多好的项目可以看。

基础版本:

第一步:创建一个项目,去包里面最少有两个app(一个是stark组件的,一个是其他用来测试的stark组件是否产生工作的),我创建了app01和stark组件的app(starkapp)名字可以任意起只要你能找到就行
第二步:首先settings中注册:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'starkapp.apps.StarkappConfig',
'app01.apps.App01Config',
]
第三步:
starkapp下面创建一个service的包,记住是包,不是的话可能出现问题。同时创建一个stark.py文件

第四步:
在stark.py中写我们的程序
from django.conf.urls import url
from django.http import HttpResponse
from django.shortcuts import render class ModelStark(object):
list_display = [] def __init__(self, model, site):
self.model = model # 当前用户访问的模型表
self.site = site # 记住这个视图函数,如果下面的get_url_2中你没有添加它的话,你根本看不到只记得页面
def change_list(self, request):
ret = self.model.objects.all()
return render(request, "index.html", locals()) 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_urls_2(self):
temp = []
model_name = self.model._meta.model_name # 当前模型表
app_label = self.model._meta.app_label # 当前app 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.change_list, name="%s_%s_list" % (app_label, model_name)))
return temp @property
def urls_2(self):
return self.get_urls_2(), None, None # [], None, None class StarkSite(object): def __init__(self):
self._registry = {} def register(self, model, stark_class=None, **options):
if not stark_class:
# 如果注册的时候没有自定义配置类,执行
stark_class = ModelStark # 配置类 # 降配置类对象加到_registry字典中,建立模型类
self._registry[model] = stark_class(model, self) # _registry={'model':stark_class(model)} def get_urls(self):
"""构造一层url"""
temp = []
for model, stark_class_obj in self._registry.items():
# model:一个模型表
# stark_class_obj:当前模型表相应的配置类对象
model_name = model._meta.model_name
app_label = model._meta.app_label
# 分发增删改查url
temp.append(url(r"^%s/%s/" % (app_label, model_name), stark_class_obj.urls_2))
"""
path('app01/user/',UserConfig(User,site).urls2),
path('app01/book/',ModelStark(Book,site).urls2),
"""
return temp @property
def urls(self):
return self.get_urls(), None, None site = StarkSite() # 生成一个单例模式的对象
第五步:
有没有发现自己的项目中urls没配stark app的路由,现在就去
from django.conf.urls import url
from django.contrib import admin
from django.urls import path from starkapp.service.stark import site urlpatterns = [
path('admin/', admin.site.urls),
url(r"^stark/", site.urls), # 有没有发现这个site是我们自己写的呢个单例模式的对象,没错就是它
]
第六步:
我们要是用stark组件为我们的模型,创建增删改查页面,我们是不是应该去自己的项目app01了


```python
要是用这个模块我们就需要创建一个stark1.py(这个名字可以改,看完你就知道咋改了)在自己得项目里面,内容是这样的:

from starkapp.service.stark import site, ModelStark

from django.utils.safestring import mark_safe

from django.urls import reverse

from .models import *

自定义配置类

class BookStark(ModelStark):

pass

list_display = ["nid", "title", "price", edit, delete]

site.register(Book, BookStark) # 初始的你只需要把BookStark类定义了,使用我们site注册了就行,里面的内容可以直接pass


```html
第七步:
前段的页面也给你:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
</head>
<body>
<h3>数据展示</h3> <div class="container">
<div class="row">
<div class="col-md-8">
<table class="table table-striped table-hover">
<heade>
<tr>
<td>id</td>
<td>标题</td>
<td>价格</td>
</tr>
</heade> {% for obj in ret %}
<tr>
<td>{{ obj.nid }}</td>
<td>{{ obj.title }}</td>
<td>{{ obj.price }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</body>
</html>
第八步:
有没有发现,我们什么都写了,但是咋么让django找到我第六步创建的stark1.py呢?
现在回到stark组件app下面,在apps.py里面写下面的代码:
from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules
class StarkappConfig(AppConfig):
name = 'starkapp' # 程序启动时,扫描app下得指定文件(stark1.py)并执行
def ready(self):
autodiscover_modules('stark1') # 这句的作用就是让django找到我们的stark1。py模块,执行里面的site.register()方法,进行初始化路由。简化版的在这里完成了,已经可以看到页面了,我需要下次的写高级一点的。

中级版本:

可以将app01.stark1.py改成
# 自定义配置类
class BookStark(ModelStark):
def edit(self, obj=None, is_header=False):
if is_header:
return "操作"
return mark_safe("<a href='/stark/app01/book/%s/change'>编辑</a>" % obj.pk) def delete(self, obj=None, is_header=False):
if is_header:
return "操作"
return mark_safe("<a href='/stark/app01/book/%s/delete'>删除</a>" % obj.pk) list_display = ["nid", "title", "price", edit, delete]
# 补充
field_obj = Book._meta.get_field("title") #就是一个charField字段的对象
field_obj.max_length # 最大长度
starkapp.service.stark.py改成这样,你只需要改着一个函数的内容
def change_list(self, request):
header_list = []
for field in self.list_display:
if callable(field):
val = field(self, is_header=True)
header_list.append(val)
else:
field_obj = self.model._meta.get_field(field)
header_list.append(field_obj.verbose_name)
data_list = self.model.objects.all()
new_data_list = []
for obj in data_list:
temp = []
for field in self.list_display:
if callable(field):
val = field(self, obj)
else:
val = getattr(obj, field)
temp.append(val)
new_data_list.append(temp)
return render(request, "index.html", locals())
index.html改成这样
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
</head>
<body>
<h3>数据展示</h3> <div class="container">
<div class="row">
<div class="col-md-8">
<table class="table table-striped table-hover">
<thead>
<tr>
{% for foo in header_list %}
<td>{{ foo }}</td>
{% endfor %}
</tr>
</thead>
<tbody>
{% for data in new_data_list %}
<tr>
{% for item in data %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

​ 然后你的页面就变成了下面这一样:接下来你也想到只要在starkapp.service.satrk下的modelstark类中将,那些函数补全你可以删除和编辑了。

现在我感觉前面缺少选择框:
# 自定义配置类
class BookStark(ModelStark):
def edit(self, obj=None, is_header=False):
if is_header:
return "操作"
return mark_safe("<a href='/stark/app01/book/%s/change'>编辑</a>" % obj.pk) def delete(self, obj=None, is_header=False):
if is_header:
return "操作"
return mark_safe("<a href='/stark/app01/book/%s/delete'>删除</a>" % obj.pk) # 加了这么一行
def select(self, obj=None, is_header=None):
if is_header:
return mark_safe("<input id='mutPut' type='checkbox'>")
return mark_safe("<input type='checkbox' value=%s>" % obj.pk)
list_display = [select, "nid", "title", "price", edit, delete]
然后加上是加上了,但是没有事件: index页面引入jquery,加上下面一句就有了
<script>
$("#mutPut").click(function () {
if ($(this).prop("checked")){
$("tbody [type='checkbox']").prop("checked", true)
}else{
$("tbody [type='checkbox']").prop("checked", false)
}
})
</script>

# 但是现在我感觉,编辑删除以及选择的函数中的href中的url都是写死的,其他的模型使用不了,所以我们是用反向解析,这样就可以适用所有的模型了,同时为了每个模型都需要这三个方法,所以将这三个方法移入modelstark中。
# 展示编辑连接
def edit_link(self, obj=None, is_header=False):
if is_header:
return "操作"
name = "{}_{}_change".format(self.app_label, self.model_name)
return mark_safe("<a href=%s>编辑</a>" % reverse(name, args=(obj.pk,))) # 原本都是在用户自己的配置类中写,但是现在写入modelStark中是因为,是因为每个表可能都能用到,减少代码重复
def delete_link(self, obj=None, is_header=False):
if is_header:
return "操作"
name = "{}_{}_delete".format(self.app_label, self.model_name)
return mark_safe("<a href=%s>删除</a>" % reverse(name, args=(obj.pk,))) def select_btn(self, obj=None, is_header=None):
if is_header:
return mark_safe("<input id='mutPut' type='checkbox'>")
return mark_safe("<input type='checkbox' value=%s name='_selected_action'>" % obj.pk) # 组成新的list_display
@property
def get_list_display(self):
new_list_display = []
new_list_display.extend(self.list_display)
if not self.list_display_link:
new_list_display.append(ModelStark.edit_link)
new_list_display.append(ModelStark.delete_link)
new_list_display.insert(0, ModelStark.select_btn) return new_list_display # 相应的下面的路由也需要加上别名,我们是用app_name和mdoel——name,以及要执行的操作三者放一起组成每个模型唯一的别名
def get_urls_2(self):
temp = []
model_name = self.model._meta.model_name # 当前模型表
app_label = self.model._meta.app_label # 当前app 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.change_list, name="%s_%s_list" % (app_label, model_name)))
return temp def change_list(self, request):
中list_display = self.get_list_display() 记得改

list_display_link:

# django中的admin有list_display我们已经实现了,现在我们来实现list_display_link
BookStrak中加一句
class BookStark(ModelStark):
list_display = ["nid", "title", "price"]
list_display_link = ["nid", "title"] # 之后修改modelStark中的
@property
def get_list_display(self):
new_list_display = []
new_list_display.extend(self.list_display)
# 如果设置了list_display_link就把编辑删除
if not self.list_display_link:
new_list_display.append(ModelStark.edit_link)
new_list_display.append(ModelStark.delete_link)
new_list_display.insert(0, ModelStark.select_btn) return new_list_display # 最后将chang_list中的获取数据的代码改动一下
data_list = self.data_list
new_data_list = []
# 这里才是我们数据库中的数据
for obj in data_list:
temp = []
for field in self.config.get_list_display:
if callable(field):
val = field(self.config, obj)
else:
val = getattr(obj, field)
# 如果有list_display_link,就把他编程a标签
if field in self.config.list_display_link:
val = mark_safe("<a href=%s>%s</a>" % (self.config.get_edit_url(obj), val))
temp.append(val)
new_data_list.append(temp)

add:

# 发现admin还有添加数据的操作,我们也需要实现,使用modelForm
# 其他的组件我们慢慢实现
- ModelStark中加一个函数
# 使用modelform组件
def get_modelfrom_class(self):
class ModelFormClass(ModelForm):
class Meta:
model = self.model
fields = "__all__"
if not self.get_modelfrom_class:
return ModelFormClass
else:
return self.model_form_class
- 完善add添加函数的逻辑
def add(self, request):
form = self.model_form_class
if request.method == "GET":
return render(request, "add_index.html", locals())
else:
data = request.POST
form = form(data=data)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, "add_index.html", locals())
# add_index.html页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
<script type="application/javascript" src="/static/jquery.js"></script>
<style>
.form-group input{
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;
}
.error{
color: red;
}
</style>
</head>
<body>
<h3>添加数据</h3>
{% include "add_conponent.html" %}
</body>
</html>
# 使用模板循环使用
<div class="container">
<div class="row">
<div class="col-md-6">
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="">{{ field.label }}:</label>
<div>
{{ field }}
<span class="error pull-right">
{{ field.errors.0 }}
</span>
</div>
</div>
{% endfor %}
<p><input type="submit" class="btn btn-primary"></p>
</form>
</div>
</div>
</div>


### delete&chanage:

# 添加页面写完了,下来再我们继续完成编辑和删除页面的逻辑,视图中的函数modelStark中
def get_delete_url(self, obj):
delete_url = "{}_{}_delete".format(self.app_label, self.model_name)
return delete_url def get_add_url(self):
add_url = "{}_{}_add".format(self.app_label, self.model_name)
return add_url def get_list_url(self):
list_url = "{}_{}_list".format(self.app_label, self.model_name)
return list_url
# 上面三个就是为了获取反向解析的name名称
def delete(self, request, id):
del_obj = self.model.objects.filter(nid=id).first()
if request.method == "GET":
list_url = self.get_list_url()
# 为什么要确认页面,是因为重要数据需要二次确认,防止误删
return render(request, "del_index.html", locals())
else:
del_obj.delete()
return redirect(self.get_list_url()) # 编辑页面
def change(self, request, id):
form = self.get_modelfrom_class()
obj = self.model.objects.filter(pk=id).first()
if request.method == "GET":
form = form(instance=obj)
return render(request, "change_index.html", locals())
else:
form = form(data=request.POST, instance=obj)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, "change_index.html", locals())
# 编辑的html页面代码:
# 因为代码和添加页面类似,所以讲代码整合,弄一个子组件
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
<script type="application/javascript" src="/static/jquery.js"></script>
<style>
.form-group input{
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;
}
.error{
color: red;
}
</style>
</head>
<body>
<h3>编辑数据</h3>
{% include "add_conponent.html" %}
</body>
</html>
# add_conponent.html
<div class="container">
<div class="row">
<div class="col-md-6">
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="">{{ field.label }}:</label>
<div>
{{ field }}
<span class="error pull-right">
{{ field.errors.0 }}
</span>
</div>
</div>
{% endfor %}
<p><input type="submit" class="btn btn-primary"></p>
</form>
</div>
</div>
</div>
# 刪除页面比较简单delete_index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>删除</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
<script src="/static/jquery-3.2.1.min.js"></script>
</head>
<body>
<h3>删除页面</h3>
<div>
<p>{{ del_obj }}</p>
</div>
<form action="" method="post">
{% csrf_token %}
<input type="submit" value="确认删除" class="btn btn-danger">
<a href="{% url list_url %}" class="btn btn-primary">取消</a>
</form>
</body>
</html>

分页:

# 这是自己写的一个分页器类,其中我们有添加了一些参数
# params self.request.GET信息 主要是为了保存,请求的参数信息,比如你选了一堆的过滤条件,比如:
# http://localhost:8000/stark/app01/book/page=1&title=django,但是你的分页器中的路径是这样子的 http://localhost:8000/stark/app01/book/page=1,用户的条件title=django没有保留,那么问题来了,用户发现这样我们次都要选择,你这网站不好用我不玩了,所以为了提高用户体验,我们需要保留用户的筛选过滤条件,那么我们就需要加一个参数params
class Pagination(object): def __init__(self, current_page, all_count, base_url, params, per_page_num=4, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param base_url: 分页中显示的URL前缀
:param pager_count: 最多显示的页码个数
:param params: self.request.GET信息
""" 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
import copy
params = copy.deepcopy(params)
params._mutable = True
self.params = params # 总页码
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) @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/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_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1 page_html_list = []
# 这个地方就用到了,每次我们把保留的参数全部拼接进入url中那么就没有问题了 # self.base_url:http://localhost:8000/stark/app01/book/
# self.params.urlencode(): ?page=1&title=django
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)

分页组件直接在starkapp下面建一个util的包放进去就好了,目录结构是这样子的。

然后再添加分页的时候返现modelstark组建中change_list函数下的代码是在是太多了,而且后面还需要继续添加,我们整合一下。用一个类ChangeList来封装一下,其中的我们主要是需要获取表头和表体的数据,所以将这两个代码各写一个函数。同时我们将分页组件引入。
class ChangeList(object):
def __init__(self, config, request, queryset):
self.config = config
self.request = request
self.queryset = queryset from starkapp.util.paginator import Pagination
current_page = request.GET.get("page")
all_count = self.queryset.count()
base_url = self.request.path_info
params = self.request.GET
paginator = Pagination(current_page, all_count, base_url, params)
data_list = self.queryset[paginator.start: paginator.end]
self.paginator = paginator
self.data_list = data_list def get_header(self):
header_list = []
# 这里的代码是处理表头数据的
for field in self.config.get_list_display:
if callable(field):
val = field(self.config, is_header=True)
header_list.append(val)
else:
if field == "__str__":
header_list.append(self.config.model._meta.model_name.upper())
else:
field_obj = self.config.model._meta.get_field(field)
header_list.append(field_obj.verbose_name)
return header_list def get_body(self):
data_list = self.data_list
new_data_list = []
# 这里才是我们数据库中的数据
for obj in data_list:
temp = []
for field in self.config.get_list_display:
if callable(field):
val = field(self.config, obj)
else:
val = getattr(obj, field)
# 如果有list_display_link,就把他编程a标签
if field in self.config.list_display_link:
val = mark_safe("<a href=%s>%s</a>" % (reverse(self.config.get_edit_url(obj), args=(obj.pk,)), val))
temp.append(val)
new_data_list.append(temp)
return new_data_list
# modelstark类中的change_list函数进行简化, 这下是不是很简洁了,而且解耦也做好了
# 首页展示页面
def change_list(self, request):
self.request = request
queryset = self.model.objects.all()
# 至于为什么将self传过去,是因为changeList类中使用到了
cl = ChangeList(self, request, queryset)
return render(request, "index.html", locals())

search:

# modelstark中添加
@property
def get_search_condition(self):
from django.db.models import Q
search_condition = Q()
search_condition.connector = "or" # 设置关系为或
if self.search_fields: # ["title", "price"]
key_word = self.request.GET.get("q", None)
# 如果有值才添加,没有纸就直接返回空的Q()
self.key_word = key_word
if key_word:
for search_field in self.search_fields:
# 因为条件设置得是or所以这里才可以成立,如果是and,全部遍历加进去查询可能会出错
search_condition.children.append((search_field + "__contains", key_word))
return search_condition
# change_list改
# 首页展示页面
def change_list(self, request):
self.request = request
add_url = self.get_add_url()
# search模糊查询
queryset = self.model.objects.filter(self.get_search_condition)
# filter模糊查询
queryset = queryset.filter(self.get_filter_condition)
cl = ChangeList(self, request, queryset)
return render(request, "index.html", locals())
# index.html改
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
<script type="application/javascript" src="/static/jquery.js"></script>
</head>
<body>
<h3>数据展示</h3>
<div class="container">
<div class="row">
<div class="col-md-8">
<!-添加-->
<a href="{% url add_url %}"><button class="btn btn-primary">添加</button></a>
<!-搜索-->
{% if cl.config.search_fields %}
<div class="pull-right form-group">
<form action="" method="get" class="form-inline">
<input type="text" class="form-control" name="q" value="{{ cl.config.key_word }}">
<input type="submit" class="btn btn-primary" value="search">
</form>
</div>
{% endif %}
<!-主题内容-->
<form action="" method="post">
{% csrf_token %}
<!-批量操作下拉框-->
<div>
<select class="form-control" name="action" id="" style="width: 200px;
margin: 5px 0; display: inline-block; vertical-align: -1px">
<option value="">------------</option>
{% for item in cl.handler_action %}
<option value="{{ item.name }}">{{ item.desc }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-primary">Go</button>
</div> <!-展示-->
<table class="table table-striped table-hover">
<thead>
<tr>
{% for foo in cl.get_header %}
<td>{{ foo }}</td>
{% endfor %}
</tr>
</thead>
<tbody>
{% for data in cl.get_body %}
<tr>
{% for item in data %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</form>
<!-分页-->
<nav aria-label="Page navigation" class="pull-right">
<ul class="pagination">
{{ cl.paginator.page_html|safe }}
</ul>
</nav>
</div>
</div>
</div>
<script>
$("#mutPut").click(function () {
if ($(this).prop("checked")){
$("tbody [type='checkbox']").prop("checked", true)
}else{
$("tbody [type='checkbox']").prop("checked", false)
}
})
</script>
</body>
</html>

actions:

# modelstark中添加
@property
def get_filter_condition(self):
from django.db.models import Q
filter_condition = Q()
for field, val in self.request.GET.items():
if field in self.list_filter:
filter_condition.children.append((field, val))
return filter_condition
# 修改 加上name属性
def select_btn(self, obj=None, is_header=None):
if is_header:
return mark_safe("<input id='mutPut' type='checkbox'>")
return mark_safe("<input type='checkbox' value=%s name='_selected_action'>" % obj.pk)
# 更改change_list函数,前段页面就是上面的呢个
# 首页展示页面
def change_list(self, request):
if request.method == "POST":
func_name = request.POST.get("action")
# getlist多个值处理
pk_list = request.POST.getlist("_selected_action")
queryset = self.model.objects.filter(pk__in=pk_list)
func = getattr(self, func_name)
func(queryset=queryset)
self.request = request
add_url = self.get_add_url()
# search模糊查询
queryset = self.model.objects.filter(self.get_search_condition)
# filter模糊查询
queryset = queryset.filter(self.get_filter_condition)
cl = ChangeList(self, request, queryset)
return render(request, "index.html", locals())
# modelstark类中加上
def get_actions(self):
temp = []
temp.extend(self.actions)
temp.append(ModelStark.patch_delete)
return temp # changeList类中加上
class ChangeList(object):
def __init__(self, config, request, queryset):
self.config = config
self.request = request
self.queryset = queryset from starkapp.util.paginator import Pagination
current_page = request.GET.get("page")
all_count = self.queryset.count()
base_url = self.request.path_info
params = self.request.GET
paginator = Pagination(current_page, all_count, base_url, params)
data_list = self.queryset[paginator.start: paginator.end]
self.paginator = paginator
self.data_list = data_list
# actions 批量操作的动作
self.actions = self.config.get_actions() # 这里的获取actions是modelstark类中的
def handler_action(self):
temp = []
for action in self.actions:
temp.append({"name": action.__name__, "desc": action.desc if getattr(action, "desc", None) else action.__name__})
return temp




### filter: 难

补充必看:
# self.config.model._meta.get_field(filter_field_name)给你看一下拿的是什么吧(下面第一张图),可以看到拿到的关联的对多字段的实例,其中我的django是2.0版本以上的,你可以通过related_model拿到对应的模型,之后通过模型管理器拿到对应的数据。(2.0版本已一下的试一试rel.to.objects.all(),这个我也没试,拿不到自己搜一搜吧。)
filter_field_obj = self.config.model._meta.get_field(filter_field_name)
# filter_field = FilterField(filter_field_name, filter_field_obj, self)
print(type(filter_field_obj))
print(filter_field_obj.related_model.objects.all())
from django.db.models.fields.related import ManyToManyField
from django.db.models.fields.related import ForeignKey

# 更爱changeList类
class ChangeList(object):
def __init__(self, config, request, queryset):
self.config = config
self.request = request
self.queryset = queryset from starkapp.util.paginator import Pagination
current_page = request.GET.get("page")
all_count = self.queryset.count()
base_url = self.request.path_info
params = self.request.GET
paginator = Pagination(current_page, all_count, base_url, params)
data_list = self.queryset[paginator.start: paginator.end]
self.paginator = paginator
self.data_list = data_list
# actions 批量操作的动作
self.actions = self.config.get_actions()
# filter 过滤的字段
self.list_filter = self.config.list_filter def get_filter_link_tag(self):
link_list = {}
data = self.request.GET
import copy
params = copy.deepcopy(data)
for filter_field_name in self.config.list_filter:
# 为什么放里面而不是放外面
data = self.request.GET
import copy
params = copy.deepcopy(data) current_id = self.request.GET.get(filter_field_name, 0)
filter_field_obj = self.config.model._meta.get_field(filter_field_name)
# filter_field = FilterField(filter_field_name, filter_field_obj, self)
if isinstance(filter_field_obj, ManyToManyField) or isinstance(filter_field_obj, ForeignKey):
data_list = filter_field_obj.related_model.objects.all()
else:
data_list = self.config.model.objects.values_list("pk", filter_field_name)
temp = []
# 处理全部标签
if params.get(filter_field_name, None):
del params[filter_field_name]
temp.append("<a href='?%s'>全部</a>" % (params.urlencode()))
else:
temp.append("<a class='active' href='?%s'>全部</a>" % (params.urlencode()))
# 处理数据标签
for obj in data_list:
if isinstance(filter_field_obj, ManyToManyField) or isinstance(filter_field_obj, ForeignKey):
pk, text = obj.pk, str(obj)
params[filter_field_name] = pk
else:
pk, text = obj
params[filter_field_name] = text
_url = params.urlencode()
if current_id == str(pk) or current_id == text:
link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text) # %s/%s/
else:
link_tag = "<a href='?%s'>%s</a>" % (_url, text) # %s/%s/
temp.append(link_tag)
link_list[filter_field_name] = temp
# 在下面我自己封装的类,可以自己玩玩,只需要将这个函数中的代码全部注释,保留这两句就可以了
# ff = FilterField(self.config, self.request)
# link_list = ff.get_filter_link()
return link_list
# 由于发现上面的呢个函数是在太长,所以我自己试试能不能将它分成一个类,可以练习一下,其实我们写代码的时候当然是都写在一个函数中看起来方便,但是为了装逼吧,所以将每个功能分开,让每个函数变得简单一些,个人感觉这种方式其实不方便阅读代码。
# 为每一个过滤的字段封装成整体类
class FilterField(object):
def __init__(self, config, request):
self.config = config
self.request = request def get_data(self):
if isinstance(self.filter_field_obj, ForeignKey) or isinstance(self.filter_field_obj, ManyToManyField):
return self.filter_field_obj.related_model.objects.all()
elif self.filter_field_obj.choices:
return self.filter_field_obj.choices
else:
return self.config.model.objects.values_list("pk", self.filter_field_name) def get_params(self):
data = self.request.GET
import copy
params = copy.deepcopy(data)
return params def get_filter_link(self):
link_list = {}
for filter_field_name in self.config.list_filter:
self.filter_field_name = filter_field_name
# 为什么放里面而不是放外面
params = self.get_params()
current_id = self.get_current_id()
self.get_filter_field_obj()
temp = self.get_link_list(params, current_id)
link_list[filter_field_name] = temp
return link_list def get_current_id(self):
current_id = self.request.GET.get(self.filter_field_name, 0)
return current_id def get_filter_field_obj(self):
filter_field_obj = self.config.model._meta.get_field(self.filter_field_name)
self.filter_field_obj = filter_field_obj def get_link_list(self, params, current_id):
data_list = self.get_data()
temp = []
temp = self.deal_all_tag(params, temp)
temp = self.deal_data_tag(params, data_list, current_id, temp)
return temp def deal_data_tag(self, params, data_list, current_id, temp):
for obj in data_list:
pk, text, params = self.get_pk_text(params, obj)
_url = params.urlencode()
if current_id == str(pk) or current_id == text:
link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text) # %s/%s/
else:
link_tag = "<a href='?%s'>%s</a>" % (_url, text) # %s/%s/
temp.append(link_tag)
return temp def get_pk_text(self, params, obj):
if isinstance(self.filter_field_obj, ManyToManyField) or isinstance(self.filter_field_obj, ForeignKey):
pk, text = obj.pk, str(obj)
params[self.filter_field_name] = pk
else:
pk, text = obj
params[self.filter_field_name] = text
return pk, text, params def deal_all_tag(self, params, temp):
if params.get(self.filter_field_name, None):
del params[self.filter_field_name]
temp.append("<a href='?%s'>全部</a>" % (params.urlencode()))
else:
temp.append("<a class='active' href='?%s'>全部</a>" % (params.urlencode()))
return temp

pop页面:

1.如果在一对多和多对多后面显示加号
2. +对应跳转路由
3. 保存记录的同时,将原页面中的下拉菜单中天骄新纪录
知识点:
window.opener当前窗口的打开窗口

最后一个也就是下面这幅图中的+号的实现,点击加号直接跳到创建页面,其中的难点就是上面列出的三条,我们一个一个解决。

# 1.如何在一对多和多对多后面显示加号(我们发先这个是在添加页面的时候才显示的所以我们找到add函数),跳到add_index.html页面发现。所有的字段都是通过form组件渲染出来的,那么有没有什么解决的办法。先来一个简易版的直接加。



```python
# 这个时候发现,每个后面都有咋办,我们只想在多对所以及一对多字段添加,这个时候我想到了model,但是发现好想联系不上。然后突然想到使用form组件渲染的,我们打印一下每个字段的类型,发现是一个boundField类型。进入看看
```

它有这么多的属性,我们一个一个打印试试,最后发现field就是我们的字段。


```
这个时候操作性就来了,我们可以判断这个字段是不是,一对多的类型(多对多继承一对多),然后给他加一个属性。
```

    # modelstark的add函数中
def add(self, request):
modelform = self.get_modelfrom_class()
from django.forms.boundfield import BoundField
form = modelform()
for field in form:
print(type(field.field))
if isinstance(field.field, ModelChoiceField):
field.is_pop = True
related_model_name = field.field.queryset.model._meta.model_name
related_app_name = field.field.queryset.model._meta.app_label
_url = reverse("{}_{}_add".format(related_app_name, related_model_name)) + \
"?pop_res_id=id_{}".format(field.name)
field.url = _url
if request.method == "GET":
return render(request, "add_index.html", locals())
else:
data = request.POST
form = modelform(data=data)
if form.is_valid():
obj = form.save()
pop_res_id = request.GET.get("pop_res_id")
if pop_res_id:
res = {"pk": obj.pk, "text": str(obj), "pop_res_id": pop_res_id}
return render(request, "pop.html", {"res": res})
else:
return redirect(self.get_list_url())
else:
return render(request, "add_index.html", locals())
<div class="container">
<div class="row">
<div class="col-md-6 col-xs-6 col-md-offset-3">
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="">{{ field.label }}:</label>
<div style="position: relative">
{{ field }}
<span class="error pull-right">
{{ field.errors.0 }}
</span>
{% if field.is_pop %} # 然后这里加上判断就可以了
<a onclick="pop('{{ field.url }}')" style="color: blue; font-size: 30px; position: absolute;top: -10px;">+</a>
{% endif %}
</div>
</div>
{% endfor %}
<p><input type="submit" class="btn btn-primary"></p>
</form>
</div>
</div>
</div>
2. +对应跳转路由
加号对应的跳转路由我们可以直接看django自带的admin路由是什么样?
http://localhost:8000/stark/app01/publish/add/?pop_res_id=id_publish但是是动态的所以我们想第一步中。给每个外键属性的字段加上一个url
for field in form:
print(type(field.field))
if isinstance(field.field, ModelChoiceField):
field.is_pop = True
related_model_name = field.field.queryset.model._meta.model_name
related_app_name = field.field.queryset.model._meta.app_label
# 就是这句
_url = reverse("{}_{}_add".format(related_app_name, related_model_name)) + \
"?pop_res_id=id_{}".format(field.name)
field.url = _url
这个时候还要解决的时候打开一个新页面
添加到添加页面的组件中,也就是下面的图。
<script>
function pop(url) {
window.open(url,"","width=600,height=400,top=100,left=200")
}
</script>

但是又如何做到,在点击保存之后关闭页面的呢。看到下面红框中的判断没有,返回了一个pop.html页面。

3. 保存记录的同时,将原页面中的下拉菜单中添加新纪录
# pop.html页面就这几句代码,加注释
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
# 这句话是将,数据传入打开它的父window,pop_response是父window的一个函数,自己定义就好了。
window.opener.pop_response('{{ res.pk }}', '{{ res.text }}', '{{ res.pop_res_id }}');
# 他才是关闭窗口,直接在js中将窗口关闭
window.close()
</script>
</body>
</html>
# add_index.html如果处理呢?看到里面的函数你就明白了,就是生成一个option标签,绑定数据和属性给,插入下拉框中。

然后到这里就完了。不习惯使用git的但是又怕代码没地方保存哎?

地址:https://github.com/maxhope8/stark.git(我直接把项目代码弄上去了,但是外面的目录没传上去)

24.stark组件全部的更多相关文章

  1. 【django之stark组件】

    一.需求 仿照django的admin,开发自己的stark组件.实现类似数据库客户端的功能,对数据进行增删改查. 二.实现 1.在settings配置中分别注册这三个app # Applicatio ...

  2. django 之 stark组件

    ----------------------------------------------------------------烦恼没完没了,内心动荡不安,呜呼哀哉. 一.有个特殊的需求,需要用sta ...

  3. 设计stark组件

    设计stark组件 作者:Eric 微信:loveoracle11g 新建Django项目crm_1随便起名 然后再创建一个App manage.py@crm_1 > startapp star ...

  4. stark组件的分页,模糊查询,批量删除

    1.分页组件高阶 2.整合展示数据showlist类 3.stark组件之分页 3.stark组件之search模糊查询 4.action批量处理数据 4.总结 1.分页组件高阶 1.分页的class ...

  5. stark组件配置,二层URL

    1.django的admin配置 2 stark组件开发 3.2层url分发 4.小结 1.django的admin配置 model.py from django.db import models # ...

  6. CRM项目之stark组件

    . stark也是一个app(用startapp stark创建),目标时把这个做成一个可以拔插的组件 . setting文件下INSTALLED_APPS 路径要配置好(app的注册) . 写好si ...

  7. stark组件开发之列表页面应用示例

    已经解决的,自定义的扩展函数,功能.但是 不可能返回. 一个 固定的页面把!  应该是,点击那条 记录之后的编辑, 就会跳转到相应的,编辑页面.所以 这个标签的  <a href="/ ...

  8. Django之stark组件

    现在让我说啥是stark组件,我也说不清楚.反正从今天讲的知识来看,今天完成的就是自己写一个模块,这个模块包含了admin后台管理工具的一些比较好用的功能,我们把它提炼出来,也就是相当于自己写一个ad ...

  9. CRM项目之stark组件(2)

    那么从今天开始呢,我们就要开始设计属于我们自己的admin组件,起个名字就叫stark吧(当然你愿意叫什么都可以). stark组件之四步走 仿照admin组件实现流程,stark组件要实现四件事情: ...

随机推荐

  1. mybatis-plus 错误 java.lang.NoClassDefFoundError

    错误 java.lang.NoClassDefFoundError: org/apache/velocity/context/Context 使用mybatis-plus自动生成文件的时候,报下面的错 ...

  2. Linux系列之ftp

    ftp的详细用法,请访问https://www.cnblogs.com/juandx/p/3998418.html 1.Windows搭建IIS类型的ftp服务器 步骤 1.打开控制面板,接着打开程序 ...

  3. TCP流量控制和拥塞避免

    TCP的流量控制      所谓的流量控制就是让发送方的发送速率不要太快,让接收方来得及接受.利用滑动窗口机制可以很方便的在TCP连接上实现对发送方的流量控制.TCP的窗口单位是字节,不是报文段,发送 ...

  4. 图解django的生命周期

    其实django的生命周期的大体框架就是这样,剩下的细致的,自己再补充! 图片实在是有点抽象! 谅解!! koala-----给你更多技术小干货

  5. Web前端开发CSS基础

    CSS 层叠样式表(英文全称:Cascading Style Sheets),是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言.CSS不 ...

  6. 并不对劲的2-SAT

    说明 板板题链接 这个人讲得很清楚 WAWAWAWA 建的边"不完整",比如当限制是"x为1时y一定为1"时,连x->y的边时,忘记连y'->x'的 ...

  7. 怎样创建并使用 vue 组件 (component) ?

    组件化开发 需要使用到组件, 围绕组件, Vue 提供了一系列功能方法, 这里仅记录组件的 最简单 的使用方法. 1. 通过 Vue.component(tagName, options) 注册一个 ...

  8. linux常用的bash指令

    文本处理 awk sed grep sort uniq cat cut echo fmt tr nl egrep fgrep wc 进程监视 ps top htop atop lsof 网络 nmap ...

  9. Left4Dead2 LAN Online

    Left4Dead2 LAN Online Franklin vs Wolverine 求生之路 局域网联机说明 ============================ 局域网联机方法: 1.先找到 ...

  10. 如何使用koa搭建一个简单服务

    1.首先检测是否已经有node环境?   把Windows的黑窗体的命令行工具调用出来   敲击命令行node -v , 然后,就可以看到这个打印出了一个版本号,这就证明我们的node.js已经是安装 ...