根据需求定制 admin
定义 list 页面
自定义 list_filter
首先,完成过滤器的功能,需要自定义过滤器。在 PostAdmin 定义的上方定义如下代码:
class CategoryOwnerFilter(admin.SimpleListFilter):
"""
自定义过滤器只展示当前用户分类
"""
title = '分类过滤器'
parameter_name = 'owner_category'
def lookups(self, request, model_admin):
return Category.objects.filter(owner=request.user).values_list('id', 'name')
def queryset(self, request, queryset):
category_id = self.value()
if category_id:
return queryset.filter(category_id=self.value())
return queryset
通过继承 Django admin 提供的 SimpleListFilter 类来实现自定义过滤器,之后只需要把自定义过滤器配置到 ModelAdmin 中即可。
SimpleListFilter 类提供了两个属性和两个方法。title 用于展示标题,parameter_name 就是查询时 URL 参数的名字,比如查询分类 id 为 1 的内容时,URL 后面的 Query 部分是 ?owner_category=1
,此时就可以通过过滤器拿到这个 id,从而进行过滤。
lookups:返回要展示的内容和查询用的 id(就是上面 Query 用的)。
queryset:根据 URL Query 的内容返回列表页数据。比如如果 URL 最后的 Query 是 ?owner_category=1
,那么这里拿到的 self.value()
就是 1,此时就会根据 1 来过滤 Queryset 。这里的 Queryset 是列表页所有展示数据的合集,即 post 的数据集。
编写完之后,只需要修改 list_filter 为:
list_filter = [CategoryOwnerFilter]
就能让用户在侧边栏的过滤器中只看到自己创建的分类了。
自定义列表页数据
完成了 list_filter 的定制,还需要继续定制,让当前登录的用户在列表页中只能看到自己创建的文章。
PostAdmin 继承自 admin.ModelAdmin,显然需要看 ModelAdmin 提供了哪些方法,可以使其重写。
blog/admin.py:
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = [
'title', 'category', 'status',
'created_time', 'owner', 'operator'
]
# 省略部分代码
def save_model(self, request, obj, form, change):
obj.owner = request.user
return super(PostAdmin, self).save_model(request, obj, form, change)
def get_queryset(self, request):
qs = super(PostAdmin, self).get_queryset(request)
return qs.filter(owner=request.user)
从这两个定制可以看出来,关于数据过滤的部分,只需要找到数据源在哪,也就是 QuerySet 最终在哪生成,然后对其进行过滤即可。
编辑页面的配置
首先,是按钮位置,在编辑页面中主要的按钮就是 “保存” ,不过 Django 提供了另外两个便于操作的按钮:“保存并继续” 以及 “保存并新增另一个” 。关于按钮的位置,有一个配置项可以完成:save_on_top 用来控制是否在页面顶部展示上述的三个按钮。
对于字段是否展示以及展示顺序的需求,可以通过 fields 或者 fieldset 来配置。通过 exclude 可以指定哪些字段是不展示的,比如下面的 owner,是在程序中自动赋值当前用户的。代码如下:
# blog/admin.py
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
# 省略其他代码
exclude = ('owner', )
fields = (
('category', 'title'),
'desc',
'status',
'content',
'tag',
)
# 省略其他代码
fields 配置有两个作用,一个是限定要展示的字段,另外一个是配置展示字段的顺序。
fieldsets 用来控制页面布局,先用它来替换上述代码的 fields :
fieldsets = (
('基础配置', {
'description': '基础配置描述',
'fields': (
('title', 'category'),
'status',
),
}),
('内容', {
'fields': (
'desc',
'content',
),
}),
('额外信息', {
'classes': ('collapse', ),
'fields': ('tag', ),
})
)
fieldsets 要求的格式是有两个元素的 tuple 的 list,如:
fieldsets = (
(名称, {内容}),
(名称, {内容}),
)
其中包含两个元素的 tuple 内容,第一个元素是当前版块的名称,第二个元素是当前版块的描述、字段和样式配置。也就是说,第一个元素是 string,第二个元素是 dict,而 dict 的 key 可以是 fields、description 和 classes。
fields 的配置效果同上面一样,可以控制展示哪些元素,也可以给元素排序并组合元素的位置。classes 的作用就是给要配置的版块加上一些 CSS 属性,Django admin 默认支持的是 collapse 和 wide 。当然,也可以写其他属性,然后自己来处理样式。
关于编辑页的配置,还有针对多对多字段展示的配置 filter_horizontal 和 filter_vertica1,它们用来控制多对多字段的展示效果。
自定义静态资源引入
可以通过自定义 Media 类来往页面上增加想要添加的 JavaScript 以及 CSS 资源。
class PostAdmin(admin.ModelAdmin):
class Media:
css = {
'all': ("https://cdn.bootcss.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css", ),
}
js = ('https://cdn.bootcss.com/bootstrap/4.0.0-beta.2/js/bootstrap.bundle.js', )
自定义 Form
上面的所有配置都是基于 ModelAdmin 的,如果有更多的定制需求,比如文章描述字段能够以多行多列的方式展示,这就需要用到 ModelForm 了。
先在 blog 的目录下新增一个文件 adminforms.py。因为这是用作后台管理的 Form,所以这里要命名为 adminforms 而不是 forms。这只是为了与前台针对用户输入进行处理的 Form 区分开来。
接着,需要在里面编写代码,定义 Form。关于 Form 的作用,可以说 Form 跟 Model 的逻辑是一致的,Model 是对数据库中字段的抽象,Form 是对用户输入以及 Model 中要展示数据的抽象。
在 adminforms.py 中,通过 Form 来定制 status 这个字段的展示:
from django import forms
class PostAdminForm(forms.ModelForm):
desc = forms.CharField(widget=forms.Textarea, label='摘要', required=False)
接着将其配置到 admin 定义中:
# blog/admin.py
from .adminforms import PostAdminForm
class PostAdmin(admin.ModelAdmin):
form = PostAdminForm
# 省略其他代码
这时,刷新页面,就能看到文章描述字段已经改为 Textarea 组件了。
定制 site
一个 site 对应一个站点,现在有一个新的需求:用户模块的管理应该跟文章分类等数据的管理分开。另外,也需要修改后台的默认展示。
url(r'^admin/', admin.site.urls)
这里的 site 是 django.contrib.admin.AdminSite 的一个实例,因此,可以继承 AdminSite 来定义自己的 site,代码如下:
from django.contrib.admin import AdminSite
class CustomSite(AdminSite):
site_header = 'Typeidea'
site_title = 'Typeidea管理后台'
index_title = '首页'
custom_site = CustomSite(name='cus_admin')
把代码放到 typeidea/typeidea/custom_site.py 中,然后修改所有 App 下 register 部分的代码,以 PostAdmin 为例:
把 @admin.register(Post)
改为 @admin.register(Post, site=custom_site)
,这需要在模块上引入 from typeidea.custom_site import custom_site
上面用的是 reverse 方式来获取后台地址,用到了 admin 这个名称,因此需要调整 blog/admin.py 代码。
原代码:
def operator(self, obj):
return format_html(
'<a href="{}">编辑</a>',
reverse('admin:blog_post_change', args=(obj.id,))
)
operator.short_description = '操作'
修改为:
def operator(self, obj):
return format_html(
'<a href="{}">编辑</a>',
reverse('cus_admin:blog_post_change', args=(obj.id,))
)
operator.short_description = '操作'
接着,需要在 urls.py 文件中进行修改,其完整代码如下:
from django.conf.urls import url
from django.contrib import admin
from .custom_site import custom_site
urlpatterns = [
url(r'^super_admin/', admin.site.urls),
url(r'^admin/', custom_site.urls)
]
这样就有两套后台地址,一套用来管理用户,另一套用来管理业务。但这两套系统都是基于一套逻辑的用户系统,只是在 URL 上进行了划分。
抽取 Admin 基类
抽象 author 基类
先抽象出一个基类 BaseownerAdmin,这个类完成两件事:一是重写 save 方法,此时需要设置对象的 owner;二是重写 get_queryset 方法,让列表页在展示文章或者分类时只能展示当前用户的数据。代码如下:
from django.contrib import admin
class BaseOwnerAdmin(admin.ModelAdmin):
"""
1. 用来自动补充文章、分类、标签、侧边栏、友链这些 Model的 owner 字段
2. 用来针对 queryset 过滤当前用户的数据
"""
exclude = ('owner', )
def get_queryset(self, request):
qs = super(BaseOwnerAdmin, self).get_queryset(request)
return qs.filter(owner=request.user)
def save_model(self, request, obj, form, change):
obj.owner = request.user
return super(BaseOwnerAdmin, self).save_model(request, obj, form, change)
把这段代码放到 base_admin.py 中,与 custom_site.py 同目录。之所以放这里是因为所有的 App 都需要用到。
有了这个基类,接下来需要做的就是改造那些需要隔离不同用户数据的管理页面,只需要让对应的 Admin 类继承这个基类即可。这里列一下完整的 blog/admin.py 代码:
from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from .adminforms import PostAdminForm
from .models import Post, Category, Tag
from typeidea.base_admin import BaseOwnerAdmin
from typeidea.custom_site import custom_site
class PostInline(admin.TabularInline):
fields = ('title', 'desc')
extra = 1 # 控制额外多几个
model = Post
@admin.register(Category, site=custom_site)
class CategoryAdmin(BaseOwnerAdmin):
inlines = [PostInline, ]
list_display = ('name', 'status', 'is_nav', 'created_time', 'post_count')
fields = ('name', 'status', 'is_nav')
def post_count(self, obj):
return obj.post_set.count()
post_count.short_description = '文章数量'
@admin.register(Tag, site=custom_site)
class TagAdmin(BaseOwnerAdmin):
list_display = ('name', 'status', 'created_time')
fields = ('name', 'status')
class CategoryOwnerFilter(admin.SimpleListFilter):
"""
自定义过滤器只展示当前用户分类
"""
title = '分类过滤器'
parameter_name = 'owner_category'
def lookups(self, request, model_admin):
return Category.objects.filter(owner=request.user).values_list('id', 'name')
def queryset(self, request, queryset):
category_id = self.value()
if category_id:
return queryset.filter(category_id=self.value())
return queryset
@admin.register(Post, site=custom_site)
class PostAdmin(admin.ModelAdmin):
form = PostAdminForm
list_display = [
'title', 'category', 'status',
'created_time', 'owner', 'operator'
]
list_display_links = []
list_filter = [CategoryOwnerFilter]
search_fields = ['title', 'category__name']
actions_on_top = True
actions_on_bottom = True
# 编辑页面
save_on_top = True
exclude = ('owner', )
# fields = (
# ('category', 'title'),
# 'desc',
# 'status',
# 'content',
# 'tag',
# )
fieldsets = (
('基础配置', {
'description': '基础配置描述',
'fields': (
('title', 'category'),
'status',
),
}),
('内容', {
'fields': (
'desc',
'content',
),
}),
('额外信息', {
'classes': ('collapse', ),
'fields': ('tag', ),
})
)
# filter_horizontal = ('tag', )
filter_vertical = ('tag', )
def operator(self, obj):
return format_html(
'<a href="{}">编辑</a>',
reverse('cus_admin:blog_post_change', args=(obj.id,))
)
operator.short_description = '操作'
# class Media:
# css = {
# 'all': ("https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css", ),
# }
# js = ('https://cdn.bootcss.com/twitter-bootstrap/3.3.7/js/bootstrap.js', )
在 admin 页面上查看操作日志
# blog/admin.py
from django.contrib.admin.models import LogEntry
@admin.register(LogEntry, site=custom_site)
class LogEntryAdmin(admin.ModelAdmin):
list_display = ['object_repr', 'object_id', 'action_flag', 'user', 'change_message']
根据需求定制 admin的更多相关文章
- django自定制Admin
如果只是在admin中简单的展示及管理模型,那么在admin.py模块中使用admin.site.register将模型注册一下就好了: from django.contrib import admi ...
- 第五章:Admin管理后台 - 1:自定制Admin
如果只是在admin中简单的展示及管理模型,那么在admin.py模块中使用admin.site.register将模型注册一下就好了: from django.contrib import admi ...
- d3.js 根据需求定制pie图饼图
参考网址: http://d3pie.org/#generator 用法: 1.在网址中,跟着步骤,设置样式和效果,最后获取pie的option格式 2.引入d3.min.js和d3pie.min.j ...
- django 定制admin
https://www.cnblogs.com/liwenzhou/p/9519321.html
- Django博客系统
零.创建项目及配置 一.编写 Model 层的代码 二.配置 admin 页面 三.根据需求定制 admin
- Django Admin定制
Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件,使用方式有: 依赖APP: django.contrib.auth django.contrib.contenttyp ...
- Django Form and Modelform Admin定义 高级查询)
Django的form表单一般具有两种功能 1. 验证输入 2.输入HTML ---------模板----------- from django import forms class BookFor ...
- django进阶-modelform&admin action
先看效果图: 登陆admin后的界面: 查看作者: 当然你也可以定制admin, 使界面更牛逼 数据库表结构: app01/models.py from django.db import models ...
- Admin管理后台
Django奉行Python的内置电池哲学.它自带了一系列在Web开发中用于解决常见问题或需求的额外的.可选工具.这些工具和插件,例如django.contrib.redirects都必须在setti ...
随机推荐
- docker HealthCheck健康检查
需求 最近遇到的问题:线上跑的一个 Node 镜像是在运行的,状态为 up ,但是访问报 502 ,重启镜像无效,重新拉了个镜像运行才恢复正常.于是想研究下如何从应用层面去检查容器的状态 为什么 do ...
- LoadRunner(2)
一.性能测试的基本概念 1.并发和在线的区别:并发的压力是一种瞬时压力,一般针对同一类型业务:在线的压力是一段时间的压力,没有并发那么集中. 规律:一般20用户并发产生的压力相当于200用户在线的压力 ...
- Jenkins构建从github上克隆时,报Host key verification failed.
首先在本地通过CMD执行git clone xxxxx时,可以成功的通过免密(SSH_KEY)克隆下来代码,但是通过Jenkins克隆时,就报如下信息: Cloning into 'GitHub'.. ...
- TCP/IP协议簇 端口 三次握手 四次挥手 11种状态集
第一章:概念介绍 1.1 VLAN 1.1.1 什么是VLAN VLAN (Virturl LAN) ,翻译成中文是:“虚拟局域网”.VLAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计 ...
- texture2dArray
https://medium.com/@calebfaith/how-to-use-texture-arrays-in-unity-a830ae04c98b http://cdn.imgtec.com ...
- nodejs解析url参数的三种方法
要解析的url:http://127.0.0.1:8090/?name=cpc&age=21 利用js字符串操作函数进行解析 const myserver = require("ht ...
- MySql链接url参数的设置
mysql JDBC URL格式如下: jdbc:mysql://[host:port],[host:port].../[database][?参数名1][=参数值1][&参数名2][=参数值 ...
- 5、docker容器数据卷: -v添加共享传递容器数据卷
1.是什么 1.docker理念 先来看看Docker的理念:* 将运用与运行的环境打包形成容器运行 ,运行可以伴随着容器,但是我们对数据的要求希望是持久化的* 容器之间希望有可能共享数据 2.保 ...
- ES集群安装
环境配置 安装openjdk(依赖) -openjdk.x86_64 安装elasticsearch yum -y install elasticsearch 配置 /etc/elasticsearc ...
- Huffman树、霍夫曼编码
Huffman树指的是带权路径长度WPL最小的二叉树 WPL=路径*权值 Huffman常用于压缩编码,正常传输ABCDEF这些字母需要3位二进制树来描述,但由于一篇文章中ABCDEF这些字母出现的概 ...