Django2.2

Django内置了一个强大的组件叫Admin,提供给网站管理员快速开发运营后台的管理站点。而xadmin则是第三方组件,它对admin进行了扩展。
我们分别来看。
before
这里我新建一个Django项目叫做admin_demo,有一个应用叫做student,在student应用中,创建模型类:

from django.db import models

# Create your models here.


class Classes(models.Model):
    """ 班级表 """
    title = models.CharField(max_length=32, verbose_name='班级名称')

    def __str__(self):
        return self.title

class Students(models.Model):
    """ 学生表 """
    username = models.CharField(max_length=128, default=None, null=True, verbose_name='学生姓名')
    mobile = models.CharField(max_length=32, null=True, unique=True, verbose_name='手机号', help_text='手机号')
    wxchat = models.CharField(max_length=100, null=True, unique=True, verbose_name='微信', help_text='微信')
    qq_number = models.CharField(max_length=16, null=True, unique=True, verbose_name='QQ', help_text='QQ')
    nickname = models.CharField(max_length=128, default=None, null=True, verbose_name='昵称')
    age = models.SmallIntegerField(verbose_name='年龄')
    email = models.CharField(max_length=128, default=None, null=True, verbose_name='邮箱')
    en_date = models.DateField(verbose_name='入学日期')
    addr = models.CharField(max_length=32, default=None, null=True, verbose_name='地址')
    to_classes = models.ForeignKey('Classes', default=1, verbose_name='所属班级', on_delete=models.CASCADE)

    def __str__(self):
        return self.username

在项目根目录下创建test.py文件,进行批量生成一些数据:

import os
import django
import random
import faker  # pip install faker

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "admin_demo.settings")  # MB:项目名称
django.setup()

from student import models

fk = faker.Faker(locale='zh_CN')


def create():
    # models.Classes.objects.bulk_create((models.Classes(i, random.randrange(100, 500)) for i in range(10)))
    gen = (
        models.Students(
            i,
            fk.name(),
            fk.phone_number(),
            fk.phone_number(),
            fk.phone_number(),
            fk.user_name(),
            random.randrange(15, 20),
            fk.email(),
            fk.date(),
            fk.city_name(),
            random.randrange(0, 9)
        ) for i in range(1, 1001)
    )
    models.Students.objects.bulk_create(gen)


if __name__ == '__main__':
    create()

现在,项目有了,数据也有了,就可以开搞。

admin

https://docs.djangoproject.com/zh-hans/2.2/ref/contrib/admin/

想要使用admin,就先要创建一个超级管理员:

(django2.2) D:\tmp\admin_demo>python manage.py createsuperuser   # 创建管理员命令
Username (leave blank to use 'anthony'): root                    # 管理员账号
Email address:                                                   # 我直接回车,省略输入邮箱步骤
Password:                                                        # 123
Password (again):                                                # 123
This password is too short. It must contain at least 8 characters. # 提示密码太短了,最少需要8个字符,这里忽略
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: y      # 绕过密码验证,直接创建
Superuser created successfully.

那,现在我们就可以访问admin站点了。
admin站点的路由,在总路由文件中,已经提前帮我们写好了:

# admin_demo/urls.py

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

所以,直接运行项目,然后使用刚才创建的超级用户访问admin路径就行了,即http://127.0.0.1:8000/admin,登录成功,后台站点默认长这样。
Django - Admin&XAdmin

可以看到,全部是英文,想要中文,需要在settings中配置:

# 修改使用中文界面
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-Hans'

# 修改时区
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

OK了:
Django - Admin&XAdmin

列表页配置

关于列表页配置,主要针对项目中各子应用里面的models.py里面的模型,根据这些模型自动生成后台运营站点的管理功能。
在我们的student应用下的admin.py中,进行用户模型的注册:

from django.contrib import admin
from student import models

class StudentModelAdmin(admin.ModelAdmin):  # 必须继承 admin.ModelAdmin
    """ 用户模型管理类,用于在admin后台对Students模型类进行扩展 """
    pass

admin.site.register(models.Students, StudentModelAdmin)  # 必须进行注册


@admin.register(models.Classes)  # 或者使用装饰器进行注册
class ClassesModelAdmin(admin.ModelAdmin):
    pass

效果如下:
Django - Admin&XAdmin

但显示的title仍然是英文,要换成中文应该怎么办?

# admin_demo/student/__init__.py
default_app_config = "student.apps.StudentConfig"  # 这么配置

# admin_demo/student/apps.py
from django.apps import AppConfig

class StudentConfig(AppConfig):
    name = 'student'
    verbose_name = "学生信息管理"  # 添加这一行

现在就好了:
Django - Admin&XAdmin

你可以点击Student或者班级表进入其列表页,这里以学生表为例:
Django - Admin&XAdmin
列表页默认显示的是每个学生记录的模型对象,现在非常的单调。

基础配置

我们在admin.py中进行其他配置:

from django.contrib import admin
from student import models


class StudentModelAdmin(admin.ModelAdmin):  # 必须继承 admin.ModelAdmin
    """ 用户模型管理类,用于在admin后台对Students模型类进行扩展 """
    list_per_page = 20  # 列表页每页展示数据的条数控制

    # 在列表页展示哪些字段
    list_display = ['id', 'username', 'age', 'email', 'mobile', 'nickname', 'to_classes']

    # 设置点击指定字段跳转到编辑页面
    list_display_links = ['id', 'username', 'age']

    # 默认是以id进行排序,你也可以通过ordering,设置指定字段排序
    # ordering = ['-age']  # ['age']: 升序; ['-age']: 降序
    ordering = ['-age', 'id']  # 年龄相同的,就按照id升序排序

    # actions_on_bottom = True  # 下方操作栏是否显示,False表示隐藏
    # actions_on_top = True     # 上方操作栏是否显示,False表示隐藏

    list_filter = ['to_classes', 'age']  # 过滤器,按指定字段进行筛选过滤, 可以写多个过滤器,在右侧展示
    date_hierarchy = "en_date"  # 按照入学时间不同,可以进行过滤,在上方展示,这个筛选必须是日期时间相关的字段

    search_fields = ['username', 'nickname']  # 指定字段搜索
    preserve_filters = True  # 默认为False,在详情页面做了操作,再返回列表页后,是否保留搜索条件

    # 允许在列表页直接编辑哪些字段
    # 注意,要编辑的字段必须在list_display中,但是也必须不在list_display_links中
    # list_editable = ['email', 'mobile']


admin.site.register(models.Students, StudentModelAdmin)  # 必须进行注册

inlines

在我们的当前示例项目中,添加班级时,可以通过设置inlines字段同时为该班级添加一些学生,因为学生表ForeignKey的班级表。
before:
Django - Admin&XAdmin

after:
Django - Admin&XAdmin

from django.contrib import admin
from student import models

@admin.register(models.Classes)
class ClassesModelAdmin(admin.ModelAdmin):
    class StudentInline(admin.StackedInline):  # 必须这么继承,当然这个类也可以写在ClassesModelAdmin外面
        """
        这两个就样式不同而已
            admin.StackedInline: 竖着
            admin.TabularInline: 横着
        """
        extra = 1  # 默认一次性添加几条记录
        model = models.Students

    inlines = [StudentInline]

action

action即在列表页,可操作的一些功能,它默认实现了一个删除操作:

Django - Admin&XAdmin

我们也可以定制实现,来看示例:

from django.contrib import admin
from student import models

class StudentModelAdmin(admin.ModelAdmin):  # 必须继承 admin.ModelAdmin
    def my_edit(self, request, queryset):
        """ 将选中的记录,年龄设置为10 """
        # print(request.POST.getlist("_selected_action"))  # ['5', '12']
        pk_list = request.POST.getlist("_selected_action")
        models.Students.objects.filter(pk__in=pk_list).update(age=10)

    my_edit.short_description = "自定义编辑功能"
    actions = [my_edit]

admin.site.register(models.Students, StudentModelAdmin)  # 必须进行注册

效果:
Django - Admin&XAdmin

定制HTML模板

默认的,admin后台使用的是自己的相关模板进行渲染数据,如果你有兴趣的话,可以去这个路径查看:解释器下的Lib\site-packages\django\contrib\admin\templates\admin中。
我们也可以选择使用自己定制的模板,通过如下几个参数来制定相关页面使用我们自己定制的模板:

from django.contrib import admin
from student import models

@admin.register(models.Classes)
class ClassesModelAdmin(admin.ModelAdmin):

    add_form_template = ['add_form.html']  # 添加页面
    change_form_template = ['change_form.html']  # 修改页面
    change_list_template = ['change_list.html']  # 列表页
    delete_confirmation_template = ['delete_confirmation.html']  # 删除时确认页面
    delete_selected_confirmation_template = ['delete_selected_confirmation.html']  # 选择操作中的删除确认页面
    object_history_template = ['object_history.html']  # 操作中删除确认页面中的对象记录页面

上面中括号中的HTML文件,直接在Django的templates中实现即可。

编辑页配置

在列表页点击某一条记录,跳转到对应的编辑页面。
我们在编辑页面也可以做些定制配置。

全局配置

xadmin

https://github.com/sshwsfc/xadmin
https://xadmin.readthedocs.io/en/latest/index.html

xadmin是Django的第三方扩展,使Django的admin站点使用更方便。
xadmin在很多地方都跟admin的配置一样,非常好理解。

install

不同的Django版本安装命令稍有不同,这里以Django2.2版本为例,只需要终端执行:

pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2

安装完后,我们需要在配置文件中进行配置:

# 注册这三个应用
INSTALLED_APPS = [
    'xadmin',
    'crispy_forms',
    'reversion'
]

# 修改使用中文界面
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-Hans'

# 修改时区
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

xadmin有建立自己的数据库模型类,所以还需要重新执行数据库迁移命令:

python manage.py makemigrations
python manage.py migrate

如果你在执行数据库迁移命令时报了错:

ImportError: cannot import name 'SKIP_ADMIN_LOG'
(django2.2) D:\tmp\admin_demo>python manage.py makemigrations
Traceback (most recent call last):
  File "manage.py", line 21, in <module>
    main()
  File "manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from
_command_line
    utility.execute()
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\django\core\management\__init__.py", line 357, in execute
    django.setup()
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\django\apps\registry.py", line 122, in populate
    app_config.ready()
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\apps.py", line 14, in ready
    self.module.autodiscover()
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\__init__.py", line 49, in autodiscover
    register_builtin_plugins(site)
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\plugins\__init__.py", line 41, in register_builtin_plug
ins
    [import_module('xadmin.plugins.%s' % plugin) for plugin in PLUGINS if plugin not in exclude_plugins]
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\plugins\__init__.py", line 41, in <listcomp>
    [import_module('xadmin.plugins.%s' % plugin) for plugin in PLUGINS if plugin not in exclude_plugins]
  File "c:\python36\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\plugins\importexport.py", line 48, in <module>
    from import_export.admin import DEFAULT_FORMATS, SKIP_ADMIN_LOG, TMP_STORAGE_CLASS
ImportError: cannot import name 'SKIP_ADMIN_LOG'

请参考下面的解决方案:点我

解决之后,继续往下走。

然后在总路由中配置:

# /admin_demo/admin_demo/urls.py

# --------- xadmin相关配置 ---------------
import xadmin

xadmin.autodiscover()
from xadmin.plugins import xversion

xversion.register_models()

# --------- 以上 ---------------


from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('xadmin/', xadmin.site.urls),  # 注册xadmin路由
]

完事之后,就可以使用超级管理员账号和密码访问了,地址:http://127.0.0.1:8000/xadmin/,效果如下:
Django - Admin&XAdmin

接下来,就可以进行一系列的配置了。

adminx

xadmin为了区分Django的admin,所以,相关配置就不在admin.py中写了,而是在相应的app下,新建一个adminx.py文件,在这个文件中写配置;另外,xadmin的站点管理类也不在继承admin.ModelAdmin,直接继承object对象即可。

全局配置

我们可以在adminx.py中,配置站点的全局的一些配置:

import xadmin
from xadmin import views


class BaseSettings(object):
    """ 站点的基本配置 """
    # 开启主题切换功能
    enable_themes = True
    use_bootswatch = True

class GlobalSettings(object):
    """ 站点的全局配置 """
    site_title = "学生管理系统"  # 设置站点标题
    site_footer = "底部信息栏"
    menu_style = "accordion"  # 设置菜单栏折叠


xadmin.site.register(views.BaseAdminView, BaseSettings)
xadmin.site.register(views.CommAdminView, GlobalSettings)

基本效果如下:

Django - Admin&XAdmin

可能的报错

ImportError: cannot import name 'SKIP_ADMIN_LOG'

Django2.2 + xadmin2.0.1

当下载完xadmin,然后要执行数据库迁移命令时,发现报错:

ImportError: cannot import name 'SKIP_ADMIN_LOG'
(django2.2) D:\tmp\admin_demo>python manage.py makemigrations
Traceback (most recent call last):
  File "manage.py", line 21, in <module>
    main()
  File "manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from
_command_line
    utility.execute()
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\django\core\management\__init__.py", line 357, in execute
    django.setup()
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\django\apps\registry.py", line 122, in populate
    app_config.ready()
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\apps.py", line 14, in ready
    self.module.autodiscover()
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\__init__.py", line 49, in autodiscover
    register_builtin_plugins(site)
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\plugins\__init__.py", line 41, in register_builtin_plug
ins
    [import_module('xadmin.plugins.%s' % plugin) for plugin in PLUGINS if plugin not in exclude_plugins]
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\plugins\__init__.py", line 41, in <listcomp>
    [import_module('xadmin.plugins.%s' % plugin) for plugin in PLUGINS if plugin not in exclude_plugins]
  File "c:\python36\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "D:\mydata\demo\all_env\django2.2\lib\site-packages\xadmin\plugins\importexport.py", line 48, in <module>
    from import_export.admin import DEFAULT_FORMATS, SKIP_ADMIN_LOG, TMP_STORAGE_CLASS
ImportError: cannot import name 'SKIP_ADMIN_LOG'

解决方案是,打开对应的报错文件,如果是PyCharm,可以直接点击链接,找到源码文件,你也可以手动去找,它在你解释器的\Lib\site-packages\xadmin\plugins\importexport.py中,修改相应的源码:

# 第一处,大约48行左右
# from import_export.admin import DEFAULT_FORMATS, SKIP_ADMIN_LOG, TMP_STORAGE_CLASS  # 注释掉原来的报错代码
from import_export.admin import DEFAULT_FORMATS, ImportMixin, ImportExportMixinBase   # 修改后的代码

# 下拉,大约90行左右,找到ImportBaseView类,找到类中get_skip_admin_log和get_tmp_storage_class方法,分别修改
class ImportBaseView(ModelAdminView):

    def get_skip_admin_log(self):
        if self.skip_admin_log is None:
            # 第二处
            # return SKIP_ADMIN_LOG  # 注释掉原来的报错代码
            return ImportMixin(ImportExportMixinBase).get_skip_admin_log()  # 修改后的代码
        else:
            return self.skip_admin_log

    def get_tmp_storage_class(self):
        if self.tmp_storage_class is None:
            # 第三处
            # return TMP_STORAGE_CLASS  # 注释掉原来的报错代码
            return ImportMixin(ImportExportMixinBase).get_tmp_storage_class()  # 修改后的代码
        else:
            return self.tmp_storage_class

修改后,重新执行数据库迁移指令,就OK了。

参考:【xadmin】报错:annot import name ‘SKIP_ADMIN_LOG‘ from | 【xadmin】报错:cannot import name ‘SKIP_ADMIN_LOG‘


that's all

相关文章:

  • 2021-08-08
  • 2021-05-20
  • 2021-12-07
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-06-11
  • 2022-03-04
  • 2022-12-23
  • 2021-06-21
相关资源
相似解决方案