Django筆記三十五之admin後臺介面介紹

2023-05-05 06:00:54

本文首發於公眾號:Hunter後端

原文連結:Django筆記三十五之admin後臺介面介紹

這一篇介紹一下 Django 的後臺介面使用。

Django 自帶了一套後臺管理介面,可用於我們直接運算元據庫資料,本篇筆記目錄如下:

  1. 建立後臺賬號以及登入操作
  2. 註冊後臺顯示的資料表
  3. 列表欄位的顯示操作
  4. 欄位值的修改操作
  5. 列表頁的執行操作

1、建立後臺賬號以及登入操作

首先我們需要建立一個可以存取後臺的賬號,以下命令在系統的根目錄下進行:

python3 manage.py createsuperuser

然後他會提示我們輸入賬號的名稱,郵箱以及兩遍密碼用於確認。

Username (leave blank to use 'hunter'): admin
Email address: [email protected]
Password: 
Password (again): 

在這個過程中,如果我們輸入的密碼少於8位元或者過於簡單,他會給我們提示說密碼過於簡單等,可以設定複雜點的,也可以直接確認。

建立好賬號密碼後,執行我們的系統:

python3 manage.py runserver 0:9898

然後就可以在瀏覽器裡存取我們的後臺系統了:

http://localhost:9898/admin

在下面的圖裡輸入賬號密碼就可以進入系統了:

這裡需要注意一點的是,如果你是按照我們的筆記一路操作過來,在前面我們的使用者登入限制裡可能限制了 login 和 register 介面才允許不登入,那麼我們在相應的驗證中介軟體裡可以簡單做一下操作:

class AuthMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        path = request.path

        # url 路徑為 /users/register 和 /users/login 的介面不需要進行判斷驗證
        if path not in [
            "/users/register",
            "/users/login",
        ]:
            if path.startswith("/admin"):
                return self.get_response(request)
            session = request.session
            if not session.get("username"):
                return JsonResponse({"code": -1, "msg": "not login"}, status=401)
        response = self.get_response(request)
        return response

這裡我們將 /admin 開頭的介面都設定為了不需要登入驗證。

2、註冊後臺顯示的資料表

輸入賬號密碼登入後臺系統後,如果之前沒有做過後臺註冊的相關操作的話,可能只會看到 Django 系統自帶的 Users 和 Groups 表,它們是預設在後臺顯示的。

如果你點選進入 Users 表,可以看到我們剛剛建立的這個用於登入的管理員賬號 admin。

如果我們想要使得我們建立的其他表也在後臺顯示,則需要手動去註冊一下。

註冊的流程很簡單,我們這裡以 blog 這個 application 為例,修改 blog/admin.py,對於需要顯示的表,我們直接使用 admin.site.register() 操作,即可在後臺顯示,比如:

# blog/admin.py
from django.contrib import admin
from blog.models import Blog

admin.site.register(Blog)

重啟系統後,就可以看到多了一個 Blog 的 application 以及其下的 Blog 表了。

再點選進入表,可以看到一條條 Blog 表裡的資料了,但是表的欄位在列表頁都是被隱藏的,只有在點選進入單條資料詳情頁時,才會顯示具體的欄位值。

3、列表欄位的顯示操作

前面介紹瞭如何註冊一個 model,使其在後臺介面顯示,但是有一個問題就是資料列表每條都只有一個欄位表示,而沒有每個欄位的具體值顯示,接下來我們就對具體欄位值的顯示做介紹。

首先,我們使用的 model 定義如下:

class Blog(models.Model):
    PUBLISHED = 1
    UNPUBLISHED = 0
    PUBLISHED_STATUS = (
        (PUBLISHED, "published"),
        (UNPUBLISHED, "not_published"),
    )

    name = models.CharField(max_length=100, unique=True)
    tag_line = models.TextField()
    char_count = models.IntegerField(verbose_name="文章字數", default=0)
    is_published = models.BooleanField(choices=PUBLISHED_STATUS, default=UNPUBLISHED)
    pub_datetime = models.DateTimeField(verbose_name="釋出日期", null=True, default=None)

定義好後的 migration 相關操作可以自己去完成。

註冊操作

前面介紹了 model 在後臺顯示的註冊操作:

# blog/admin.py
from django.contrib import admin
from blog.models import Blog

admin.site.register(Blog)

但是這種操作只能在後臺顯示列表資訊,具體的欄位資訊不會在列表顯示,如果要實現這種操作,我們需要進行另一種註冊方式:

# blog/admin.py

from django.contrib import admin
from blog.models import Blog


class BlogAdmin(admin.ModelAdmin):
    pass


admin.site.register(Blog, BlogAdmin)

接下來,我們的具體操作都會在 BlogAdmin 中實現。

列表顯示欄位

如果我們想要在列表中就顯示資料的具體欄位,比如 id, name,is_published,pub_datetime 三個欄位,我們可以使用 list_display 屬性:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "pub_datetime")

重啟系統後,就可以在列表看到相應的欄位顯示了:

注意:在這裡,is_published 這裡直接顯示了該欄位的用於顯示的值,這個和之前介紹的 get_field_display() 的方式是一致的。

可以看到,這個列表的表頭就是我們定義的 model 裡的 verbose_name 的值,如果沒有定義該屬性,則會直接顯示欄位名。

資料格式化顯示

可以注意到日期的顯示並不利於直觀的檢視,所以可以對日期欄位做一個格式化處理返回顯示。

以下是對 model 的處理:

# blog/models.py

from django.contrib import admin

class Blog(models.Model):
    # 欄位在這裡省略
    pass
    
    @admin.display(description="釋出時間")
    def format_pub_datetime(self):
        return self.pub_datetime.strftime("%Y-%m-%d %H:%M:%S")

以下是對 BlogAdmin 的處理:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "format_pub_datetime")

這樣在列表頁就會多一列,表頭為【釋出時間】。

這裡我們其實可以這樣理解,我們對 Blog 這個 model 新增了一個名為 format_pub_datetime 的屬性,這個屬性用 admin.display() 作為裝飾器,這個屬性可以作為被 admin 的 list_display 使用作為列表的展示項。

在這裡我們是直接對 pub_datetime 欄位做格式化處理,當然也可以對非時間欄位做其他的處理,主要看想要實現的效果。

注意: 這裡還有需要注意的一點是,pub_datetime 欄位是允許為 null 的,所以這裡最好是做一下適配處理,比如說 return 的時候判斷一下:

    @admin.display(description="釋出時間")
    def format_pub_datetime(self):
        return self.pub_datetime.strftime("%Y-%m-%d %H:%M:%S") if self.pub_datetime else ""

上面這種方式是在 model 下定義的函數,我們也可以直接在 BlogAdmin 定義該函數操作:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "char_count", "format_pub_datetime")

    @admin.display(description="釋出時間")
    def format_pub_datetime(self, obj):
        return obj.pub_datetime.strftime("%Y-%m-%d %H:%M:%S") if obj.pub_datetime else ""

列表資料排序

列表的資料預設是按照 id 的倒序排列返回的,如果想要按照其他欄位排序返回,比如 char_count,可以使用 ordering 屬性:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "char_count", "format_pub_datetime")
    ordering = ("char_count",)

如果是想要倒序排列,欄位前加一個 - 即可:

ordering = ("-char_count",)

4、欄位值的修改操作

如果我們想要在 admin 的頁面修改資料的欄位值,目前我們能做的操作就是點選每條資料前面的 id,他會進入這條資料的詳情頁,每個欄位都是預設可修改的。

指定欄位點選進入編輯頁

我們也可以指定某個,或者某幾個欄位進入這條資料的詳情頁進行編輯,用到的屬性是 list_display_links,這個屬性的值預設是 id 主鍵欄位,但如果我們想點選 id 和 name 欄位的時候都進入詳情頁,可以如下操作:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "format_pub_datetime")
    list_display_links = ("id", "name",)

直接在列表頁面修改欄位值

如果某個欄位是需要經常修改的,我們想要在列表頁面就修改而不用進入資料的詳情頁,可以加上 list_editable 屬性,比如我們直接在列表頁修改 name 欄位的值:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "format_pub_datetime")
    list_editable = ("name", )

需要注意的是,list_editable 和 list_display_links 這兩個屬性是相斥的,也就是說這兩個的欄位列表不能擁有同一個欄位值。

詳情頁欄位的顯示與否

點選進入資料的編輯詳情頁,預設所有欄位是可以修改的,如果想要某些欄位在詳情頁顯示或者不顯示,可以使用 fields 和 exclude 屬性,分別表示顯示和不顯示的欄位。

顯示 name 和 is_published 欄位:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "format_pub_datetime")
    fields = ("name", "is_published")

隱藏 name 和 is_published 欄位:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "format_pub_datetime")
    exclude = ("name", "is_published")

欄位僅可讀

有一些比較重要的欄位,如果在詳情頁不希望能夠被修改,可以使用 readonly_fields 屬性,比如不希望 name 欄位被修改:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "format_pub_datetime")
    readonly_fields = ("name",)

重啟系統後,就可以在列表頁看到 name 欄位沒有修改框,為僅可讀狀態了。

注意: 需要提醒的一點是,上面所有的新增欄位屬性的操作的值都需要是列表或者元組。

save_as 屬性

save_as,這是一個在資料詳情頁儲存時的屬性,目前進入資料的詳情頁,右下角有三個按鈕,左邊的第一個是 save_and_add_another,意思是儲存操作之後會自動進入新的頁面,可用於建立資料。

當我們設定 save_as=True 之後:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "char_count", "format_pub_datetime")
    save_as = True

再看這三個按鈕,第一個按鈕就會變成 save as new,意思是在原資料上修改了之後,點選可以儲存為一條新資料,原資料保持不變。

這個操作過程可以理解成我們前面一篇筆記中介紹如何新建一條資料的方式,就是在原資料上將 id 欄位變成 None 然後進行 save 操作,就會在原資料的基礎上建立一條新資料。

這個過程可以去看 Django筆記的第十八篇中自增的主鍵那一段。

5、列表頁的執行操作

在列表頁,目前僅有一個可供執行的操作,那就是選中 id 那一欄的資料之後,點選 action 旁邊的下拉框,有一個 delete 操作,意思是刪除選中的資料:

快速搜尋過濾操作

對於某些值的種類比較少的資料,比如 is_published 欄位,或者日期欄位,想要實現快速搜尋的操作,可以使用 list_filter 屬性:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "char_count", "format_pub_datetime")
    list_filter = ("is_published", "pub_datetime")

重啟系統後可以看到列表頁面右側出現了一個篩選列表:

對於非日期欄位,這裡有一些固定的日期的選擇,對於其他欄位,則是會列出所有欄位值作為篩選項。

指定欄位搜尋

如果想要對某個或者某幾個欄位進行模糊搜尋,可以使用 search_fields 屬性,比如想要搜尋 name 和 pub_datetime 欄位:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "char_count", "format_pub_datetime")
    search_fields = ("name", "pub_datetime")

重啟系統後,頁面的左上角會出現一個搜尋方塊,比如我們搜尋 python,就會去查詢 name 和 pub_datetime 欄位中包含 python 的資料。

如果我們搜尋的時候使用空格將搜尋的關鍵字分隔開,那麼系統會自動為我們進行 split() 操作,然後搜尋,比如這裡我們搜尋的是 python 2021,那麼系統轉化的 sql 就會是:

where (name like '%python%' or pub_datetime like '%python%') and (name like '%2021%' or pub_datetime like '%2021%')

如果希望搜尋的內容是一個整體,可以使用單引號或者雙引號括起來 'python 2021'

日期分級篩選

前面介紹了一個快速搜尋過濾的操作,這裡針對於日期欄位介紹一下另一種篩選過濾的方式,比如我們對 pub_datetime 欄位進行操作:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "char_count", "format_pub_datetime")
    date_hierarchy = "pub_datetime"

重啟系統後,可以在頁面左上角看到 2021,2022,2023 幾個年份,都是根據當前表裡的資料統計出來的結果,然後點選進入相當於是進行了一次年份的篩選,在第二層頁面繼續點選選擇則是一次新的篩選:

save_model()

前面在 Django筆記第十八篇中有介紹過一條資料的儲存 save() 操作的繼承處理,我們可以通過自定義一些邏輯使得資料在儲存前進行一些操作,在這裡,save_model() 的操作也可以提供同樣的功能:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "char_count", "format_pub_datetime")

    def save_model(self, request, obj, form, change):
        print("do something")
        super().save_model(request, obj, form, change)

actions 自定義執行任務

假設說我們想要實現一些批次執行的操作,比如選中列表頁某些資料,將 is_published 欄位批次更新成 True(即已釋出),我們就可以用到 actions 來實現。

目前在頁面的左上角有一個 action 和旁邊的下拉框,系統實現了一個預設的函數邏輯,即刪除選中項,我們可以來實現一個更新選中項的功能:

class BlogAdmin(admin.ModelAdmin):
    list_display = ("id", "name", "is_published", "char_count", "format_pub_datetime")
    
    actions = ["make_published"]

    @admin.action(description="make queryset published")
    def make_published(self, request, queryset):
        queryset.update(is_published=True)

使用 admin.action 作為裝飾器來裝飾一個函數,然後將函數名稱作為值放入 actions 列表中,在這裡 queryset 引數即為頁面選中的資料,它是一個 queryset 型別,所以這裡可以直接進行 update() 操作。

如果想執行一些更深入的操作,我們也可以對 request 引數進行操作解析,它即為我們前端選中執行傳過來的請求。

重啟系統後,重新整理頁面,點開 action 旁邊的下拉框,就可以看到我們定義的函數了,選中資料,點選旁邊的 Go 按鈕即可執行。

可以定義多個執行函數,記得新增到 actions 列表進行註冊即可。

如果想獲取更多後端相關文章,可掃碼關注閱讀: