美文网首页
xadmin使用笔记

xadmin使用笔记

作者: 七彩色工作室 | 来源:发表于2019-01-24 09:52 被阅读0次

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字段即可

相关文章

网友评论

      本文标题:xadmin使用笔记

      本文链接:https://www.haomeiwen.com/subject/zozejqtx.html