[Python自学] Django的admin组件

一、admin使用注意事项

1.初始化

在使用admin组件之前,需要初始化数据库,django会为我们生成admin所需要的一些表。

执行:

python manage.py migrate

2.django版本问题

在使用django 3.0.2版本的过程中,发现创建superuser后无法登陆admin页面,程序会异常退出。

将版本变更为2.2.8以后,可以正常登录admin。

二、在admin中注册model

如果想使用admin来管理我们自己创建的model,则需要在admin.py中注册:

代码如下:

from django.contrib import admin

# 导入我们定义的model
from .models import Business, Application, Host

admin.site.register(Business)
admin.site.register(Application)
admin.site.register(Host)

页面效果:

点击进入其中一张表(例如Hosts),发现展示的都是object:

要想让其显示hostname,则需要的model中重写__str__()方法:

# 创建主机表
class Host(models.Model):
    nid = models.AutoField(primary_key=True)
    hostname = models.CharField(max_length=32, db_index=True)
    ip = models.GenericIPAddressField(protocol='ipv4', db_index=True, null=True)
    port = models.IntegerField()
    busi = models.ForeignKey('Business', on_delete=models.CASCADE, to_field='id')

    def __str__(self):
        return self.hostname

此时再看页面效果:

三、admin配置

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

以下是一些常用的admin功能配置。

1.配置显示字段

在第二节中的Hosts页面中,展示的每一行都是一个host对象,我们可以通过配置,使其展示需要的字段:

在admin.py中:

from django.contrib import admin

# 导入我们定义的model
from .models import Business, Application, Host


# 定义一个host的配置类,必须继承于admin.ModelAdmin类
class HostConfig(admin.ModelAdmin):
    # 定义要显示的字段
    list_display = ["hostname", "ip", "port", "busi"]


# 注册时,将HostConfig类作为参数传递给register
admin.site.register(Host, HostConfig)

这个类只针对Host做了配置。对Business和Application没有影响。

页面效果:

 可以看到,页面按我们定义的字段进行了显示。

2.配置跳转连接

在上面展示的页面效果中,只有hostname可以点击跳转。我们可以通过配置,自定义link。

from django.contrib import admin

# 导入我们定义的model
from .models import Business, Application, Host


# 定义一个host的配置类,必须继承于admin.ModelAdmin类
class HostConfig(admin.ModelAdmin):
    # 定义要显示的字段
    list_display = ["hostname", "ip", "port", "busi"]
    list_display_links = ["hostname", "ip"]

# 注册时,将HostConfig类作为参数传递给register
admin.site.register(Host, HostConfig)

这样,字段"hostname"和"IP"都可以点击跳转,跳转到的页面时同一个页面(即host的详情页面)。

页面效果:

3.添加自定义字段

from django.contrib import admin

# 导入我们定义的model
from .models import Business, Application, Host
from django.utils.safestring import mark_safe


# 定义一个host的配置类,必须继承于admin.ModelAdmin类
class HostConfig(admin.ModelAdmin):

    # 定义一个函数来返回"删除"按钮
    def deletes(self):
        return mark_safe("<a href=''>删除</a>")

    # 定义要显示的字段
    list_display = ["hostname", "ip", "port", "busi", deletes]
    list_display_links = ["hostname", "ip"]


# 注册时,将HostConfig类作为参数传递给register
admin.site.register(Host, HostConfig)

在<a>标签的href中写入删除的URL就可以实现删除操作了。

页面效果:

4.添加过滤字段

# 定义一个host的配置类,必须继承于admin.ModelAdmin类
class HostConfig(admin.ModelAdmin):

    # 定义一个函数来返回"删除"按钮
    def deletes(self):
        return mark_safe("<a href=''>删除</a>")

    # 定义要显示的字段
    list_display = ["hostname", "ip", "port", "busi", deletes]
    list_display_links = ["hostname", "ip"]
    list_filter = ["port","busi"]  # 设置过滤字段"port"和"busi"

此时,页面效果:

在页面右侧出现过滤器,点击其中的值就可以进行过滤。

5.模糊查询功能

# 定义一个host的配置类,必须继承于admin.ModelAdmin类
class HostConfig(admin.ModelAdmin):

    # 定义一个函数来返回"删除"按钮
    def deletes(self):
        return mark_safe("<a href=''>删除</a>")

    # 定义要显示的字段
    list_display = ["hostname", "ip", "port", "busi", deletes]
    list_display_links = ["hostname", "ip"]
    list_filter = ["port","busi"]  # 设置过滤字段"port"和"busi"
    search_fields = ['hostname','ip']  # 添加一个模糊查询框,搜索条件会在hostname和ip字段中模糊匹配

页面效果:

6.批量处理

在默认页面中,可以看到如下的一个Action框:

这个就是批量操作的功能,但其中只有默认的一个批量操作,即批量删除。当我们选中下面记录中的一条或多条时,就可以使用批量删除了。

如何自定义批量操作:

# 定义一个host的配置类,必须继承于admin.ModelAdmin类
class HostConfig(admin.ModelAdmin):

    # 定义一个函数来返回"删除"按钮
    def deletes(self):
        return mark_safe("<a href=''>删除</a>")

    # 定义要显示的字段
    list_display = ["hostname", "ip", "port", "busi", deletes]
    list_display_links = ["hostname", "ip"]
    list_filter = ["port", "busi"]  # 设置过滤字段"port"和"busi"
    search_fields = ['hostname']  # 添加一个模糊查询框

    # 定义一个批量操作的方法
    def patch_proc(self, request, queryset):
        queryset.update(port=80)

    # 定义一个短描述(显示在页面的Action列表中)
    patch_proc.short_description = "批量修改port为80"
    # 加入Action列表
    actions = [patch_proc, ]

页面效果:

此时,我们选中一条或多条记录,然后使用自定义的批量操作,就可以将选中记录的port都修改为80。

四、Django中admin的实现流程

1.Django如何启动admin

当Django启动的时候,会加载settings中 INSTALLED_APPS 列表中的所有模块,包括 django.contrib.admin 模块。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

我们进入django.contrib.admin的源码:

# ACTION_CHECKBOX_NAME is unused, but should stay since its import from here
# has been referenced in documentation.
from django.contrib.admin.decorators import register
from django.contrib.admin.filters import (
    AllValuesFieldListFilter, BooleanFieldListFilter, ChoicesFieldListFilter,
    DateFieldListFilter, FieldListFilter, ListFilter, RelatedFieldListFilter,
    RelatedOnlyFieldListFilter, SimpleListFilter,
)
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
from django.contrib.admin.options import (
    HORIZONTAL, VERTICAL, ModelAdmin, StackedInline, TabularInline,
)
from django.contrib.admin.sites import AdminSite, site
from django.utils.module_loading import autodiscover_modules

__all__ = [
    "register", "ACTION_CHECKBOX_NAME", "ModelAdmin", "HORIZONTAL", "VERTICAL",
    "StackedInline", "TabularInline", "AdminSite", "site", "ListFilter",
    "SimpleListFilter", "FieldListFilter", "BooleanFieldListFilter",
    "RelatedFieldListFilter", "ChoicesFieldListFilter", "DateFieldListFilter",
    "AllValuesFieldListFilter", "RelatedOnlyFieldListFilter", "autodiscover",
]


def autodiscover():
    autodiscover_modules('admin', register_to=site)


default_app_config = 'django.contrib.admin.apps.AdminConfig'
View Code

在 autodiscover()方法中,自动加载所有app中的admin.py模块。所以,当我们添加了app之后,将app加入了INSTALLED_APPS 列表,在Django启动的时候就会加载相应的admin.py模块,并在加载的同时执行其中的所有代码。

# 在app mgmt的admin.py中打印
print("加载/mgmt/admin.py")

启动django:

"D:\Apps\PyCharm 2019.3\bin\runnerw64.exe" D:\pycharm_workspace\secondsite\venv\Scripts\python.exe D:/pycharm_workspace/secondsite/manage.py runserver 8000
加载/mgmt/admin.py
加载/mgmt/admin.py
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
January 21, 2020 - 16:45:28
Django version 2.2.8, using settings 'secondsite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

这里打印了两次,第一次应该是在内部debug时打印的。

2.admin的model注册

注册时使用的admin.site是一个单例对象。

我们调用 admin.site.register()来注册一个model类的时候,其实就是AdminSite类的_registry字典中添加一个键值对。

AdminSite类的源码:

class AdminSite:
    ...
    ...

    def __init__(self, name='admin'):
        self._registry = {}  # model_class class -> admin_class instance
        self.name = name
        self._actions = {'delete_selected': actions.delete_selected}
        self._global_actions = self._actions.copy()
        all_sites.add(self)
    ...
    ...

    def register(self, model_or_iterable, admin_class=None, **options):
        """
        Register the given model(s) with the given admin class.

        The model(s) should be Model classes, not instances.

        If an admin class isn't given, use ModelAdmin (the default admin
        options). If keyword arguments are given -- e.g., list_display --
        apply them as options to the admin class.

        If a model is already registered, raise AlreadyRegistered.

        If a model is abstract, raise ImproperlyConfigured.
        """
        admin_class = admin_class or ModelAdmin
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            if model._meta.abstract:
                raise ImproperlyConfigured(
                    'The model %s is abstract, so it cannot be registered with admin.' % model.__name__
                )

            if model in self._registry:
                raise AlreadyRegistered('The model %s is already registered' % model.__name__)

            # Ignore the registration if the model has been
            # swapped out.
            if not model._meta.swapped:
                # If we got **options then dynamically construct a subclass of
                # admin_class with those **options.
                if options:
                    # For reasons I don't quite understand, without a __module__
                    # the created class appears to "live" in the wrong place,
                    # which causes issues later on.
                    options['__module__'] = __name__
                    admin_class = type("%sAdmin" % model.__name__, (admin_class,), options)

                # Instantiate the admin class to save in the registry
                self._registry[model] = admin_class(model, self)
View Code

在AdminSite类的register方法中:

self._registry[model] = admin_class(model, self)

就是向_registry字典中添加一个键为model的键值对,键model就是我们注册的model类。而值就是admin_class()产生的对象,admin_class是一个类:

admin_class在我们没有指定自定义类的情况下,是admin.ModelAdmin类

也就是说默认情况下,_registry中保存的键值对是 {Host类,ModelAdmin的对象}。

后面这个ModelAdmin的对象,实际上就是控制页面样式的类。

我们在第三节中已经自定义过这种类,需要继承于admin.ModelAdmin。当我们在admin.site.register()的时候传入了自定义的类,则使用我们定义的类作为样式控制类。

参照《第三节》中的HostConfig类

 

打印admin.site._registry,查看一共注册了多少个model:

# admin.py

print(admin.site._registry)

打印结果:

{<class 'django.contrib.auth.models.Group'>: <django.contrib.auth.admin.GroupAdmin object at 0x000001FD57F96048>,
<class 'django.contrib.auth.models.User'>: <django.contrib.auth.admin.UserAdmin object at 0x000001FD57FBEFD0>, 
<class 'mgmt.models.Host'>: <mgmt.admin.HostConfig object at 0x000001FD57FDA320>, 
<class 'mgmt.models.Business'>: <django.contrib.admin.options.ModelAdmin object at 0x000001FD57FDA358>, 
<class 'mgmt.models.Application'>: <django.contrib.admin.options.ModelAdmin object at 0x000001FD57FDA390>}

可以看到,已注册的model有admin自己的User和Group,以及我们自己定义的Host、Business、Application。

我们自己定义的model对应的默认样式配置类是ModelAdmin,而在Host中我们传入了HostConfig,admin组件自带的model的样式配置类是  <model名+Admin>,例如UserAdmin以及GroupAdmin。

 

 

66

posted @ 2020-01-21 14:49  风间悠香  阅读(413)  评论(0编辑  收藏  举报