利用 Django admin 完成更多任務(轉)

利用 Django admin 完成更多任務

 

 

Django admin

Django 爲將來的開發人員提供了許多功能:一個成熟的標準庫,一個活躍的用戶社區,以及 Python 語言的全部好處。雖然其餘 Web 框架也聲稱能提供一樣的內容,但 Django 的獨特之處在於它內置了管理應用程序 —— adminhtml

admin 提供了開箱即用的高級 Create-Read-Update-Delete (CRUD) 功能,減小了重複工做所需的時間。這是許多 Web 應用程序的關鍵所在,程序員能夠在開發時快速瀏覽他們的數據庫模型;非技術最終用戶能夠在部署時使用 admin 添加和編輯站點內容。python

在現實中,總須要執行某些定製操做。關於 admin 外觀的基本狀況,Django 文檔提供許多指南,Django 自身也包含了一些簡單的方法用來改寫 admin 行爲的子集。若是您須要更多功能怎麼辦呢?從哪裏開始着手呢?本文將指導您如何進行一些高級 adimin 定製。程序員

admin 快速瀏覽

大多數 Django 開發人員都很熟悉 admin 的默認功能。讓咱們快速回顧一下,首先編輯頂級 urls.py 啓用 admin,見清單 1。shell

清單 1. 在 urls.py 中啓用 admin
1
2
3
4
5
6
7
8
9
from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
 
urlpatterns = patterns('',
     # Uncomment the next line to enable the admin:
     (r'^admin/(.*)', admin.site.root),
)

您還須要將 django.contrib.admin 應用程序添加到 settings.INSTALLED_APPS安全

在繼續下一步前,建議計劃擴展 admin 的用戶熟悉一下源代碼。對於支持快捷鍵和符號連接的操做系統,建立一個指向 admin 應用程序的快捷鍵或符號連接會頗有用。服務器

admin 包含在 Django 包中。假如已經使用安裝工具安裝了 admin,則它位於 django/contrib/admin 下的 site-packages 中。如下是一個項目到 Django admin 源的符號連接樣例,您能夠根據操做系統和 Django 安裝的位置定製,以便更輕鬆的複製和引用:app

$ ln -s /path/to/Python/install/site-packages/django/contrib/admin admin-source

admin.autodiscover() 方法迭代設置 .INSTALLED_APPS 中的每一個應用程序,並查找名爲 admin.py 的文件。該文件一般位於應用程序目錄的最上方,與 models.py 級別同樣。

樣例應用程序須要清單 2 中提供的 models.py。相應的 admin.py 以下所示。

清單 2. 該應用程序的樣例 models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
from django.db import models
class Document(models.Model):
     '''A Document is a blog post or wiki entry with some text content'''
     name = models.CharField(max_length=255)
     text = models.TextField()
     
     def __unicode__(self):
         return self.name
 
class Comment(models.Model):
     '''A Comment is some text about a given Document'''
     document = models.ForeignKey(Document, related_name='comments')
     text = models.TextField()

如今,您能夠經過運行 Django 開發服務器調用 admin:

1
python manage.py runserver

admin 可從默認位置 http://localhost:8000/admin/ 獲取。登陸以後,您能夠看到基本的 admin 屏幕,以下所示。

圖 1. 基本的 Django admin 屏幕

基本的 Django admin 屏幕

注意,您的模型如今尚不可用,由於您尚未建立 admin.py。清單 3 展現的代碼讓您能在 admin 中使用模型。

清單 3. 樣例 admin.py
1
2
3
4
5
6
7
8
9
10
11
from django.contrib import admin
from more_with_admin.examples import models
 
class DocumentAdmin(admin.ModelAdmin):
     pass
 
class CommentAdmin(admin.ModelAdmin):
     pass
 
admin.site.register(models.Document, DocumentAdmin)
admin.site.register(models.Comment, CommentAdmin)

如今若是您在 admin 中重載主頁,您將看到可用的新模型,以下所示。

圖 2. 能夠支持定製模型的 Django admin

帶有定製模型的 Django admin 屏幕

定製 admin 模型頁面

理解在不修改 Django 源代碼的狀況下如何定製 admin 的關鍵在於,記住 admin 像其餘程序同樣只是一個普通的 Django 應用程序。最重要的一點是,這意味着可使用 Django 模版繼承系統。

Django 的模版搜索順序老是將您本身項目的模版排在其餘系統以前。此外,admin 在恢復到默認狀況前,會嘗試搜索匹配每一個模型的硬編碼模版。這爲輕鬆定製提供了一個擴展點。

首先,確保 Django 經過編輯項目的 settings.py 來查看您的模版目錄。

清單 4. 編輯 settings.py 以查看模版目錄
1
2
3
4
TEMPLATE_DIRS = (
     "/path/to/project/more_with_admin/templates",
     "/path/to/project/more_with_admin/examples/templates",
)

而後在項目中建立如下目錄:

1
2
$ mkdir templates/admin/examples/document/
$ mkdir templates/admin/examples/comment/

Django admin 的特殊行爲將檢查目錄和應用程序名稱(這裏是 examples),而後是模型的名稱(document 和 comment),而後才能使用系統模版呈現該管理頁面。

重寫單個模型添加/編輯頁面

admin 用來添加和編輯模型實例的頁面名稱是 change_form.html。首先在 Document 模型目錄中建立一個名爲 templates/admin/examples/document/change_form.html 的頁面,而後將 Django 模版繼承線置入其中:{% extends "admin/change_form.html" %}

如今能夠進行定製了。花一些時間熟悉實際的 admin/change_form.html 的內容。它很合理地將一些能夠重寫的模板塊組織到一塊兒,可是有些定製可能須要大量複製模塊。不過,使用基於塊的模板重寫老是比複製整個頁面要好。

您想對添加/編輯頁面執行哪一種定製?對於系統中的每一個 Document,您應該展現 5 個最近評論的預覽。

首先,建立一些樣例內容。

清單 5. 使用 Django shell 建立帶幾個評論的樣例Document
1
2
3
4
5
6
$ python manage.py shell
In [1]: from examples import models
In [2]: d = models.Document.objects.create(name='Test document',
                                            text='This is a test document.')
In [3]: for c in range(0, 10):
    ...:     models.Comment.objects.create(text='Comment number %s' % c, document=d)

如今,admin 列表頁面展現一個 Document。選擇該 Document 打開默認的添加/編輯頁面,以下所示。

圖 3. 帶有 Document 的默認添加/編輯頁面

默認的添加/編輯頁面

注意,相關評論不顯示。在 admin 中顯示相關模型的標準方法是使用強大的 Inline 類。Inline 類容許 admin 用戶在單個頁面編輯或添加多個相關模型。要查看運行的 inline,按照清單 6 編輯應用程序 admin.py。

清單 6. 向 Document admin 添加相關模型評論
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.contrib import admin
from more_with_admin.examples import models
 
class CommentInline(admin.TabularInline):
     model = models.Comment
 
class DocumentAdmin(admin.ModelAdmin):
     inlines = [CommentInline,]
 
class CommentAdmin(admin.ModelAdmin):
     pass
 
admin.site.register(models.Document, DocumentAdmin)
admin.site.register(models.Comment, CommentAdmin)

圖 4 展現了添加 TabularInline 控件以後新的添加/編輯頁面。

圖 4. 將評論模型做爲 Inline 添加以後的添加/編輯頁面

將評論模型做爲 Inline 添加以後的添加/編輯頁面

這無疑很是強大,可是若是隻想快速預覽評論的話就不必這樣作了。

這裏您能夠採起兩種方法。一種是使用 Django admin widget 接口編輯與 inline 關聯的 HTML widget;Django 文檔詳細描述了 widget。另外一種方法是直接修改添加/編輯頁面。若是不但願使用任何特定於 admin 的功能,那麼該方法很是有用。

若是不容許編輯評論(多是因爲用戶沒有足夠的權限),可是又想 讓用戶看到評論,那麼能夠修改 change_form.html。

Django admin 提供的變量

要在模型實例頁面添加功能,您須要瞭解 admin 已經可使用什麼數據。兩個鍵變量的說明以下。

表 1. 定製 admin 模版須要的變量

爲 admin 頁面中的內容建立模板標記

列出沒法直接輸入 Django 模板的相關評論查詢代碼。最佳的解決方案是使用模板標記。首先,建立模板標記目錄 and __init__.py 文件:

1
2
$ mkdir examples/templatetags/
$ touch examples/templatetags/__init__.py

建立一個名爲 examples/templatetags/example_tags.py 的新文件,並添加如下代碼。

清單 7. 根據給定 Document ID 檢索評論的模板標籤
1
2
3
4
5
6
7
8
9
10
from django import template
from examples import models
 
register = template.Library()
 
@register.inclusion_tag('comments.html')
def display_comments(document_id):
     document = models.Document.objects.get(id__exact=document_id)
     comments = models.Comment.objects.filter(document=document)[0:5]
     return { 'comments': comments }

因爲這是一個包含標籤,您須要建立相應的模板文件:comments.html。編輯 examples/templates/comments.html 文件並輸入清單 8 中的代碼。

清單 8. 顯示評論預覽集的模板
1
2
3
{% for comment in comments %}
< blockquote >{{ comment.text }}</ blockquote >
{% endfor %}

如今能夠將它添加到 admin 頁面了。在 admin.py 中註釋掉對 CommentInline 的引用,並按照清單 9 更改 change_form.html 的本地版本。

清單 9. 在添加/編輯頁面包含模板標籤
1
2
3
4
5
6
7
{% extends "admin/change_form.html" %}
 
{% load example_tags %}
 
{% block after_field_sets %}
   {% if object_id %}{% display_comments object_id %}{% endif %}
   {% endblock %}

在使用前檢查 object_id 的存在很重要,由於 change_form.html 還能夠用來建立新實例,在這種狀況下 object_id 不可用。after_field_sets 塊只是 admin 中提供的衆多擴展點之一。其餘請參考 change_form.html 源頁面。

圖 5 展現了更新後的表格。

圖 5. 包含模板標記以後的添加/編輯頁面

包含模板標記以後的添加/編輯頁面

修改 admin 行爲

模板重寫只能作這麼多了。若是您想更改 admin 的實際流和行爲怎麼辦呢?修改源代碼不是不可能,可是那會讓您受制於更新時使用的特定 Django 版本。

重寫 AdminModel 方法

默認狀況下,在 admin 中單擊 Save 將用戶帶回到列表頁面。一般這沒有問題,可是若是您想直接到 admin 外部的對象預覽頁面,那應該怎麼辦?在開發內容管理系統 (CMS) 時這種狀況很常見。

admin 應用程序中的大部分功能都附加到 admin.ModelAdmin 類。這是該對象從 admin.py 中繼承的類。您能夠重寫許多許多公開方法。類定義請查看 admin-source/options.py 中的源代碼。

有兩種方法能夠更改 Save 按鈕的行爲:您能夠重寫 admin.ModelAdmin.response_add,該按鈕負責保存後的實際重定向;還能夠重寫 admin.ModelAdmin.change_view。後一種方式更爲簡單。

清單 10. 保存事件以後重寫指向用戶的頁面
1
2
3
4
5
6
7
8
9
10
11
12
class DocumentAdmin(admin.ModelAdmin):
 
     def change_view(self, request, object_id, extra_context=None):
 
         result = super(DocumentAdmin, self).change_view(request, object_id, extra_context)
 
         document = models.Document.objects.get(id__exact=object_id)
         
         if not request.POST.has_key('_addanother') and
               not request.POST.has_key('_continue'):
             result['Location'] = document.get_absolute_url()
         return result

如今用戶單擊 Save 時,他們將被指向預覽頁面,而不是展現全部 Documents 的列表頁面。

使用 signals 向 admin 添加功能

signals 是 Django 中較少使用的功能,它能夠提升代碼的模塊化程度。signals 定義保存模型或加載模板的事件,不管它在哪裏運行,Django 項目均可以偵聽到並對它作出反應。這意味着您能夠輕鬆的提升應用程序的行爲,而無需直接修改它們。

admin 提供了一個應用程序開發人員常常想修改的功能:經過 django.contrib.auth.models.User 類管理用戶。Django 用戶每每只能添加或修改 admin,這使得這個有用的類很難定製。

想象一下,您但願每次建立一個新的 User 對象時,站點管理員都能收到一封電子郵件。由於 User 模塊沒法直接在項目中使用,實現該目標的惟一方法彷佛是子類化 User 或者使用間接方法,好比建立虛擬配置文件對象進行修改。

清單 11 展現了在保存 User 實例時添加運行的函數有多麼簡單。signals 一般被添加到 models.py。

清單 11. 添加新用戶時使用 Django signals 進行通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from django.db import models
from django.db.models import signals
from django.contrib.auth.models import User
from django.core.mail import send_mail
 
class Document(models.Model):
     [...]
 
class Comment(models.Model):
     [...]
 
def notify_admin(sender, instance, created, **kwargs):
     '''Notify the administrator that a new user has been added.'''
     if created:
        subject = 'New user created'
        message = 'User %s was added' % instance.username
        from_addr = 'no-reply@example.com'
        recipient_list = ('admin@example.com',)
        send_mail(subject, message, from_addr, recipient_list)       
 
signals.post_save.connect(notify_admin, sender=User)

post_save signal 由 Django 提供,每次保存或建立模型時都會激活。connect() 方法帶有兩個參數:一個回調參數(notify_admin)和 sender 參數,後者指定該回調只關注 User 模型的保存事件。

在回調中,post_save signal 傳遞發送方(模型類)、該模型的實例和提示是否剛剛建立了實例的布爾值。在本例中,若是建立了 User,該方法將發送一封電子郵件;不然不執行任何操做。

有關其餘 Django 提供的 signals 列表,請參見 參考資料,以及介紹如何編寫 signals 的文檔。

進一步修改:添加低級權限

一個經常使用的 Django admin 特性是它的權限系統,該系統能夠擴展以包含低級權限。默認狀況下,admin 能夠細粒度控制角色和權限,可是這些角色僅應用於類級別:用戶能夠修改全部 Document 或不修改任何 Document。

每每只容許用戶修改特定的對象。這經常稱爲低級 權限,由於它們只能修改特定數據庫表格行,而綜合權限能夠修改表格中的任何記錄。樣例應用程序中的狀況多是您只但願用戶看到他們建立的 Document。

首先,更新 models.py 以包含建立 Document 的屬性記錄,以下所示。

清單 12. 更新 models.py 以記錄建立每一個 Document 的用戶
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from django.db import models
from django.db.models import signals
from django.contrib.auth.models import User
from django.core.mail import send_mail
     
class Document(models.Model):
     name = models.CharField(max_length=255)
     text = models.TextField()
     added_by = models.ForeignKey(User,
       null=True, blank=True)
 
     def get_absolute_url(self):
         return 'http://example.com/preview/document/%d/' % self.id
 
     def __unicode__(self):
         return self.name
[...]

接下來,須要添加代碼自動記錄哪一個用戶建立了 Document。signals 沒法用於這種狀況,由於 signal 沒有訪問用戶對象。可是,ModelAdmin 類提供了一個方法,使用該請求和當前用戶做爲參數。

修改 admin.py 中的 save_model() 方法,以下所示。

清單 13. 重寫 DocumentAdmin 中的方法,以在建立數據庫時保存當前用戶
1
2
3
4
5
6
7
8
9
from django.contrib import admin
class DocumentAdmin(admin.ModelAdmin):
     def save_model(self, request, obj, form, change):
         if getattr(obj, 'added_by', None) is None:
             obj.added_by = request.user
         obj.last_modified_by = request.user
         obj.save()
 
[...]

若是 added_by 的值是 None,那麼這就是一個還沒有保存的新記錄。(您還能夠檢查 change 是否爲 false,這指示是否將添加記錄,可是檢查 added_by 是否爲空表示是否填寫添加到 admin 以外的記錄)。

另外一個低級權限是將文檔列表限制在建立它們的用戶範圍內。ModelAdmin 類爲此提供了一個鉤子程序 —— 它有一個名爲 queryset() 的方法,該方法能夠肯定任何列表頁面返回的默認查詢集。

如清單 14 所示,重寫 queryset() 以便將清單限制在當前用戶建立的這些 Document 中。超級用戶能夠看到全部文檔。

清單 14. 重寫列表頁面返回的查詢集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.contrib import admin
from more_with_admin.examples import models
 
class DocumentAdmin(admin.ModelAdmin):
    
     def queryset(self, request):
         qs = super(DocumentAdmin, self).queryset(request)
 
         # If super-user, show all comments
         if request.user.is_superuser:
             return qs
         
         return qs.filter(added_by=request.user)
[...]

如今,任何對 admin 中 Document 列表頁面的查詢只顯示當前用戶建立的文檔(除非當前用戶是高級用戶,這種狀況下將顯示全部文檔)。

固然,只要知道 ID,用戶就能夠訪問編輯頁面查看未受權的文檔,當前對此沒有任何阻止手段。真正安全的低級權限須要重寫更多方法。由於 admin 用戶一般都達到某種水平的信任,因此有時一些基本的權限便足以提供精簡的工做流了。

結束語

定製 Django admin 不須要 admin 源代碼知識,也不須要修改源代碼。admin 可使用普通的 Python 繼承和一些 Django 特有功能(好比 signals)進行擴展。

經過建立全新管理界面定製 admin 的優勢不少:

  • 保證活動開發持續進行的同時,您的應用程序能夠從 Django 的優點中受益。
  • admin 已經支持大部分通用用例。
  • 添加到項目的外部應用程序能夠自動與代碼並排管理。
相關文章
相關標籤/搜索