若要把app應用顯示在後臺管理中,須要在admin.py中註冊。這個註冊有兩種方式,我比較喜歡用裝飾器的方式。javascript
先看看普通註冊方法。打開admin.py文件,以下代碼:html
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)
上面方法是將管理器和註冊語句分開。有時容易忘記寫註冊語句,或者模型不少,不容易對應。java
還有一種方式是用裝飾器,該方法是Django1.7的版本新增的功能:python
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')
該方式比較方便明顯,推薦用這種方式。mysql
默認admin後臺管理界面是英文的,對英語盲來講用起來不方便。能夠在settings.py中設置:sql
1.8版本以後的language code設置不一樣:數據庫
記錄列表是咱們打開後臺管理進入到某個應用看到的界面,以下所示:django
咱們能夠對該界面進行設置,主要包括列表和篩選器。json
比較實用的記錄列表設置有顯示字段、每頁記錄數和排序等。服務器
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')
篩選器是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/
站點和用戶組在一塊兒
而第一種方法是分開的。
編輯界面是咱們編輯數據所看到的頁面。咱們能夠對這些字段進行排列設置等。
若不任何設置,以下圖所示:
這個界面比較簡陋,須要稍加設置便可。
首先多ManyToMany多對多字段設置。能夠用filter_horizontal或filter_vertical:
效果以下圖:
這樣對多對多字段操做更方便。
另外,能夠用fields或exclude控制顯示或者排除的字段,二選一便可。
例如,我想只顯示標題、做者、分類標籤、內容。不想顯示是否推薦字段,能夠以下兩種設置方式:
或者
設置以後,你會發現這些字段都是一個字段佔一行。若想兩個字段放在同一行能夠以下設置:
效果以下:
不過,我不怎麼用fields和exclude。用得比較多的是fieldsets。該設置能夠對字段分塊,看起來比較整潔。以下設置:
效果以下:
還有一種比較特殊的狀況,父子表的狀況。編輯父表以後,再打開子表編輯,並且子表只能一條一條編輯,比較麻煩。
這種狀況,咱們也是能夠處理的,將其放在同一個編輯界面中。
例如,有兩個模型,一個是訂單主表(BillMain),記錄主要信息;一個是訂單明細(BillSub),記錄購買商品的品種和數量等。
admin.py以下:
這樣就能夠快速方便處理數據。
相關的admin比較有用的設置大體這些,若你以爲還有一些比較有用的,能夠留意參與討論。
在使用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')
效果:
代碼:
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)
默認 普通用戶下 是存在 「歷史」 按鈕的:
此時 chang_form.html 的代碼爲:
咱們將代碼修改成:
這樣,就能夠限制 只讓管理員看到歷史 按鈕了。普通用戶看不到了:
需求以下:
每條數據都有 個確認標識(上圖紅框中),若是已經確認,用戶再點擊進入查看信息的時候所有隻讀顯示,即不能在作修改,若是沒確認在能夠修改。以下:
已確認:
未確認:
實現方法:
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
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
在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。‘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', )
效果:
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)
參考:
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