xadmimn简介

Xadmin采用python语言编写,基于Bootstrap3,内置功能丰富 github地址: https://github.com/sshwsfc/xadmin 本人建议:由于Xadmin已经不维护,本文仅以学习为主。 学完Xadmin,如想了解更多python的后台管理系统,推荐https://github.com/happybeta/simpleui 和 https://github.com/mtianyan/django-antd-tyadmin

下载源码后,拖入项目中,安装xadmin需要的相关依赖,执行migrate,生成xadmin所需要的表

Xadmin简介

xadmin会自动发现我们自定义的用户表并自动生成

1
2
3
4
5
6
7
8
9
10
11
12
class UserProfile(AbstractUser):
nick_name = models.CharField(max_length=50, verbose_name="昵称", default="")
birthday = models.DateField(verbose_name="生日", null=True, blank=True)
gender = models.CharField(verbose_name="性别", choices=GENDER_CHOICES, max_length=6)
address = models.CharField(max_length=100, verbose_name="地址", default="")
mobile = models.CharField(max_length=11, verbose_name="手机号码")
image = models.ImageField(verbose_name="用户头像", upload_to="head_image/%Y/%m", default="default.jpg")

class Meta:
verbose_name = "用户信息"
verbose_name_plural = verbose_name

自定义的表

简单使用

自定义后台管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 自定义后台管理类
from apps.organizations.models import Teacher
class CityAdmin(object):
# 列表页的显示
list_display = ["id", "name", "desc"]
# 搜索框的显示极其范围
search_fields = ["name", "desc"]
# 过滤器的显示极其范围
# 如果City这个模型类存在外键如country,那么可以使用country__外键的字段来指明过滤范围
list_filter = ["name", "desc", "add_time"]
# 字段是否可编辑
list_editable = ["name", "desc"]

# 注册后台管理类
xadmin.site.register(City, CityAdmin)


全局配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class GlobalSettings(object):
# 设置标题
site_title = "eu课堂后台管理系统"
# 设置页脚
site_footer = "eu课堂"
# 设置左侧导航栏是否可以展开
menu_style = "accordion"


class BaseSettings(object):
# 设置主题
enable_themes = True
use_bootswatch = True


更高级的使用

自定义表的详情页面

重写get_form_layout方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

class NewCourseAdmin(object):
...
list_display = ['name', 'desc', 'show_image', 'go_to', 'detail', 'degree', 'learn_times', 'students']
search_fields = ['name', 'desc', 'detail', 'degree', 'students']
list_filter = ['name', 'teacher__name', 'desc', 'detail', 'degree', 'learn_times', 'students']
...

def get_form_layout(self):
# self.org_obj判断是否是编辑页面,如果是编辑页面则返回True
if self.org_obj:
self.form_layout = (
# 中间主体内容
Main(
# 划分区域,每个字段独立一行
Fieldset("讲师信息",
'teacher', 'course_org',
# 不显示讲师信息字样
css_class='unsort no_title'
),

# 调用Row()的字段在同一行
Fieldset("基本信息",
'name', 'desc',
Row('learn_times', 'degree'),
Row('category', 'tag'),
'youneed_know', 'teacher_tell', 'detail',
),
),
# 右侧部分
Side(
Fieldset("访问信息",
'fav_nums', 'click_nums', 'students', 'add_time'
),
),
Side(
Fieldset("选择信息",
'is_banner', 'is_classics'
),
)
)
return super(NewCourseAdmin, self).get_form_layout()

组和权限的概念

可以给每个用户设置权限,不同的权限对应不同的功能;如果需要对多个用户设置权限,则需要添加一个组,对组配置权限,再将用户选中为某个组,那么该用户便有了那个组的权限。

不同用户登录时返回不同的数据

重写queryset方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class NewCourseAdmin(object):
...
list_display = ['name', 'desc', 'show_image', 'go_to', 'detail', 'degree', 'learn_times', 'students']
search_fields = ['name', 'desc', 'detail', 'degree', 'students']
list_filter = ['name', 'teacher__name', 'desc', 'detail', 'degree', 'learn_times', 'students']
...

def queryset(self):
# 获取所有数据
qs = super().queryset()
# 判断该用户是否为超级管理员用户
if not self.request.user.is_superuser:
# 不是超级管理员则对数据进行筛选
# 这边teacher和user是一对一的关系
qs = qs.filter(teacher=self.request.user.teacher)
return qs



新增一条数据时的拦截

重写save_models方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class UserCourseAdmin(object):
...
list_display = ['user', 'course', 'add_time']
search_fields = ['user', 'course']
list_filter = ['user', 'course', 'add_time']
...

# 当新增或者修改一条数据时,调用
def save_models(self):
# obj为UserCourse的实例
obj = self.new_obj
# 判断是否为新增数据,如果obj.id为None时,则新增一条数据
if not obj.id:
# 保存当前新增对象
obj.save()
# 当新增一条数据时,学生人数加1
course = obj.course
course.students += 1
course.save()


多个管理器管理同一张表

models.py中新建一个类,该类继承要管理的那张表

1
2
3
4
5
6
7
8

class BannerCourse(Course):
class Meta:
verbose_name = "轮播课程"
verbose_name_plural = verbose_name
# 设置proxy=True,做migration不会生成一张表,但是具有了管理表的功能(继承Course)
proxy = True

adminx.py中新建一个管理器,重写queryset方法

1
2
3
4
5
6
7
8
9
10
11
12
13

class BannerCourseAdmin(object):
list_display = ['name', 'desc', 'detail', 'degree', 'learn_times', 'students']
search_fields = ['name', 'desc', 'detail', 'degree', 'students']
list_filter = ['name', 'teacher__name', 'desc', 'detail', 'degree', 'learn_times', 'students']
list_editable = ["degree", "desc"]

def queryset(self):
qs = super().queryset()
# 筛选出是否为广告位的课程
qs = qs.filter(is_banner=True)
return qs

注册管理类

1
2
3
xadmin.site.register(BannerCourse, BannerCourseAdmin)


列表页图片字段,显示图片

自定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Course(BaseModel):
teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE, verbose_name="讲师")
course_org = models.ForeignKey(CourseOrg, null=True, blank=True, on_delete=models.CASCADE, verbose_name="课程机构")
name = models.CharField(verbose_name="课程名", max_length=50)
desc = models.CharField(verbose_name="课程描述", max_length=300)
learn_times = models.IntegerField(default=0, verbose_name="学习时长(分钟数)")
degree = models.CharField(verbose_name="难度", choices=(("cj", "初级"), ("zj", "中级"), ("gj", "高级")), max_length=2)
students = models.IntegerField(default=0, verbose_name='学习人数')
fav_nums = models.IntegerField(default=0, verbose_name='收藏人数')
click_nums = models.IntegerField(default=0, verbose_name="点击数")
notice = models.CharField(verbose_name="课程公告", max_length=300, default="")
category = models.CharField(default=u"后端开发", max_length=20, verbose_name="课程类别")
tag = models.CharField(default="", verbose_name="课程标签", max_length=10)
youneed_know = models.CharField(default="", max_length=300, verbose_name="课程须知")
teacher_tell = models.CharField(default="", max_length=300, verbose_name="老师告诉你")
is_classics = models.BooleanField(default=False, verbose_name="是否经典")
detail = UEditorField(verbose_name="课程详情", width=600, height=300, imagePath="courses/ueditor/images/",
filePath="courses/ueditor/files/", default="")
is_banner = models.BooleanField(default=False, verbose_name="是否广告位")
image = models.ImageField(upload_to="courses/%Y/%m", verbose_name="封面图", max_length=100)

class Meta:
verbose_name = "课程信息"
verbose_name_plural = verbose_name

def __str__(self):
return self.name

# 自定义方法
# 显示图片
def show_image(self):
from django.utils.safestring import mark_safe
# 自定义html标签
return mark_safe("<img src='{}'>".format(self.image.url))

# 为该方法添加一个属性,作为列表页名称
show_image.short_description = "图片"

# 自定义方法
# 跳转到课程链接
def go_to(self):
from django.utils.safestring import mark_safe
return mark_safe("<a href='/course/{}'>跳转</a>".format(self.id))

# 为该方法添加一个属性,作为列表页名称
go_to.short_description = "跳转"


管理器中在list_display 添加该方法

1
2
3
4
5
6
class NewCourseAdmin(object):
...
list_display = ['name', 'desc', 'show_image', 'go_to', 'detail', 'degree', 'learn_times', 'students']
...


编辑页面某些字段设置为只读

在管理器类中添加属性readonly_fields ,在该属性中添加字段

1
2
3
4
5
6
class NewCourseAdmin(object):
...
readonly_fields = ["students", "add_time"]
...


编辑页面隐藏某些字段

在管理器类中添加属性exclude,在该属性中添加字段

1
2
3
4
5
6
7
8
class NewCourseAdmin(object):
...
# 隐藏某些字段,字段不能与readonly_fields中的字段相同,否则以readonly_fields中的字段为主,必填字段不得添加
# exclude中有的字段在form_layout中不要出现
exclude= ["students", "add_time"]
...


用户进入列表页的默认排序

在管理器类中添加属性ordering,在该属性中添加字段

1
2
3
4
5
6
class NewCourseAdmin(object):
...
ordering = ["click_nums"]
...


自定义后台管理图标

1
2
3
4
5
6
class NewCourseAdmin(object):
...
# 格式:fa fa-xxxxxx
model_icon = 'fa fa-addres'
...

自定义更多图标
百度:awesome font—> 找到中文链接—>下载,解压—>拷贝css,fonts至xadmin的原始路径下—>xadmin/static/xadmin/vendor/font-awesome

一个页面修改多个页面的信息

在adminx.py自定义一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class LessonInline(object):
# 定义的该模型类必须有一个外键指向下面管理器锁管理的模型类
model = Lesson
# 设置样式为标签
# style = "tab"
extra = 0
exclude = ["add_time"]

class NewCourseAdmin(object):
...
# 配置同一个编辑页面同时可以编辑其他页面的信息
inlines = [LessonInline,]

# 注册管理器
xadmin.site.register(Course, NewCourseAdmin)
...

集成ueditor富文本编辑器到xadmin中

注:由于Ueditor富文本不在维护,本文仅以学习为主。将下载好的Ueditor依赖拉入site-package或拖入项目中。
github地址:https://github.com/zhangfisher/DjangoUeditor

配置

settings.py

1
2
3
4
5
6
7
在INSTALL_APPS里面增加DjangoUeditor app,如下:
INSTALLED_APPS = (
#........
'DjangoUeditor',
)


urls.py

1
2
3
url(r'^ueditor/',include('DjangoUeditor.urls' )),


在model中的使用

1
2
3
4
5
6
7

from DjangoUeditor.models import UEditorField
class Blog(models.Model):
Name=models.CharField(,max_length=100,blank=True)
Content=UEditorField('内容 ',width=600, height=300, toolbars="full", imagePath="", filePath="", upload_settings={"imageMaxSize":1204000},
settings={},command=None,event_handler=myEventHander(),blank=True)

集成xadmin

xadmin的plugins创建一个ueditor.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# -*- coding: utf-8 -*-



import xadmin
from xadmin.views import BaseAdminPlugin, CreateAdminView, ModelFormAdminView, UpdateAdminView
from DjangoUeditor.models import UEditorField
from DjangoUeditor.widgets import UEditorWidget
from django.conf import settings


class XadminUEditorWidget(UEditorWidget):
def __init__(self,**kwargs):
self.ueditor_options=kwargs
self.Media.js = None
super(XadminUEditorWidget,self).__init__(kwargs)

class UeditorPlugin(BaseAdminPlugin):

def get_field_style(self, attrs, db_field, style, **kwargs):
if style == 'ueditor':
if isinstance(db_field, UEditorField):
widget = db_field.formfield().widget
param = {}
param.update(widget.ueditor_settings)
param.update(widget.attrs)
return {'widget': XadminUEditorWidget(**param)}
return attrs

def block_extrahead(self, context, nodes):
js = '<script type="text/javascript" src="%s"></script>' % (settings.STATIC_URL + "ueditor/ueditor.config.js") #自己的静态目录
js += '<script type="text/javascript" src="%s"></script>' % (settings.STATIC_URL + "ueditor/ueditor.all.min.js") #自己的静态目录
nodes.append(js)

xadmin.site.register_plugin(UeditorPlugin, UpdateAdminView)
xadmin.site.register_plugin(UeditorPlugin, CreateAdminView)

在xadmin的plugins中的init.py注册ueditor插件

1
2
3
4
5
6
7
8
9
10
11
12
PLUGINS = (
'actions',
'filters',
'bookmark',
# 'export',
'layout',
'refresh',
...
'ueditor',
)


urls.py

1
2
3
4
5
6
7
class NewCourseAdmin(object):
# 配置detail字段显示为富文本
style_fields = {
"detail": "ueditor"
}


在templates中可能要使用:
{ % autoescape off %}…{ % endautoescape % }:关闭django转义

xadmin数据的导入和导出

依赖

1
2
pip install django-import-export

注册app

1
2
3
4
5
6
7
INSTALLED_APPS = [

# xadmin的导入和导出功能
‘import_export’,

]

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyResource(resources.ModelResource):
class Meta:
model = Course
# fields = ('name', 'description',)
# exclude = ()

class NewCourseAdmin(object):
...
# 配置导入和导出按钮
import_export_args = {'import_resource_class': MyResource, 'export_resource_class': MyResource}
...


如果要去掉自带的导出功能,到xadmin下的 plugins的__init__注释掉export