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


利用 Django admin 完成更多任務

 

 

Django admin

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

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

在現實中,總需要執行某些定制操作。關於 admin 外觀的基本情況,Django 文檔提供許多指南,Django 自身也包含了一些簡單的方法用來改寫 admin 行為的子集。如果您需要更多功能怎么辦呢?從哪里開始着手呢?本文將指導您如何進行一些高級 adimin 定制。

admin 快速瀏覽

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

清單 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 安裝的位置定制,以便更輕松的復制和引用:

$ 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 已經支持大部分通用用例。
  • 添加到項目的外部應用程序可以自動與代碼並排管理。

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM