Django+Xadmin打造在线教育系统(九)

xadmin的进阶开发

因版本问题.有些配置可能无效

自定义icon

xadmin的图标采用的是第三方css样式font awesome,我们可以进官网下载最新的样式替代原本的,下载地址:http://www.fontawesome.com.cn/

下载完后把里面的“css”和“fonts”两个文件夹拷贝到xadmin的源码(路径:xadmin/static/vendor/font-awesome)里面
使用model_icon来进行修改

# Course的admin管理器
class CourseAdmin(object):
    '''课程'''

    list_display = [ 'name','desc','detail','degree','learn_times','students']
    search_fields = ['name', 'desc', 'detail', 'degree', 'students']
    list_filter = [ 'name','desc','detail','degree','learn_times','students']
    model_icon = 'fa fa-book'

默认排序、只读字段和不显示的字段

课程:

  • 按点击数倒序排序
  • 点击数不能编辑
  • 不显示收藏人数
# Course的admin管理器
class CourseAdmin(object):
    '''课程'''

    list_display = [ 'name','desc','detail','degree','learn_times','students']   #显示的字段
    search_fields = ['name', 'desc', 'detail', 'degree', 'students']             #搜索
    list_filter = [ 'name','desc','detail','degree','learn_times','students']    #过滤 
    model_icon = 'fa fa-book'            #图标
    ordering = ['-click_nums']           #排序
    readonly_fields = ['click_nums']     #只读字段,不能编辑
    exclude = ['fav_nums']               #不显示的字段

下拉框搜索

写在外键所指的adminx配置中

relfield_style = 'fk-ajax'

当有外键指向他,会以ajax方式加载
数据量过大时很有用

inlines添加数据

目前在添加课程的时候没法添加章节和课程资源,我们可以用inlines去实现这一功能

class LessonInline(object):
    model = Lesson
    extra = 0


class CourseResourceInline(object):
    model = CourseResource
    extra = 0


# 在CourseAdmin中使用inlines添加上面两个的方法
class CourseAdmin(object):
    inlines = [LessonInline,CourseResourceInline]    #增加章节和课程资源

一张表分两个Model来管理

课程里面分为轮播课程和不是轮播课程两种类型,我们可以分开来管理
course/models.py里面新建一个Model

class BannerCourse(Course):
    '''显示轮播课程'''
    class Meta:
        verbose_name = '轮播课程'
        verbose_name_plural = verbose_name
        #这里必须设置proxy=True,这样就不会再生成一张表,同时还具有Model的功能
        proxy = True

course/adminx.py

class CourseAdmin(object):
    '''课程'''

    list_display = [ 'name','desc','detail','degree','learn_times','students']   #显示的字段
    search_fields = ['name', 'desc', 'detail', 'degree', 'students']             #搜索
    list_filter = [ 'name','desc','detail','degree','learn_times','students']    #过滤
    model_icon = 'fa fa-book'            #图标
    ordering = ['-click_nums']           #排序
    readonly_fields = ['click_nums']     #只读字段
    exclude = ['fav_nums']               #不显示的字段
    inlines = [LessonInline,CourseResourceInline]    #增加章节和课程资源

    def queryset(self):
        # 重载queryset方法,来过滤出我们想要的数据的
        qs = super(CourseAdmin, self).queryset()
        # 只显示is_banner=True的课程
        qs = qs.filter(is_banner=False)
        return qs


class BannerCourseAdmin(object):
    '''轮播课程'''

    list_display = [ 'name','desc','detail','degree','learn_times','students']
    search_fields = ['name', 'desc', 'detail', 'degree', 'students']
    list_filter = [ 'name','desc','detail','degree','learn_times','students']
    model_icon = 'fa fa-book'
    ordering = ['-click_nums']
    readonly_fields = ['click_nums']
    exclude = ['fav_nums']
    inlines = [LessonInline,CourseResourceInline]

    def queryset(self):
        #重载queryset方法,来过滤出我们想要的数据的
        qs = super(BannerCourseAdmin, self).queryset()
        #只显示is_banner=True的课程
        qs = qs.filter(is_banner=True)
        return qs

# 将管理器与model进行注册关联
xadmin.site.register(Course, CourseAdmin)
xadmin.site.register(BannerCourse, BannerCourseAdmin)

xadmin的其它常见功能

可以在列表上快速修改内容

list_editable = [ 'degree','desc']

自定义函数作为列

def get_zj_nums(self):
    return self.lesson_set.all().count()
get_zj_nums.short_description = "章节数"

显示自定义的html代码

def go_to(self):
    from django.utils.safestring import mark_safe
    # 如果不mark safe。会对其进行转义
    return  mark_safe("<a href='http://iceflower.xyz'>跳转</>")
go_to.short_description = "跳转"

refresh定时刷新工具

refresh_times = [3,5]           #自动刷新(里面是秒数)

字段联动
应用场景:当添加一门课程的时候,希望课程机构里面的课程数 +1
重写xadmin的save_models方法

 def save_models(self):
      # 在保存课程的时候统计课程机构的课程数
      # 字段联动
      obj = self.new_obj
      # 新增课程还没有保存,统计的课程数少一个
      obj.save()
      # 必须确定存在。
      if obj.course_org is not None:
          # obj实际是一个course对象
          course_org = obj.course_org
          course_org.course_nums = Course.objects.filter(course_org = course_org).count()
          course_org.save()

xadmin自行探究

  • local 语言包
  • migration 数据表的记录
  • plugins 每一个后台页面都是一个plugin 插件机制
  • static文件。js css
  • template xadmin自己用到的html文件
  • 对django admin的封装

增加富文本编辑器Ueditor

下载地址 https://github.com/twz915/DjangoUeditor3/
解压后,把DjangoUeditor文件夹拷贝到项目目录下面

# settings中添加app

INSTALLED_APPS = [
    'DjangoUeditor',
]
 # 富文本编辑器url
    path('ueditor/',include('DjangoUeditor.urls' )),

course/models.py中Course修改detail字段

class Course(models.Model):
    # detail = models.TextField("课程详情")
    detail = UEditorField(verbose_name=u'课程详情', width=600, height=300, imagePath="courses/ueditor/",
                          filePath="courses/ueditor/", default='')

xadmin/plugs目录下新建ueditor.py文件,代码如下

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/plugs/__init__.py里面添加ueditor插件

PLUGINS = (
   'ueditor',
)

course/adminx.py中使用

class CourseAdmin(object):
    #detail就是要显示为富文本的字段名
    style_fields = {"detail": "ueditor"}

在模板中必须关闭Django的自动转义才能正常显示

    <div class="tab_cont tab_cont1">
         {% autoescape off %}
         {{ course.detail }}
         {% endautoescape %}
     </div>

或者

    {{ course.detail|safe }}

导入excel

  • 如何注入导入excel代码到菜单
  • 如何只在课程列表显示
  • 如何接收文件对文件进行处理

新建xadmin/plugins/excel.py

import xadmin
from xadmin.views import BaseAdminPlugin, ListAdminView
from django.template import loader


#excel 导入
class ListImportExcelPlugin(BaseAdminPlugin):
    import_excel = False

    def init_request(self, *args, **kwargs):
        return bool(self.import_excel)

    def block_top_toolbar(self, context, nodes):
        nodes.append(loader.render_to_string('xadmin/excel/model_list.top_toolbar.import.html', context=get_context_dict(context)))


xadmin.site.register_plugin(ListImportExcelPlugin, ListAdminView)

添加到init.py文件中

PLUGINS = (
   'excel',
)

新建xadmin/templates/xadmin/excel/model_list.top_toolbar.import.html

    {% load i18n %}
    <div class="btn-group export">
      <a class="dropdown-toggle btn btn-default btn-sm" data-toggle="dropdown" href="#">
        <i class="icon-share"></i> 导入 <span class="caret"></span>
      </a>
      <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
          <li><a data-toggle="modal" data-target="#export-modal-import-excel"><i class="icon-circle-arrow-down"></i> 导入 Excel</a></li>
      </ul>
        <script>
            function fileChange(target){
    //检测上传文件的类型
                var imgName = document.all.submit_upload.value;
                var ext,idx;
                if (imgName == ''){
                    document.all.submit_upload_b.disabled=true;
                    alert("请选择需要上传的 xls 文件!");
                    return;
                } else {
                    idx = imgName.lastIndexOf(".");
                    if (idx != -1){
                        ext = imgName.substr(idx+1).toUpperCase();
                        ext = ext.toLowerCase( );
    {#                    alert("ext="+ext);#}
                        if (ext != 'xls' && ext != 'xlsx'){
                            document.all.submit_upload_b.disabled=true;
                            alert("只能上传 .xls 类型的文件!");

                            return;
                        }
                    } else {
                        document.all.submit_upload_b.disabled=true;
                        alert("只能上传 .xls 类型的文件!");
                        return;
                    }
                }

            }
        </script>
        <div id="export-modal-import-excel" class="modal fade">
          <div class="modal-dialog">
            <div class="modal-content">
              <form method="post" action="" enctype="multipart/form-data">
                  {% csrf_token %}
              <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                <h4 class="modal-title">导入 Excel</h4>
              </div>
              <div class="modal-body">
                   <input type="file" onchange="fileChange(this)" name="excel" id="submit_upload">

              </div>
              <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
                <button class="btn btn-success" type="submit" id="submit_upload_b"><i class="icon-share"></i> 导入</button>
              </div>
              </form>
            </div><!-- /.modal-content -->
          </div><!-- /.modal-dalog -->
        </div><!-- /.modal -->

    </div>

只需要在adminx中设置变量import_excel=True就可以开启导入按钮
在adminx中重写post方法,将文件内容保存到数据中

def post(self, request, *args , **kwargs):
    if 'excel' in request.FILES:
        pass
# 中间pass步骤不管做什么事情,都要最后return父类的

return super(CourseAdmin, self).post(request, args, kwargs)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,706评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,002评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,462评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,375评论 0 216
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,763评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,849评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,033评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,768评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,490评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,734评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,204评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,566评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,227评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,137评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,934评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,926评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,774评论 2 274

推荐阅读更多精彩内容

  • 世界十大名校第四名:斯坦福大学 小利兰·斯坦福大学,常直接称为斯坦福大学,为一所坐落于美国加利福尼亚州斯坦福市的私...
    麦吉可先森阅读 739评论 0 0
  • 1.很有心机的小孩子—— 那天我拿了个玩具公仔在公园的凉亭里, 一个四五岁的孩子看见了,于是过来对我说:“姐姐,能...
    范末末阅读 339评论 1 0
  • 天旺无意中登录了网上的婚恋网站,一幅幅美眉的照片让他无比贪恋,搜索了一下,竟然有很多本地征婚的女子。一时兴起,天旺...
    阿卫1阅读 339评论 1 1
  • 方糖小语 方糖小语 阴冷的雨天在晶莹透亮的玻璃杯...
    乐尽天真且陶陶阅读 287评论 0 1