Django之admin后台

目录

  1. 简介

  2. 简单使用与注册

  3. 常用字段介绍

  4. 数据展示问题

  5. 编辑页面配置

  6. 自定义静态资源引入

  7. 自定义Form

  8. 在同一页面编辑关联数据

  9. 定制site

  10. 抽取admin基类

  11. simpleui的使用

  小技巧

    1. 自定义显示类名与字段名

    2. 默认创建人

    3. 在列表页每条数据增加操作(删除, 修改)

    4. 统计分类中的物品数量

    5. 增加日志管理

    6. 标题与站点名称的修改

    7. 左边目录主菜单名称设置

    

    

 

 

1. 简介

  admin是Django的杀手锏. 对于内容管理系统来说, 当你有了数据表, 有了Model, 就相当于自动有了一套管理后台, 还包括权限控制, 这简直就是太爽的操作了, 当然, 这得益于Django的诞生环境 -- '他最初用来开发新闻内容相关网站'. 从框架本身来讲, 这完全依托于Django的Model层

 

2. 简单使用与注册

  要使用admin的话要操控Django的admin文件, 要将要将Model展示出来就要将它注册一下, 注册有两种方式, 分别是:

  2.1 装饰器方式注册

from django.contrib import admin

# Register your models here.
from .models import Tag, Category


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name', 'status', 'is_nav', 'created_time')
    fields = ('name', 'status', 'is_nav')

  2.2 写完admin类之后注册

  如果不写相关的admin类它默认将所需要的数据全部展示, 写了会按类中要求展示的数据展示

from django.contrib import admin

# Register your models here.
from .models import Tag, Category


class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name', 'status', 'is_nav', 'created_time')
    fields = ('name', 'status', 'is_nav')


admin.site.register(Category, CategoryAdmin)

 

3. 常用字段介绍

    list_display
    用来配置列表页面展示哪些字段

list_display_link
    用来配置哪些字段可以作为连接, 点击它们可以进入编辑页面

list_filter
    配置页面过滤器, 需要通过哪些字段来过滤列表页

search_fields
    配置搜索字段

actions_on_top
    动作相关的配置, 是否展示在顶部

actions_on_bottom
    动作相关的配置, 是否展示在底部

save_on_top
    保存, 编辑, 编辑并新建按钮是否在顶部展示    

 

4. 数据展示问题

  当前的登录用户却可以看到其他用户的数据, 这是权限问题, 当前用户应该只能看到自己的数据才对

  1. 过滤器展示所有数据问题(自定义过滤器)

  通过继承Django admin提供的SimpleListFilter 类来实现自定义过滤器, 之后只需要把自定义过滤器配置到ModelAdmin中即可

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

  SimpleListFilter类提供了两个属性和两个方法来供我们重写. 分别是:

    title
    用于展示标题

parameter_name
    查询是URL参数的名字, 比如查询分类 id 为1的内容时, URL后面的Query部分是?owner_category=1

lookups
    返回要展示的内容和查询的id

queryset
    根据URL Query的内容返回列表页数据. 比如如果URL最后的Query是?owner_category=1, 那么这里拿到的self.value()就是1,
    此时就会根据1来过滤QuerySet. 这里的QuerySet是列表页所有展示数据的集合, 即post的数据集

  编写完之后, 只需要在需要有过滤的页面的AdminModel中加上:

list_filter = [CategoryOwnerFilter]

  2. 当前用户展示所有数据问题(自定义列表页)

   我们看源码可以发现, ModelAdmin中通过get_queryset获取数据并返回, 那么我们只要重写get_queryset方法就可以了 

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    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 = '文章数量'

    def get_queryset(self, request):
        qs = super(CategoryAdmin, self).get_queryset(request)
        return qs.filter(owner=request.user)

    def save_model(self, request, obj, form, change):
        obj.owner = request.user
        return super(CategoryAdmin, self).save_model(request, obj, form, change)

  3. 添加或修改数据时下拉框展示所有数据问题

  使用formfield_for_foreignkey函数, 源码中如果queryset不存在, 便会获取该字段所有的数据, 所以我们可以给queryset设置数据

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = [
        'title', 'category', 'status',
        'created_time'
    ]

    fields = (
        ('category', 'title'),
        'desc',
        'status',
        'content',
        'tag'
    )


    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'category':
            kwargs['queryset'] = Category.objects.filter(owner=request.user)
        if db_field.name == 'tag':
            kwargs['queryset'] = Tag.objects.filter(owner=request.user)
        return super(PostAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

    def get_queryset(self, request):
        qs = super(PostAdmin, self).get_queryset(request)
        return qs.filter(owner=request.user)

    def save_model(self, request, obj, form, change):
        obj.owner = request.user
        return super(PostAdmin, self).save_model(request, obj, form, change)

 

5. 编辑页面配置

  1. 按钮位置

  使用save_on_top用来控制是否在页面顶部展示'保存', '保存并继续', '保存并新增另一个'按钮

  2. 字段是否展示

  使用fields或fieldset来配置展示, exclude来配置不展示

# fields and exclude
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    fields = (
        ('category', 'title'),
        'desc',
        'status',
        'content',
        'tag'
    )
    exclude = ('owner',)

  fields有两个作用, 一个市现在展示的字段, 另一个就是配置展示字段的顺序

  # fieldsets
fieldsets = ( ('基础配置', { 'description': '基础配置描述', 'fields': ( ('title', 'category'), 'status' ) }), ('内容', { 'fields': ( 'desc', 'content' ) }), ('额外信息', { 'classes': ('collapse',), 'fields': ('tag', ) }) )

  fieldsets用来控制布局, 要求的格式是有两个元素的tuple的list

    fieldsets = (
        (名称, {内容}),
        (名称, {内容}),
    )

   第一个元素是当前板块的名称, 第二个元素是当前板块的描述,字段和样式配置. 也就是说第一个元素是string, 第二个元素是dict, 而dict的key可以是'fields'、 'description'和'classes'

   fields的配置效果跟上面一样, 可以控制展示哪些元素, 也可以给元素排序并组合元素位置

  classes的作用就是给要配置的板块加上一些CSS属性, Django admin默认支持collapse和wide. 当然你也可以写其他属性, 然后自己来处理样式

  3. 控制多对多字段展示的配置

  filter_horizontal  多对多字段横向展示

  filter_verical       多对多字段纵向展示

 

6. 自定义静态资源引入

  页面是Django帮我们生成的, 但我们可以自定义元素的class, 那么怎么来定义CSS呢?

  Django给我们提供了接口, 在页面加载时, Django会把定义的CSS加载到页面

  我们通过自定义Media类来往页面上增加想要添加的Javascript和CSS资源

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
   class Media:
        css = {
            'all': ('https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css',)
        }
        js = ('https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.4.1/js/bootstrap.bundle.js', )

 

7. 自定义Form

  上述的所有配置都是基于ModelAdmin的, 如果有更多的定制需求, 就需要使用到ModelForms. 我们目前看到的以及用到的admin页面, 就是通过这些组件生成的, ModelForm就是其中一环. 只是用到的是Django admin默认的Form而已.

  用法与Form组件一样, 但在这里我们命名为adminforms.py, 这只是为了跟前台针对用户输入进行处理的Form区分开来.

  例:

adminforms.py文件
from
django import forms class PostAdminForm(forms.ModelForm): desc = forms.CharField(widget=forms.Textarea, label='摘要', required=False)
admin.py文件
from django.contrib import admin
@admin.register(Post)
class PostAdmin(admin.ModelAdmin): '''文章后台配置''' form = PostAdminForm
  ......

 

8. 在同一页面编辑关联数据

  需求: 添加颜色属性, 并在同一页面添加属性中的颜色

class ColorInline(admin.TabularInline):
    fields = ('name', 'status')
    extra = 1
    model = Category
  
    def get_queryset(self, request):  # 当现有的属性修改时会显示所有用户的bug, 加上这个只显示当前用户
        queryset = super(ColorInline, self).get_queryset(request)
        return queryset.filter(owner=request.user)


class CategoryAdmin(admin.ModelAdmin):
    inlines = [ColorInline, ]
   .......

 

9. 定制site

  大部分情况下, 只需要一个site就够了, 一个site对应一个站点, 这就像上面所有的操作最终都反应在一个后台. 当然, 我们也可以通过定制site来实现一个系统对外提供多套admin后台的逻辑.

  一个URL后面对应的就是一个site. 看一下urls.py文件中的这段代码: url(r'^admin/', admin.site.urls) 这就对应了一个site.

  上面的admin.site就是Django提供的一个admin.site模块, 这里面的site其实是django.contrib.admin.AdminSite的一个实例.

  因此我们可以继承AdminSite来定义自己的site

custom_site.py文件   
from
django.contrib.admin import AdminSite class CustomSite(AdminSite): site_header = 'Typeidea' site_title = 'Typeidea管理后台' index_title = '首页' custom_site = CustomSite(name='cus_admin')

  使用:

from django.conf.urls import url
from django.contrib import admin

from .custom_site import custom_site

urlpatterns = [
    url(r'^admin/', custom_site.urls),  # 添加url
    url(r'^super_admin/', admin.site.urls),
]
@admin.register(Post, site=custom_site)  # 注册时标明site
class PostAdmin(admin.ModelAdmin):
    '''文章后台配置'''
    form = PostAdminForm

    list_display = [
        'title', 'category', 'status',
        'created_time', 'operator'
    ]
    list_display_links = []
    def operator(self, obj):
        return format_html(
            '<a href="{}">编辑</a>  '
            '<a href="{}">删除</a>',
            reverse('cus_admin:blog_post_change', args=(obj.id,)),  # 跳转是要注意更改跳转的site
            reverse('cus_admin:blog_post_delete', args=(obj.id,))
        )

    operator.short_description = '操作'

 

 10. 抽取admin基类

  在开发时, 我们时刻保持这样的理念-- 尽量降低后期的维护成本. 自然是降低修改代码时的负担, 降低我们在修改一个需求是要修改的代码量, 让后来的程序员在修改代码时不会被之前凌乱的代码'绊倒'

  先来抽取一个BaseOwnerAdmin, 这个类帮我们完成两件事: 意识重写save方法, 此时需要设置对象的owner; 二十重写get_queryset方法, 让列表页在展示文章或者文章分类时只能展示当前用户的数据.

# BaseOwnerAdmin.py 文件  
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)

  使用:

@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 = '文章数量'

 

11. simpleui的使用

  🚀simpleui 是django admin的一个主题 是一个基于element-ui+vue开发,重写和优化90%以上的页面。 与suit是同类产品。我们是一个更符合国人审美和使用习惯的一个主题。

  https://simpleui.88cto.com/docs/simpleui/QUICK.html

 

小技巧

1. 自定义显示类名与字段名

class Category(models.Model):
    STATUS_NORMAL = 1
    STATUS_DELETE = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, '正常'),
        (STATUS_DELETE, '删除'),
    )

    name = models.CharField(max_length=50, verbose_name='名称')  # 参数verbose_name 定义字段的名字
    status = models.PositiveIntegerField(choices=STATUS_ITEMS, verbose_name='状态', default=STATUS_NORMAL)
    is_nav = models.BooleanField(default=False, verbose_name='是否为导航')
    owner = models.ForeignKey(User, verbose_name='作者')
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')

    def __str__(self):
        return '<Category: {}>'.format(self.name)

    class Meta:
        verbose_name = verbose_name_plural = '分类'  # Meta中的verbose_name定义展示的表名, verbose_name_plural默认给定义展示的表名后加个s, 所以一般同时定义

 

2. 默认创建人

  如果将创建人字段加上, 那么岂不是创建人可以随意更改成别的创建人, 所以我们要把创建人设定问当前的登录用户, 这时候我们就要重写ModelAdmin的save_model方法, 其作用是保存数据到数据库中

from django.contrib import admin

# Register your models here.
from .models import Tag, Category


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name', 'status', 'is_nav', 'created_time')
    fields = ('name', 'status', 'is_nav')

    def save_model(self, request, obj, form, change):
        obj.owner = request.user
        return super(CategoryAdmin, self).save_model(request, obj, form, change)

 

3. 在列表页每条数据增加操作(删除, 修改)

  根据需求, 要在每条数据后增加一个操作列, 分别用来对该数据的修改与删除操作, 我们要使用admin的自定义函数与django.utils.html的format_html函数返回数据

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = [
        'title', 'category', 'status',
        'created_time', 'operator'  # operator 为自定义函数
    ]
    list_display_links = []

    list_filter = ['category', ]
    search_fields = ['title', 'category_name']

    actions_on_top = True
    actions_on_bottom = False

    # 编辑页面
    save_on_top = True

    fields = (
        ('category', 'title'),
        'desc',
        'status',
        'content',
        'tag'
    )

    def operator(self, obj):  # 自定义函数
        return format_html(
            '<a href="{}">编辑</a> | <a href="{}">删除</a>',
            reverse('admin:blog_post_change', args=(obj.id,)),
            reverse('admin:blog_post_delete', args=(obj.id,))
        )
    operator.short_description = '操作'

    def save_model(self, request, obj, form, change):
        obj.owner = request.user
        return super(PostAdmin, self).save_model(request, obj, form, change)

 

4. 统计分类中的物品数量

  使用自定义函数

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    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 = '文章数量'

    def save_model(self, request, obj, form, change):
        obj.owner = request.user
        return super(CategoryAdmin, self).save_model(request, obj, form, change)

 

5.  增加日志管理

  简单配置admin, 在config/admin中配置以下代码:

from django.contrib import admin
from django.contrib.admin.models import LogEntry

@admin.register(LogEntry)
class LogEntryAdmin(admin.ModelAdmin):
    list_display = ('object_repr', 'user', 'content_type',
                    'change_message', 'action_time')

 

6. 标题与站点名称的修改

#修改网页title和站点header。
admin.site.site_title = "MyDjango后台管理"
admin.site.site_header = "MyDjango"

 

7. 左边目录主菜单名称设置

  在apps.py中设置verbose_name设置名称, 但是还需要在__init__.py文件中设中default_app_config

#  apps.py文件
from
django.apps import AppConfig class BlogConfig(AppConfig): name = 'blog' verbose_name = '文章管理'
#  __init__.py文件
default_app_config = 'blog.apps.BlogConfig'

 

posted @ 2019-06-23 20:52  yyfgrd  阅读(573)  评论(0)    收藏  举报