1、源碼安裝(待補(bǔ)充)
安裝方法
復(fù)制 xadmin 目錄到你的項(xiàng)目(從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'博客'
還需要在該應(yīng)用下的init.py下添加
default_app_config = "blog.apps.BlogConfig"
3甫题、頁(yè)面顯示中文
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'
4符欠、修改后臺(tái)顯示的網(wǎng)站名稱和footer名稱蛹批,菜單樣式
from xadmin import views
class BaseSetting(object):
enable_themes = True #開(kāi)啟主題選擇
use_bootswatch = True
class GlobalSettings(object):
site_title = "我的管理系統(tǒng)" #設(shè)置左上角title名字
site_footer = "504工作室" #設(shè)置底部關(guān)于版權(quán)信息
#設(shè)置菜單縮放
menu_style = "accordion" #左側(cè)導(dǎo)航條修改可折疊
global_models_icon = {
User: "glyphicon glyphicon-user", UserDistrict: "fa fa-cloud"
} # 設(shè)置models的全局圖標(biāo)
xadmin.site.register(views.BaseAdminView, BaseSetting)
xadmin.site.register(views.CommAdminView, GlobalSettings)
5仰泻、modelview里面可以指定的參數(shù)
list_display=[] #要顯示的字段
search_fields=[] #搜索的字段
list_filter = [] #過(guò)濾器
date_hierarchy =['publication_date'] #添加過(guò)濾(這里是過(guò)濾日期)
ordering = ['-publication_date',] #排序(這里以日期排序荆陆,加‘-’表示降序)
filter_horizontal = ('authors',) #filter_horizontal 從‘多選框’的形式改變?yōu)椤^(guò)濾器’的方式,水平排列過(guò)濾器集侯,必須是一個(gè) ManyToManyField類型被啼,且不能用于 ForeignKey字段,默認(rèn)地棠枉,管理工具使用`` 下拉框`` 來(lái)展現(xiàn)`` 外鍵`` 字段
filter_vertical = ['authors',]#同上filter_horizontal浓体,垂直排列過(guò)濾器
raw_id_fields = ['publisher',] #將ForeignKey字段從‘下拉框’改變?yōu)椤谋究颉@示
list_editable = ['csdevice'] #在列表頁(yè)可直接編輯的字段
model_icon = 'fa fa-user-secret' #圖標(biāo)樣式
style_fields = {'csdevice': 'm2m_transfer','csservice': 'ueditor',} #字段顯示樣式
refresh_times = [10, 60] #自動(dòng)刷新時(shí)間
show_detail_fields=['ttdsn'] #在指定的字段后添加一個(gè)顯示數(shù)據(jù)詳情的一個(gè)按鈕
relfield_style = 'fk-ajax' #涉及到外鍵下拉的時(shí)候使用ajax搜索的方式而不是全部列出的方式,比如在分類下拉很多的情況下辈讶,這個(gè)功能就很好用
free_query_filter=['字段1','字段2',......]#默認(rèn)為 True , 指定是否可以自由搜索. 如果開(kāi)啟自由搜索, 用戶可以通過(guò) url 參數(shù)來(lái)進(jìn)行特定的搜索
exclude=['字段1','字段2',......]#隱藏字段
aggregate_fields = {"expire": "max"}# 列聚合汹碱,在list表格下面會(huì)增加一行統(tǒng)計(jì)的數(shù)據(jù),可用的值:"count","min","max","avg", "sum"
# 添加數(shù)據(jù)時(shí)候荞估,一步一步提供數(shù)據(jù)咳促,分塊顯示
wizard_form_list = [
("基礎(chǔ)信息", ("name", "contact", "telphone", "address")),
("其它信息", ("customer_id", "expire", "description")),
]
grid_layouts = ("table", "thumbnails") #列表的布局方式稚新,是以表格一行一條的方式還是類似于縮略圖的方式展示的
list_per_page = 50 # 每頁(yè)顯示數(shù)據(jù)的條數(shù)
list_max_show_all = 200 #每頁(yè)最大顯示數(shù)據(jù)的條數(shù)
list_display_links=[] #指定列表顯示的哪列可以點(diǎn)擊跳轉(zhuǎn)到詳情更新頁(yè)
preserve_filters=True #詳細(xì)頁(yè)面,刪除跪腹、修改褂删,更新后跳轉(zhuǎn)回列表后,是否保留原搜索條件
save_as = False#詳細(xì)頁(yè)面冲茸,按鈕為“Sava as new” 或 “Sava and add another”
save_as_continue = True#點(diǎn)擊保存并繼續(xù)編輯
save_on_top = False#詳細(xì)頁(yè)面屯阀,在頁(yè)面上方是否也顯示保存刪除等按鈕
radio_fields = {"ug": admin.VERTICAL} # 或admin.HORIZONTAL #詳細(xì)頁(yè)面時(shí),使用radio顯示選項(xiàng)(FK默認(rèn)使用select)
show_full_result_count = True #列表時(shí)轴术,模糊搜索后面顯示的數(shù)據(jù)個(gè)數(shù)樣式
7难衰、自定義顯示界面
可分為Main主區(qū)域和Side側(cè)邊區(qū)域,Main或Side中又可通過(guò)Fieldset再分多個(gè)區(qū)域逗栽。Fieldset為一個(gè)元組盖袭,第一個(gè)字段為需要設(shè)置的名稱,其它字段均為模型中的字段名彼宠。如下:
class DeviceAdmin(object):
...
form_layout = (
Main(
Fieldset('基礎(chǔ)信息',
'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稽物、根據(jù)登錄用戶過(guò)濾數(shù)據(jù)
需要根據(jù)登錄用戶或組過(guò)濾數(shù)據(jù)時(shí)辜梳,xadmin.py中改為重寫queryset方法即可膜廊。如下:
class DeviceAdmin(object):
...
def queryset(self):
"""函數(shù)作用:使當(dāng)前登錄的用戶只能看到自己負(fù)責(zé)的設(shè)備"""
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文件增加路由設(shè)置:
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操作摧冀,比如現(xiàn)在我需要對(duì)選中的用戶批量加錢
- 1倍踪、創(chuàng)建adminx_actions.py文件,要代碼如下
# coding:utf-8
from xadmin.plugins.actions import BaseActionView
from django.http import HttpResponse
class MoneyAddAction(BaseActionView):
# 這里需要填寫三個(gè)屬性
action_name = "money_add_100" #: 相當(dāng)于這個(gè) Action 的唯一標(biāo)示, 盡量用比較針對(duì)性的名字
description = u'批量增加金錢' #: 描述, 出現(xiàn)在 Action 菜單中, 可以使用 ``%(verbose_name_plural)s`` 代替 Model 的名字.
model_perm = 'change' #: 該 Action 所需權(quán)限
# 而后實(shí)現(xiàn) do_action 方法
def do_action(self, queryset):
# queryset 是包含了已經(jīng)選擇的數(shù)據(jù)的 queryset
for obj in queryset:
# obj 的操作
obj.money+=100
obj.save()
# 返回 HttpResponse
return HttpResponse("修改成功")
這里做藥就是在do_action中對(duì)選中的queryset進(jìn)行增刪改查操作索昂。
- 2建车、在adminx.py文件中,導(dǎo)入剛剛的文件并使用
from .adminx_actions import *
class UserAdmin(object):
list_display=['username','sex','image','money']
#list_filter=[]
model_icon='fa fa-address-card'
actions=[MoneyAddAction,] #這里使用剛剛創(chuàng)建的action
xadmin.site.register(Userinfo,UserAdmin)
如下圖楼镐,刷新之后這里就有新建的action,點(diǎn)擊之后則會(huì)執(zhí)行相應(yīng)的操作
11往枷、圖表的使用
在 Model OptionClass 中設(shè)定 data_charts 屬性, 該屬性為 dict 類型, key 是圖表的標(biāo)示名稱, value 是圖表的具體設(shè)置屬性. 使用示例:
class RecordAdmin(object):
data_charts = {
"host_service_type_counts": {
'title' : '主機(jī)類型統(tǒng)計(jì)',
'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' : '機(jī)房統(tǒng)計(jì)',
'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 軸數(shù)據(jù)列, 一般是日期, 時(shí)間等
y-field : 圖表的 Y 軸數(shù)據(jù)列, 該項(xiàng)是一個(gè) list, 可以同時(shí)設(shè)定多個(gè)列, 這樣多個(gè)列的數(shù)據(jù)會(huì)在同一個(gè)圖表中顯示
order : 排序信息, 如果不寫則使用數(shù)據(jù)列表的排序
在上面的屬性中框产,如果只是這樣子的話,默認(rèn)展示的就是折線圖错洁,如果需要用到其它圖表類型秉宿,可以重寫option屬性
data_charts = {
"host_service_type_counts": {
'title' : '主機(jī)類型統(tǒng)計(jì)',
'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' : '機(jī)房統(tǒng)計(jì)',
'x-field' : "idc",
'y-field' : ("idc",),
'option' : {
"series" : {"bars":{"align":"center", "barWidth": 0.5,"show":True}},
"xaxis" : {"aggregate":"count","mode":"categories"}
}
}
}
但是,到目前為止屯碴,僅發(fā)現(xiàn)以上的bars柱形圖可以用描睦,其它圖形的設(shè)置沒(méi)有找到。导而。忱叭。隔崎。
順便提一下,在xadmin\static\xadmin\vendor\flot目錄下韵丑,可以看到圖表是使用jquery.flot.js渲染的爵卒,但是我使用里面的option參數(shù)去測(cè)試餅圖的時(shí)候并沒(méi)有成功,不懂要怎么處理撵彻。钓株。。
============================2019.11.27更新內(nèi)容==============
12陌僵、adminx.py配置views的時(shí)候轴合,可以重定義以下函數(shù)實(shí)現(xiàn)其它自定義功能。
class JFLogsAdmin(object):
def queryset(self):
"""queryset對(duì)應(yīng)的是列表頁(yè)顯示的數(shù)據(jù)內(nèi)容"""
qs = super(JFLogsAdmin, self).queryset()
if not self.request.user.is_superuser:
qs=qs.filter(...) #按用戶過(guò)濾
return qs
def formfield_for_dbfield(self, db_field, **kwargs):
"""formfield_for_dbfield是指在新增或者編輯內(nèi)容的時(shí)候的數(shù)據(jù)碗短,比如下面只顯示指定部門的員工"""
print(db_field.name)
if not self.request.user.is_superuser:
# 對(duì)case這個(gè)表項(xiàng)的下拉框選擇進(jìn)行過(guò)濾
if db_field.name == "employee":
kwargs["queryset"] = Employee.objects.filter(dept=self.request.user.dept).order_by('id')
# 對(duì)assigned_recipient這個(gè)表項(xiàng)的下拉選擇進(jìn)行過(guò)濾
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):
"""新增或者修改保存數(shù)據(jù)的時(shí)候調(diào)用受葛,可以在保存之前修改數(shù)據(jù)內(nèi)容或者添加其它只讀字段的內(nèi)容"""
self.new_obj.operator=self.request.user
super().save_models()
def post(self, request, *args, **kwargs):
"""在點(diǎn)擊提交保存按鈕的時(shí)候觸發(fā),可以對(duì)數(shù)據(jù)進(jìn)行一個(gè)校驗(yàn)"""
#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="個(gè)人獎(jiǎng)勵(lì)次數(shù)已到達(dá)次數(shù)上限"
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="個(gè)人獎(jiǎng)勵(lì)次數(shù)已到達(dá)每天上限"
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="個(gè)人獎(jiǎng)勵(lì)次數(shù)已到達(dá)每周上限"
else:
err_tip='未知錯(cuò)誤'
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="部門獎(jiǎng)勵(lì)次數(shù)已到達(dá)上限"
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="部門獎(jiǎng)勵(lì)次數(shù)已到達(dá)每天上限"
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="部門獎(jiǎng)勵(lì)次數(shù)已到達(dá)每周上限"
else:
err_tip='未知錯(cuò)誤'
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豪椿、書簽的使用
以下是官方書簽的示例奔坟,可以看到大概就是可以設(shè)置書簽名稱、和過(guò)濾條件搭盾。
class UserAdmin(object):
list_bookmarks = [{
'title': "書簽名稱", # 書簽的名稱, 顯示在書簽菜單中
'query': {'gender': True,'postdate__gte':20200327}, # 過(guò)濾參數(shù), 是標(biāo)準(zhǔn)的 queryset 過(guò)濾
'order': ('-age','name'), # 排序參數(shù)
'cols': ('first_name', 'age', 'phones'), # 顯示的列
'search': 'Tom' # 搜索參數(shù), 指定搜索的內(nèi)容
}, {...}
]
如何一打開(kāi)就使用默認(rèn)書簽?zāi)乜缺课沂沁@么做的:
1、修改\xadmin\plugins\bookmark.py文件鸯隅,增加一個(gè)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設(shè)置,增加selected參數(shù)
list_bookmarks = [{
'title': "書簽名稱", # 書簽的名稱, 顯示在書簽菜單中
'query': {'gender': True,'postdate__gte':20200327}, # 過(guò)濾參數(shù), 是標(biāo)準(zhǔn)的 queryset 過(guò)濾
'selected':True
},]
3蝌以、當(dāng)然,默認(rèn)查詢的queryset也要增加一個(gè)默認(rèn)過(guò)濾條件
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')}, # 過(guò)濾參數(shù), 是標(biāo)準(zhǔn)的 queryset 過(guò)濾
'order': ('classify','-postdate','orirank'), # 排序參數(shù)
'selected':True,
},
{
'title': "近兩天新聞", # 書簽的名稱, 顯示在書簽菜單中
'query': {'postdate__gte': (datetime.datetime.now()+datetime.timedelta(-1)).strftime('%Y%m%d')}, # 過(guò)濾參數(shù), 是標(biāo)準(zhǔn)的 queryset 過(guò)濾
'order': ('classify','-postdate','orirank'), # 排序參數(shù)
},
]
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')} #如果沒(méi)有選擇標(biāo)簽就要增加一個(gè)默認(rèn)的過(guò)濾條件
qs = super(NewsAdmin, self).queryset()
qs=qs.filter(**fillters)
return qs
==============2020-04-21更新==============
在導(dǎo)出數(shù)據(jù)的時(shí)候指定導(dǎo)出字段
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字段即可