xadmin使用笔记

1、源码安装(待补充)
安装方法
复制 xadmin 目录到你的项目(从github下载 https://github.com/sshwsfc/xadmin/tree/django2)

pip install httplib2 django-formtools django-crispy-forms
# setting.py
INSTALLED_APPS = [
    ...,
    'xadmin',
    'crispy_forms',
    'reversion',
]
# urls.py
import xadmin
xadmin.autodiscover()

from xadmin.plugins import xversion
xversion.register_models()

urlpatterns = [
    url(r'^admin/', xadmin.site.urls)
]

2、左边菜单名称的修改,修改apps.py文件

class BlogConfig(AppConfig):
    name = 'blog'
    verbose_name = u'博客'

还需要在该应用下的init.py下添加

default_app_config = "blog.apps.BlogConfig"

3、页面显示中文

# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
  
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

4、修改后台显示的网站名称和footer名称,菜单样式

from xadmin import views
 
class BaseSetting(object):
    enable_themes = True #开启主题选择
    use_bootswatch = True
class GlobalSettings(object):
    site_title = "我的管理系统"  #设置左上角title名字
    site_footer = "504工作室"  #设置底部关于版权信息
    #设置菜单缩放
    menu_style = "accordion"     #左侧导航条修改可折叠
    global_models_icon = {
         User: "glyphicon glyphicon-user", UserDistrict: "fa fa-cloud"
}  # 设置models的全局图标
xadmin.site.register(views.BaseAdminView, BaseSetting)
xadmin.site.register(views.CommAdminView, GlobalSettings)

5、modelview里面可以指定的参数

list_display=[] #要显示的字段
search_fields=[] #搜索的字段
list_filter = [] #过滤器
date_hierarchy =['publication_date']  #添加过滤(这里是过滤日期)
ordering = ['-publication_date',]   #排序(这里以日期排序,加‘-’表示降序)
filter_horizontal = ('authors',) #filter_horizontal 从‘多选框’的形式改变为‘过滤器’的方式,水平排列过滤器,必须是一个 ManyToManyField类型,且不能用于 ForeignKey字段,默认地,管理工具使用`` 下拉框`` 来展现`` 外键`` 字段
filter_vertical = ['authors',]#同上filter_horizontal,垂直排列过滤器
raw_id_fields = ['publisher',] #将ForeignKey字段从‘下拉框’改变为‘文本框’显示
list_editable = ['csdevice'] #在列表页可直接编辑的字段
model_icon = 'fa fa-user-secret'  #图标样式
style_fields = {'csdevice': 'm2m_transfer','csservice': 'ueditor',} #字段显示样式
refresh_times = [10, 60] #自动刷新时间
show_detail_fields=['ttdsn'] #在指定的字段后添加一个显示数据详情的一个按钮
relfield_style = 'fk-ajax' #涉及到外键下拉的时候使用ajax搜索的方式而不是全部列出的方式,比如在分类下拉很多的情况下,这个功能就很好用
free_query_filter=['字段1','字段2',......]#默认为 True , 指定是否可以自由搜索. 如果开启自由搜索, 用户可以通过 url 参数来进行特定的搜索
exclude=['字段1','字段2',......]#隐藏字段
aggregate_fields = {"expire": "max"}#  列聚合,在list表格下面会增加一行统计的数据,可用的值:"count","min","max","avg",  "sum"
# 添加数据时候,一步一步提供数据,分块显示
wizard_form_list = [
        ("基础信息", ("name", "contact", "telphone", "address")),
        ("其它信息", ("customer_id", "expire", "description")),
    ]
grid_layouts = ("table", "thumbnails") #列表的布局方式,是以表格一行一条的方式还是类似于缩略图的方式展示的
list_per_page = 50 # 每页显示数据的条数
list_max_show_all = 200 #每页最大显示数据的条数
list_display_links=[] #指定列表显示的哪列可以点击跳转到详情更新页
preserve_filters=True #详细页面,删除、修改,更新后跳转回列表后,是否保留原搜索条件
save_as = False#详细页面,按钮为“Sava as new” 或 “Sava and add another”
save_as_continue = True#点击保存并继续编辑
save_on_top = False#详细页面,在页面上方是否也显示保存删除等按钮
radio_fields = {"ug": admin.VERTICAL} # 或admin.HORIZONTAL #详细页面时,使用radio显示选项(FK默认使用select)
show_full_result_count = True #列表时,模糊搜索后面显示的数据个数样式

7、自定义显示界面
可分为Main主区域和Side侧边区域,Main或Side中又可通过Fieldset再分多个区域。Fieldset为一个元组,第一个字段为需要设置的名称,其它字段均为模型中的字段名。如下:

class DeviceAdmin(object):
  ...
  form_layout = (
    Main(
        Fieldset('基础信息', 
                 'site', 'device_name', 'device_id', 'device_type', 'account', 'password'),
        Fieldset('EXTRA',
                 'device_model', 'supplier', 'responsible_by', 'device_ip', 'sn_number'),
    ),
    Side(
        Fieldset('其它',
                 'buy_date', 'expire_date', 'note', 'attachment', 'date'),
    )
)

8、根据登录用户过滤数据
需要根据登录用户或组过滤数据时,xadmin.py中改为重写queryset方法即可。如下:

class DeviceAdmin(object):
    ...
    def queryset(self):
        """函数作用:使当前登录的用户只能看到自己负责的设备"""
        qs = super(DeviceAdmin, self).queryset()
        if self.request.user.is_superuser:
            return qs
        return qs.filter(area_company=Group.objects.get(user=self.request.user))

9、文件上传
settings.py文件增加以下代码:

MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

urls.py文件增加路由设置:

from django.conf.urls import url
from django.conf import settings
from django.views.static import serve
urlpatterns = [
    url(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
]

然后就可以在models里面定义字段为图片或者文件类型了

class Userinfo(models.Model):
    sex_choice={
        ('male','男'),
        ('female','女')
    }
    username=models.CharField("用户名",max_length=100)
    sex=models.CharField('性别',max_length=10,choices=sex_choice,default='male')
    image=models.ImageField('头像',upload_to='image/%Y%m',default='image/default.png',max_length=254)

10、增加自定义的actions操作,比如现在我需要对选中的用户批量加钱

  • 1、创建adminx_actions.py文件,要代码如下
# coding:utf-8
from xadmin.plugins.actions import BaseActionView
from django.http import HttpResponse

class MoneyAddAction(BaseActionView):
    # 这里需要填写三个属性
    action_name = "money_add_100"    #: 相当于这个 Action 的唯一标示, 尽量用比较针对性的名字
    description = u'批量增加金钱'  #: 描述, 出现在 Action 菜单中, 可以使用 ``%(verbose_name_plural)s`` 代替 Model 的名字.

    model_perm = 'change'    #: 该 Action 所需权限

    # 而后实现 do_action 方法
    def do_action(self, queryset):
        # queryset 是包含了已经选择的数据的 queryset
        for obj in queryset:
            # obj 的操作
            obj.money+=100
            obj.save()
        # 返回 HttpResponse
        return HttpResponse("修改成功")

这里做药就是在do_action中对选中的queryset进行增删改查操作。

  • 2、在adminx.py文件中,导入刚刚的文件并使用
from .adminx_actions import  *
class UserAdmin(object):
    list_display=['username','sex','image','money']
    #list_filter=[]
    model_icon='fa fa-address-card'
    actions=[MoneyAddAction,] #这里使用刚刚创建的action
xadmin.site.register(Userinfo,UserAdmin)

如下图,刷新之后这里就有新建的action,点击之后则会执行相应的操作


image.png

11、图表的使用
在 Model OptionClass 中设定 data_charts 属性, 该属性为 dict 类型, key 是图表的标示名称, value 是图表的具体设置属性. 使用示例:

class RecordAdmin(object):
    data_charts = {
    "host_service_type_counts": {
        'title' : '主机类型统计',
        'x-field' : "service_type",
        'y-field' : ("service_type"),
        'option' : {
            "series" : {"bars":{"align":"center", "barWidth": 0.8,"show":True}},
            "xaxis"  : {"aggregate":"count","mode":"categories"}
        },
    },
    "host_idc_counts" : {
        'title' : '机房统计',
        'x-field' : "idc",
        'y-field' : ("idc",),
        'option' : {
            "series" : {"bars":{"align":"center", "barWidth": 0.5,"show":True}},
            "xaxis"  : {"aggregate":"count","mode":"categories"}
        }
    }
}

图表的主要属性为:

title : 图表的显示名称
x-field : 图表的 X 轴数据列, 一般是日期, 时间等
y-field : 图表的 Y 轴数据列, 该项是一个 list, 可以同时设定多个列, 这样多个列的数据会在同一个图表中显示
order : 排序信息, 如果不写则使用数据列表的排序

在上面的属性中,如果只是这样子的话,默认展示的就是折线图,如果需要用到其它图表类型,可以重写option属性

data_charts = {
    "host_service_type_counts": {
        'title' : '主机类型统计',
        'x-field' : "service_type",
        'y-field' : ("service_type"),
        'option' : {
            "series" : {"bars":{"align":"center", "barWidth": 0.8,"show":True}},
            "xaxis"  : {"aggregate":"count","mode":"categories"}
        },
    },
    "host_idc_counts" : {
        'title' : '机房统计',
        'x-field' : "idc",
        'y-field' : ("idc",),
        'option' : {
            "series" : {"bars":{"align":"center", "barWidth": 0.5,"show":True}},
            "xaxis"  : {"aggregate":"count","mode":"categories"}
        }
    }
}

但是,到目前为止,仅发现以上的bars柱形图可以用,其它图形的设置没有找到。。。。
顺便提一下,在xadmin\static\xadmin\vendor\flot目录下,可以看到图表是使用jquery.flot.js渲染的,但是我使用里面的option参数去测试饼图的时候并没有成功,不懂要怎么处理。。。

============================2019.11.27更新内容==============
12、adminx.py配置views的时候,可以重定义以下函数实现其它自定义功能。

class JFLogsAdmin(object):
    
    def queryset(self):
        """queryset对应的是列表页显示的数据内容"""
        qs = super(JFLogsAdmin, self).queryset()
        if not self.request.user.is_superuser:
            qs=qs.filter(...) #按用户过滤
        return qs

    def formfield_for_dbfield(self, db_field, **kwargs):
        """formfield_for_dbfield是指在新增或者编辑内容的时候的数据,比如下面只显示指定部门的员工"""
        print(db_field.name)
        
        if not self.request.user.is_superuser:
            # 对case这个表项的下拉框选择进行过滤
            if db_field.name == "employee": 
                kwargs["queryset"] = Employee.objects.filter(dept=self.request.user.dept).order_by('id')                                                
            # 对assigned_recipient这个表项的下拉选择进行过滤
            return db_field.formfield(**dict(**kwargs))

        else:
            attrs = self.get_field_attrs(db_field, **kwargs)
            return db_field.formfield(**dict(attrs, **kwargs))

    def save_models(self):
        """新增或者修改保存数据的时候调用,可以在保存之前修改数据内容或者添加其它只读字段的内容"""
        self.new_obj.operator=self.request.user
        super().save_models()

    def post(self, request, *args, **kwargs):
        """在点击提交保存按钮的时候触发,可以对数据进行一个校验"""
        #print(request.POST)
        if request.POST.get('action') not in ['delete_selected']:
            err_tip=''
            jf=RulesRecord.objects.filter(id=request.POST.get('jf')).first()
            #print(jf.person_limit,jf.date_limit,jf.times_limit,jf.jf_num,jf.jf_num_max)
            if jf.person_limit=='每人':
                if jf.date_limit=='不限':
                    exists_count=JFLogs.objects.filter(employee_id=request.POST.get('employee'),jf_id=request.POST.get('jf')).count()
                    if exists_count>=jf.times_limit:
                        err_tip="个人奖励次数已到达次数上限"
                elif jf.date_limit=='每天':
                    start_date = datetime.datetime(datetime.datetime.now().year, datetime.datetime.now().month, datetime.datetime.now().day,0,0,0)
                    end_date = datetime.datetime(datetime.datetime.now().year, datetime.datetime.now().month, datetime.datetime.now().day,23,59,59)
                    #print(start_date,end_date)
                    exists_count=JFLogs.objects.filter(employee_id=request.POST.get('employee'),jf_id=request.POST.get('jf')).filter(inserttime__range=(start_date,end_date)).count()
                    #print(exists_count)
                    if exists_count>=jf.times_limit:
                        err_tip="个人奖励次数已到达每天上限"
                elif jf.date_limit=='每周':
                    monday,sunday=get_current_week()
                    start_date = datetime.datetime(monday.year, monday.month, monday.day,0,0,0)
                    end_date = datetime.datetime(sunday.year, sunday.month, sunday.day,23,59,59)
                    exists_count=JFLogs.objects.filter(employee_id=request.POST.get('employee'),jf_id=request.POST.get('jf')).filter(inserttime__range=(start_date,end_date)).count()
                    #print(exists_count)
                    if exists_count>=jf.times_limit:
                        err_tip="个人奖励次数已到达每周上限"
                else:
                    err_tip='未知错误'
            elif jf.person_limit=='每部门':
                dept=Employee.objects.filter(id=request.POST.get('employee')).first()
                emps=Employee.objects.filter(dept=dept.dept).values("id")
                #print(emps)
                if jf.date_limit=='不限':
                    exists_count=JFLogs.objects.filter(employee_id__in=emps,jf_id=request.POST.get('jf')).count()
                    if exists_count>=jf.times_limit:
                        err_tip="部门奖励次数已到达上限"
                elif jf.date_limit=='每天':
                    start_date = datetime.datetime(datetime.datetime.now().year, datetime.datetime.now().month, datetime.datetime.now().day,0,0,0)
                    end_date = datetime.datetime(datetime.datetime.now().year, datetime.datetime.now().month, datetime.datetime.now().day,23,59,59)
                    #print(start_date,end_date)
                    exists_count=JFLogs.objects.filter(employee_id__in=emps,jf_id=request.POST.get('jf')).filter(inserttime__range=(start_date,end_date)).count()
                    #print(exists_count)
                    if exists_count>=jf.times_limit:
                        err_tip="部门奖励次数已到达每天上限"
                elif jf.date_limit=='每周':
                    monday,sunday=get_current_week()
                    start_date = datetime.datetime(monday.year, monday.month, monday.day,0,0,0)
                    end_date = datetime.datetime(sunday.year, sunday.month, sunday.day,23,59,59)
                    exists_count=JFLogs.objects.filter(employee_id__in=emps,jf_id=request.POST.get('jf')).filter(inserttime__range=(start_date,end_date)).count()
                    #print(exists_count)
                    if exists_count>=jf.times_limit:
                        err_tip="部门奖励次数已到达每周上限"
                else:
                    err_tip='未知错误'
            if err_tip:
                messages.add_message(request, messages.ERROR, err_tip)
                return HttpResponseRedirect('/xadmin/frontapp/jflogs/add/')
            else:
                obj=Employee.objects.filter(id=request.POST.get('employee')).first()
                obj.jf_num+=int(request.POST.get('jf_num'))
                obj.save()
                return super(JFLogsAdmin, self).post(request, args, kwargs)
        else:
            if request.POST.get('post')=='yes':
                print(request.POST)
                idx=[int(x) for x in request.POST.getlist('_selected_action')]
                print(idx)
                jfs=JFLogs.objects.filter(id__in=idx)
                for jf in jfs:
                    obj=Employee.objects.filter(id=jf.employee_id).first()
                    obj.jf_num-=jf.jf_num
                    if obj.jf_num<0:obj.jf_num=0
                    obj.save()
            return super(JFLogsAdmin, self).post(request, args, kwargs)
              

14、书签的使用
以下是官方书签的示例,可以看到大概就是可以设置书签名称、和过滤条件。

class UserAdmin(object):
    list_bookmarks = [{
        'title': "书签名称",         # 书签的名称, 显示在书签菜单中
        'query': {'gender': True,'postdate__gte':20200327}, # 过滤参数, 是标准的 queryset 过滤
        'order': ('-age','name'),         # 排序参数
        'cols': ('first_name', 'age', 'phones'),  # 显示的列
        'search': 'Tom'    # 搜索参数, 指定搜索的内容
        }, {...}
    ]

如何一打开就使用默认书签呢?我是这么做的:

1、修改\xadmin\plugins\bookmark.py文件,增加一个selected判断(大概是84行)
#修改前
selected = (current_qs == bk_qs)
#修改后
if not current_qs and 'selected' in bk:
    selected=bk['selected']
    current_qs = bk_qs
 else:
    selected = (current_qs == bk_qs)

2、然后配合adminx.py里面的list_bookmarks设置,增加selected参数
list_bookmarks = [{
        'title': "书签名称",         # 书签的名称, 显示在书签菜单中
        'query': {'gender': True,'postdate__gte':20200327}, # 过滤参数, 是标准的 queryset 过滤
        'selected':True
        },]

3、当然,默认查询的queryset也要增加一个默认过滤条件
class NewsAdmin(object):
    model_icon='fa fa-tasks'
    list_display=['classify','postdate','title','go_to']
    ...
    list_bookmarks = [{
        'title': "今天新闻",         # 书签的名称, 显示在书签菜单中
        'query': {'postdate__exact': datetime.datetime.now().strftime('%Y%m%d')}, # 过滤参数, 是标准的 queryset 过滤
        'order': ('classify','-postdate','orirank'),         # 排序参数
        'selected':True,
        }, 
        {
        'title': "近两天新闻",         # 书签的名称, 显示在书签菜单中
        'query': {'postdate__gte': (datetime.datetime.now()+datetime.timedelta(-1)).strftime('%Y%m%d')}, # 过滤参数, 是标准的 queryset 过滤
        'order': ('classify','-postdate','orirank'),         # 排序参数
        }, 
    ]
    class Meta:
        verbose_name="新闻"
        verbose_name_plural=verbose_name
    def queryset(self):
        fillters={}
        for k in self.request.GET.keys():
            if '_p_' in k:
                fillters[k.replace('_p_','')]=self.request.GET.get(k)
        if not fillters:
            fillters={'postdate__exact': datetime.datetime.now().strftime('%Y%m%d')} #如果没有选择标签就要增加一个默认的过滤条件
        qs = super(NewsAdmin, self).queryset()
        qs=qs.filter(**fillters) 
        return qs

==============2020-04-21更新==============
在导出数据的时候指定导出字段
1、修改extra_apps\xadmin\plugins\export.py的get_result_list方法

def get_result_list(self, __):
        if self.request.GET.get('all', 'off') == 'on':
            self.admin_view.list_per_page = sys.maxsize
        #增加下面这一行
        self.admin_view.list_display=getattr(self.admin_view,'list_export_fields', self.admin_view.list_display)
        return __()

2、在admin.py或者xadmin.py中增加list_export_fields字段即可

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 1.新建环境2.配置mysql3.运行manage.py makemaigrations,migrate 生成数据...
    青穗黄阅读 529评论 0 0
  • 三、xadmin后台管理 3.1见xadmin建站(预装)获取xadmin 3.2.xadmin的设置 (1)新建...
    偷了月光的猫阅读 947评论 0 6
  • 通过自身的学习,深度了解比较优势原理,已用在自己身上,看能发挥出什么效果,下述为比较优势原理。 比较优势原理可以表...
    临拂晓阅读 1,678评论 0 0
  • 读书:健康的家庭关系中,夫妻关系是第一位! 老爸最爱老妈,还有快两个月才到老妈的生日,老爸提醒我到时候要订个蛋糕给...
    蕴阳_渡己成长阅读 143评论 0 0