根据需求定制 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)
]

这样就有两套后台地址,一套用来管理用户,另一套用来管理业务。但这两套系统都是基于一套逻辑的用户系统,只是在 URL 上进行了划分。

猜你喜欢

转载自www.cnblogs.com/qiuxirufeng/p/11391693.html