一、基本設置
1、應用注冊
若要把app應用顯示在后台管理中,需要在admin.py中注冊。這個注冊有兩種方式,我比較喜歡用裝飾器的方式。
先看看普通注冊方法。打開admin.py文件,如下代碼:
from django.contrib import admin from blog.models import Blog #Blog模型的管理器 class BlogAdmin(admin.ModelAdmin): list_display=('id', 'caption', 'author', 'publish_time') #在admin中注冊綁定 admin.site.register(Blog, BlogAdmin)
上面方法是將管理器和注冊語句分開。有時容易忘記寫注冊語句,或者模型很多,不容易對應。
還有一種方式是用裝飾器,該方法是Django1.7的版本新增的功能:
from django.contrib import admin from blog.models import Blog #Blog模型的管理器 @admin.register(Blog) class BlogAdmin(admin.ModelAdmin): list_display=('id', 'caption', 'author', 'publish_time')
該方式比較方便明顯,推薦用這種方式。
2、admin界面漢化
默認admin后台管理界面是英文的,對英語盲來說用起來不方便。可以在settings.py中設置:
- LANGUAGE_CODE = 'zh-CN'
- TIME_ZONE = 'Asia/Shanghai'
1.8版本之后的language code設置不同:
- LANGUAGE_CODE = 'zh-hans'
- TIME_ZONE = 'Asia/Shanghai'
二、記錄列表界面設置
記錄列表是我們打開后台管理進入到某個應用看到的界面,如下所示:
我們可以對該界面進行設置,主要包括列表和篩選器。
1、記錄列表基本設置
比較實用的記錄列表設置有顯示字段、每頁記錄數和排序等。
from django.contrib import admin from blog.models import Blog #Blog模型的管理器 @admin.register(Blog) class BlogAdmin(admin.ModelAdmin): #listdisplay設置要顯示在列表中的字段(id字段是Django模型的默認主鍵) list_display = ('id', 'caption', 'author', 'publish_time') #list_per_page設置每頁顯示多少條記錄,默認是100條 list_per_page = 50 #ordering設置默認排序字段,負號表示降序排序 ordering = ('-publish_time',) #list_editable 設置默認可編輯字段 list_editable = ['machine_room_id', 'temperature'] #fk_fields 設置顯示外鍵字段 fk_fields = ('machine_room_id',)
此處比較簡單,自己嘗試一下即可。
另外,默認可以點擊每條記錄第一個字段的值可以進入編輯界面。
我們可以設置其他字段也可以點擊鏈接進入編輯界面。
from django.contrib import admin from blog.models import Blog #Blog模型的管理器 @admin.register(Blog) class BlogAdmin(admin.ModelAdmin): #設置哪些字段可以點擊進入編輯界面 list_display_links = ('id', 'caption')
2、篩選器
篩選器是Django后台管理重要的功能之一,而且Django為我們提供了一些實用的篩選器。
主要常用篩選器有下面3個:
from django.contrib import admin from blog.models import Blog #Blog模型的管理器 @admin.register(Blog) class BlogAdmin(admin.ModelAdmin): list_display = ('id', 'caption', 'author', 'publish_time') #篩選器 list_filter =('trouble', 'go_time', 'act_man__user_name', 'machine_room_id__machine_room_name') #過濾器 search_fields =('server', 'net', 'mark') #搜索字段 date_hierarchy = 'go_time' # 詳細時間分層篩選
對應效果如下:
此處注意:
使用 date_hierarchy 進行詳細時間篩選的時候 可能出現報錯:Database returned an invalid datetime value. Are time zone definitions for your database and pytz installed?
處理方法:
一般ManyToManyField多對多字段用過濾器;標題等文本字段用搜索框;日期時間用分層篩選。
過濾器如果是外鍵需要遵循這樣的語法:本表字段__外鍵表要顯示的字段。如:“user__user_name”
from django.db import models from django.contrib import admin from django.utils.html import format_html class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) color_code = models.CharField(max_length=6) def colored_name(self): return format_html( '<span style="color: #{};">{} {}</span>', self.color_code, self.first_name, self.last_name, ) class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'colored_name')
4.調整頁面頭部顯示內容和頁面標題
代碼:
class MyAdminSite(admin.AdminSite): site_header = '好醫生運維資源管理系統' # 此處設置頁面顯示標題 site_title = '好醫生運維' # 此處設置頁面頭部標題 admin_site = MyAdminSite(name='management')
需要注意的是: admin_site = MyAdminSite(name='management') 此處括號內name值必須設置,否則將無法使用admin設置權限,至於設置什么值,經本人測試,沒有影響。
效果如下:
后經網友提示發現也可以這樣:
from django.contrib import admin from hys_operation.models import * # class MyAdminSite(admin.AdminSite): # site_header = '好醫生運維資源管理系統' # 此處設置頁面顯示標題 # site_title = '好醫生運維' # # # admin_site = MyAdminSite(name='management') # admin_site = MyAdminSite(name='adsff') admin.site.site_header = '修改后' admin.site.site_title = '哈哈'
不繼承 admin.AdminSite 了,直接用admin.site 下的 site_header 和 site_title 。
更加簡單方便,容易理解。 唯一的區別就是 這種方法 是登錄http://ip/admin/
站點和用戶組在一起
而第一種方法是分開的。
三、編輯界面設置
編輯界面是我們編輯數據所看到的頁面。我們可以對這些字段進行排列設置等。
若不任何設置,如下圖所示:
這個界面比較簡陋,需要稍加設置即可。
1、編輯界面設置
首先多ManyToMany多對多字段設置。可以用filter_horizontal或filter_vertical:
- #Many to many 字段
- filter_horizontal=('tags',)
效果如下圖:
這樣對多對多字段操作更方便。
另外,可以用fields或exclude控制顯示或者排除的字段,二選一即可。
例如,我想只顯示標題、作者、分類標簽、內容。不想顯示是否推薦字段,可以如下兩種設置方式:
- fields = ('caption', 'author', 'tags', 'content')
或者
- exclude = ('recommend',) #排除該字段
設置之后,你會發現這些字段都是一個字段占一行。若想兩個字段放在同一行可以如下設置:
- fields = (('caption', 'author'), 'tags', 'content')
效果如下:
2、編輯字段集合
不過,我不怎么用fields和exclude。用得比較多的是fieldsets。該設置可以對字段分塊,看起來比較整潔。如下設置:
- fieldsets = (
- ("base info", {'fields': ['caption', 'author', 'tags']}),
- ("Content", {'fields':['content', 'recommend']})
- )
效果如下:
3、一對多關聯
還有一種比較特殊的情況,父子表的情況。編輯父表之后,再打開子表編輯,而且子表只能一條一條編輯,比較麻煩。
這種情況,我們也是可以處理的,將其放在同一個編輯界面中。
例如,有兩個模型,一個是訂單主表(BillMain),記錄主要信息;一個是訂單明細(BillSub),記錄購買商品的品種和數量等。
admin.py如下:
- #coding:utf-8
- from django.contrib import admin
- from bill.models import BillMain, BillSub
- @admin.register(BillMain)
- class BillMainAdmin(admin.ModelAdmin):
- inlines = [BillSubInline,] #Inline把BillSubInline關聯進來
- list_display = ('bill_num', 'customer',)
- class BillSubInline(admin.TabularInline):
- model = BillSub
- extra = 5 #默認顯示條目的數量
這樣就可以快速方便處理數據。
相關的admin比較有用的設置大致這些,若你覺得還有一些比較有用的,可以留意參與討論。
4.設置只讀字段
在使用admin的時候,ModelAdmin默認對於model的操作只有增加,修改和刪除,但是總是有些字段是不希望用戶來編輯的。而 readonly_fields 設置之后不管是admin還是其他用戶都會變成只讀,而我們通常只是想限制普通用戶。 這時我們就可以通過重寫 get_readonly_fields 方法來實現對特定用戶的只讀顯示。
代碼:
class MachineInfoAdmin(admin.ModelAdmin): def get_readonly_fields(self, request, obj=None): """ 重新定義此函數,限制普通用戶所能修改的字段 """ if request.user.is_superuser: self.readonly_fields = [] return self.readonly_fields readonly_fields = ('machine_ip', 'status', 'user', 'machine_model', 'cache', 'cpu', 'hard_disk', 'machine_os', 'idc', 'machine_group')
效果:
5、數據保存時進行一些額外的操作(通過重寫ModelAdmin的save_model實現)
代碼:
def save_model(self, request, obj, form, change): """ 重新定義此函數,提交時自動添加申請人和備案號 """ def make_paper_num(): """ 生成隨機備案號 """ import datetime import random CurrentTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") # 生成當前時間 RandomNum = random.randint(0, 100) # 生成的隨機整數n,其中0<=n<=100 UniqueNum = str(CurrentTime) + str(RandomNum) return UniqueNum obj.proposer = request.user obj.paper_num = make_paper_num() super(DataPaperStoreAdmin, self).save_model(request, obj, form, change)
這樣,在添加數據時,會自動保存申請人和備案號。
我們也可以在修改數據時獲取保存前的數據:
通過change參數,可以判斷是修改還是新增,同時做相應的操作。上述代碼就是在替換磁盤的時候修改狀態,並寫入日志。
代碼:
def save_model(self, request, obj, form, change): if change: # 更改的時候 machine_code = self.model.objects.get(pk=obj.pk).machine disk_id = self.model.objects.get(pk=obj.pk).disk_id disk_code = self.model.objects.get(pk=obj.pk).disk machine.Device.objects.filter(pk=disk_id).update(device_status='待報廢') data = {'server_code': machine_code, 'device_type': '硬盤', 'original_code': disk_code, 'way': '變更', 'current_code': obj.disk} common.DeLog.objects.create(**data) # 創建日志 else: # 新增的時候 data = {'server_code': obj.machine, 'device_type': '硬盤', 'original_code': '', 'way': '新增', 'current_code': obj.disk} common.DeLog.objects.create(**data) # 創建日志 super(MachineExDiskAdmin, self).save_model(request, obj, form, change)
同樣的,還有delete_model:
def delete_model(self, request, obj): machine.Device.objects.filter(pk=obj.pk).update(device_status='待報廢') data = {'server_code': obj.machine, 'device_type': '硬盤', 'original_code': obj.disk, 'way': '刪除', 'current_code': '', 'user_name': request.user} common.DeLog.objects.create(**data) # 創建日志 super(MachineExDiskAdmin, self).delete_model(request, obj)
6. 修改模版 chang_form.html 讓普通用戶 無法看到 “歷史” 按鈕。
默認 普通用戶下 是存在 “歷史” 按鈕的:
此時 chang_form.html 的代碼為:
我們將代碼修改為:
這樣,就可以限制 只讓管理員看到歷史 按鈕了。普通用戶看不到了:
7.對單條數據 顯示樣式的修改
需求如下:
每條數據都有 個確認標識(上圖紅框中),如果已經確認,用戶再點擊進入查看信息的時候全部只讀顯示,即不能在做修改,如果沒確認在可以修改。如下:
已確認:
未確認:
實現方法:
change_view 方法 和 get_readonly_fields 方法 配合,代碼:
def get_readonly_fields(self, request, obj=None): """ 重新定義此函數,限制普通用戶所能修改的字段 """ if request.user.is_superuser: self.readonly_fields = ['commit_date', 'paper_num'] elif hasattr(obj, 'is_sure'): if obj.is_sure: self.readonly_fields = ('project_name', 'to_mail', 'data_selected', 'frequency', 'start_date', 'end_date') else: self.readonly_fields = ('paper_num', 'is_sure', 'proposer', 'sql', 'commit_date') return self.readonly_fields def change_view(self, request, object_id, form_url='', extra_context=None): change_obj = DataPaperStore.objects.filter(pk=object_id) self.get_readonly_fields(request, obj=change_obj) return super(DataPaperStoreAdmin, self).change_view(request, object_id, form_url, extra_context=extra_context)
注:
change_view方法,允許您在渲染之前輕松自定義響應數據。(凡是對單條數據操作的定制,都可以通過這個方法配合實現)
詳細信息可見:https://docs.djangoproject.com/en/1.10/ref/contrib/admin/#django.contrib.admin.ModelAdmin.change_view
8.修改app的顯示名稱
Dajngo在Admin后台默認顯示的應用的名稱為創建app時的名稱。
我們如何修改這個app的名稱達到定制的要求呢,其實Django已經在文檔里進行了說明。
從Django1.7以后不再使用app_label,修改app相關需要使用AppConfig。我們只需要在應用的__init__.py里面進行修改即可:
from django.apps import AppConfig import os default_app_config = 'hys_operation.PrimaryBlogConfig' VERBOSE_APP_NAME = u"1-本地服務器資源" def get_current_app_name(_file): return os.path.split(os.path.dirname(_file))[-1] class PrimaryBlogConfig(AppConfig): name = get_current_app_name(__file__) verbose_name = VERBOSE_APP_NAME
9.自定義列表字段
在DataPaperStore模型中有 end_date 字段,如果當前時間大於end_date 是我們想顯示一個“已過期”,但admin列表顯示不能直接用該字段,也顯示不出來。此時可以通過自定義列表字段顯示。如下設置admin:
def expired(self, ps): """自定義列表字段, 根據數據單截止日期和當前日期判斷是否過期,並對數據庫進行更新""" import datetime from django.utils.html import format_html end_date = ps.end_date if end_date >= datetime.date.today(): ret = '未過期' color_code = 'green' else: ret = '已過期' color_code = 'red' DataPaperStore.objects.filter(pk=ps.pk).update(is_expired=ret) return format_html( '<span style="color: {};">{}</span>', color_code, ret, ) expired.short_description = '是否已過期'
通過自定義列表字段,獲取相關數據再列表中顯示,效果如下:
expired.admin_order_field = 'end_date' # 使自定義字段 可以通過單擊進行排序
10.actions
參考:https://docs.djangoproject.com/en/1.11/ref/contrib/admin/actions/
def copy_one(self, request, queryset):
# 定義actions函數
# 判斷用戶選擇了幾條數據,如果是一條以上,則報錯
if queryset.count() == 1:
old_data = queryset.values()[0]
old_data.pop('id')
# 將原數據復制並去掉id字段后,插入數據庫,以實現復制數據功能,返回值即新數據的id(這是在model里__str__中定義的)
r_pk = Record.objects.create(**old_data)
# 修改數據后重定向url到新加數據頁面
return HttpResponseRedirect('{}{}/change'.format(request.path, r_pk))
else:
self.message_user(request, "只能選取一條數據!")
copy_one.short_description = "復制所選數據"
效果如下:
為每個對象自定義 action
有時候你需要在單個對象上執行特定的 action。‘actions’工具當然可以完成這個任務,不過過程會顯得很麻煩:點擊對象、選擇 action、再點擊一個按鈕……肯定有更便捷的方式,對吧?
讓我們想辦法只點擊一次就全部搞定。
我們可以先自定義一個字段(上面提到過),讓這個字段可以每次點擊的時候幫我們做一些事情,比如:復制本條數據
自定義字段這個功能我們沒問題,但是如何讓它幫我們復制數據呢?
我們知道,django里所有的業務邏輯都是通過訪問url從而指向對應的views來實現的,就是說我們想要實現復制數據,就必須有對應的url和views。
而admin為我們提供了對應的方法:get_urls
這個方法可以讓我們臨時添加一個url,並且可以防止手動輸入此url實現操作。
代碼:
class DailyReportDbaAdmin(admin.ModelAdmin): def copy_current_data(self, obj): """自定義一個a標簽,跳轉到實現復制數據功能的url""" dest = '{}copy/'.format(obj.pk) title = '復制' return '<a href="{}">{}</a>'.format(dest, title) copy_current_data.short_description = '復制' copy_current_data.allow_tags = True def get_urls(self): """添加一個url,指向實現復制功能的函數copy_one""" from django.conf.urls import url urls = [ url('^(?P<pk>\d+)copy/?$', self.admin_site.admin_view(self.copy_one), name='copy_data'), ] return urls + super(DailyReportDbaAdmin, self).get_urls() def copy_one(self, request, *args, **kwargs): """函數實現復制本條數據,並跳轉到新復制的數據的修改頁面""" obj = get_object_or_404(DailyReportDba, pk=kwargs['pk']) old_data = {'create_date': obj.create_date, 'db_server': obj.db_server, 'db_user': obj.db_user, 'request_type': obj.request_type, 'request': obj.request, 'scripts': obj.scripts, 'de_proposer': obj.de_proposer, 'fde_proposer': obj.fde_proposer, 'operator': obj.operator, 'is_complete': obj.is_complete, 'remark': obj.remark, } r_pk = DailyReportDba.objects.create(**old_data) co_path = request.path.split('/') co_path[-2] = "{}/change".format(r_pk) new_path = '/'.join(co_path) return redirect(new_path) # actions = ['copy_one'] fieldsets = [ ('時間和服務器*', {'fields': [('create_date', 'db_server', 'db_user')]}), ('需求和腳本*', {'fields': ['request_type', 'request', 'scripts']}), ('申請人和操作人*', {'fields': [('de_proposer', 'fde_proposer', 'operator', 'is_complete'), 'remark']}) ] list_display = ('create_date', 'db_server', 'db_user', 'request', 'request_type', 'de_proposer', 'fde_proposer', 'operator', 'is_complete', 'copy_current_data', )
效果:
11.formfield_for_foreignkey
ModelAdmin.
formfield_for_foreignkey
(db_field, request, **kwargs)¶
這個方法可以過濾下拉列表的數據,使之顯示過濾后的數據
下面的代碼表示,car字段會根據當前登錄的用戶顯示此用戶所擁有的車
class MyModelAdmin(admin.ModelAdmin): def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "car": kwargs["queryset"] = Car.objects.filter(owner=request.user) return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
12.Admin中使用二級聯動
參考:
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.change_view
http://www.smallerpig.com/1125.html
https://www.ibm.com/developerworks/cn/opensource/os-django-admin/
默認的django會自動根據我們定義的模型生成form給admin使用,使用到這個form的地方分別是change和add的時候。
最終生成的結果就是可以選擇所有的省,也可以選擇所有的市,這並不合理,正確的應該是在選擇某個省的時候在市的下拉列表里只有該省的城市。
而,django原生並不能做到這么智能。下面介紹一下實現方法:
(1)admin.py
class RecordAdmin(admin.ModelAdmin): change_form_template = 'admin/extras/record_change_form.html' ...
使用change_form_template 重置 change_form所使用得模版
(2)在上一步配置的路徑下新建html文件 record_change_form.html
{% extends "admin/change_form.html" %} {% load i18n admin_urls static admin_modify %} {% block extrahead %}{{ block.super }} <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script> <script> django.jQuery(function() { var select = django.jQuery("#id_machine_room_id"); console.log(select); select.change(function(){ {# console.log("value change"+django.jQuery(this).val());#} var url = "/report/sub_servers/"+django.jQuery(this).val();//能夠正確的訪問到view的url {# console.log(url);#} django.jQuery.get( url, function(data){ var target = django.jQuery("#id_server_ip_id"); target.empty();//先要清空一下 data.forEach(function(e){ // 將從view得到的id和db_user名稱賦值給db_server的select console.log(e,e.id,e.name); target.append("<option value='"+e.id+"'>"+e.name+"<option>"); target.eq(0).attr('selected', 'true'); }); }) }); }); </script> {#{{ media }}#} {% endblock %}
注意:1.繼承change_form.html 2.設計好url
(3)在urls.py中添加一條對應的url
urls.py
from django.conf.urls import url from hys_operation import views urlpatterns = [ # url(r'^sub_users/(?P<obj_id>\d+)', views.get_sub_users), url(r'^sub_servers/(?P<obj_id>\d+)', views.get_sub_servers), ]
(4)創建views函數
def get_sub_servers(request, obj_id): # 查找此機房id下的ip servers = MachineInfo.objects.filter(idc=obj_id) result = [] for i in servers: # 對應的id和ip組成一個字典 result.append({'id': i.id, 'name': i.machine_ip}) # 返回json數據 return HttpResponse(json.dumps(result), content_type="application/json")
返回值就是過濾后的值。
參考了博客:http://yshblog.com/blog/
參考網站: http://code.ziqiangxuetang.com/django/django-admin.html
http://django-intro-zh.readthedocs.io/zh_CN/latest/part2/
http://python.usyiyi.cn/translate/django_182/ref/contrib/admin/index.html
https://www.ibm.com/developerworks/cn/opensource/os-django-admin/
http://www.cnblogs.com/linxiyue/p/4074141.html