如何使用 Bootstrap 重新設計 Django Admin

Django 管理站點非常出色——功能齊全、易於使用、設計安全、穩如磐石…但有些醜陋,當您希望將其與您網站其餘部分的風格整合時,這可能是一個缺點。讓我們來解決這個問題。

如果沒壞…

The default Django admin. (Source)

假設您剛剛使用 Django 和 Vue.js 原型化了一個網絡應用。對於許多情況,直接使用 Django 的後台管理進行後台操作,甚至在適當設置權限後將其交給客戶,都是完全可以的。畢竟,它運行得非常順暢,並且可以使用內置工具進行大量定制以應對多種情況。

那麼,為什麼還要費心呢?

改變管理站點外觀的理由

然而,有幾個合理的理由需要更進一步地整合:

  • 品牌化:希望使用您公司的名稱和顏色而不是“Django 管理”並無不妥(順便說一句,這符合 Django 的BSD 許可證)。
  • 主站與管理後台的無縫整合:你可能希望在瀏覽網站時能夠順暢切換至後台功能,反之亦然,透過共用一個導航欄來實現。
  • 美化介面:雖然管理後台看起來已經不錯,並且自從v2版本以來就已經採用了響應式網頁設計原則(無論在手機或桌面上都表現良好),但一個精心設計的樣式表還是可以大幅提升其外觀。
  • 功能繞過:你也許還想為管理後台創建自定義的下拉菜單,僅展示你實際使用的選項,並隱藏那些不常用的選項於用戶界面之外,這樣可以提供更佳的用戶體驗

A Practical Example

以本例來說,為避免重複,我們將繼續開發之前在使用Django和Vue.js原型化網路應用文章中啟動的簡單出版網路應用。

簡而言之:

  • a Django app with two models:
  • 文章具有名稱作者(關聯)、內容slug字段
  • 作者:具有名稱slug字段
  • A single view called frontend that queries all registries in both models.
  • A single template called template.
  • 使用Vue.js配合Vue RouterVuex打造響應式可擴展介面。

在本節中,我們將不特別關注Vue.js的整合,亦不在此處進行修改。

基本模板

Source

利用Django模板,其功能強大且靈活,可於應用層級(Django網站的一部分)或網站層級建立,甚至能覆蓋Django自帶的模板(正如我們將在此處所做的)。

Source

我們建立了一個基本模板,連結至Bootstrap的JavaScript及樣式表,並包含其輔助工具jQueryPopper

以下是我們用於主站點的基礎模板,與通常用於任何其他Django站點的模板無異:

<!doctype html>
<html lang="en">
  <head>
    <!-- 必須的元標籤 -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">

    <title>Django and Vue.js</title>
  </head>
  <body class="bg-light">
    <div class="bg-white container">
      <h1>Prototyping a Web App with Django and Vue.js</h1>

      <!-- 內容 -->
    </div>

    <!-- Vue.js -->
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vue-router"></script>

    <!-- 首先jQuery,然後Popper.js,然後Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  </body>
</html>

接下來,我們將此整合到管理後台,並在主站點和後台辦公室兩端添加一個共享的導航欄!

將主UI模板與管理後台整合

如前所述,我們可以覆蓋模板,包括管理後台的模板。然而,由於Django的設計,不出所料,主站點和後台辦公室是兩個不同的系統,每個系統都有其自己的模板、樣式表和contrib 套件。因此,即使它們將幾乎相同,我們還是需要維護兩個不同的模板——一個用於主UI,另一個用於管理後台。

在一般情況下啟用模板目錄

首先,我們需要告訴Django我們將在基礎目錄中存儲修改後的管理模板。

因此,我們需要編輯myproject/settings.py。首先,找到TEMPLATES常數和這個DIRS鍵:

'DIRS': [],

將該代碼更改為以下內容:

'DIRS': [os.path.join(BASE_DIR, 'templates')],

包裝管理模板(admin/base 修改技巧)

如果我們只想進行外觀上的變更,例如傳遞一個自定義的樣式表給管理界面,或是移除/替換其標頭,我們可以通過編輯 admin/base_site 模板來實現,而不需要進行當前步驟。然而,如果我們想要更進一步,將管理部分“包裹”起來,使其如同包含在我們的主站點中,並能夠擁有共同的標頭和頁尾,那麼請繼續閱讀。

我們需要將 Django 的 admin/base.html 複製到我們的模板目錄中,即 templates/admin/base.html,以便我們可以放置包裝元素。

我們將修改代碼中圍繞 container 部分的內容,從原本的:


<div id="container">
(...)
</div>

改為:

{% block bodyheader %}{% endblock %}


<div id="container">
(...)
</div>


{% block bodyfooter %}{% endblock %}

這樣就完成了!我們簡單地創建了 bodyheaderbodyfooter 區塊標籤,以便在下一步注入將管理界面包裹起來的代碼。

編寫自定義管理模板(admin/base_site 修改技巧)

接著,我們將在templates/admin/base_site.html中編寫實際的模板(我們需要在項目根目錄創建這些目錄):

{% extends "admin/base_site.html" %}

{% block title %}Django with Bootstrap | Admin site{% endblock %}

{% block branding %}{% endblock %}
{% block breadcrumbs %}{% endblock %}

{% block bodyclass %}bg-light{% endblock %}

{% block extrastyle %}
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <style>
      #header, .breadcrumbs { display: none; }

      /* 管理員後台的Bootstrap問題 */
      * { box-sizing: unset; }
      div.module caption { caption-side: top !important; }
      .collapse { display: block !important; }
    </style>
{% endblock %}

{% block bodyheader %}
    <div class="bg-white container">

      <div class="jumbotron">
        <h1 class="display-4">Hacking the Django Admin with Bootstrap</h1>
        <p class="lead">
          The <a ref="https://docs.djangoproject.com/en/dev/ref/contrib/admin/">Django administration site</a> is great—full-featured, easy to use, secure by design, rock solid… and somewhat ugly, which can be something of a downside when you want to integrate it with the look-and-feel of the rest of the website. Let’s sort that out.
        </p>
      </div>
{% endblock %}

{% block bodyfooter %}
    </div>

    <!-- 首先jQuery,然後Popper.js,最後Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
{% endblock %}

分解說明

讓我們試著解釋一下這裡我們在做什麼:

  1. 我們告訴模板引擎,我們正在“擴展”admin/base_site.html模板,以有效地覆蓋其部分定義。
  2. 我們利用title區塊來自定義正在瀏覽的管理頁面的標題。
  3. 我們清空了brandingbreadcrumbs區塊的內容,因為我們實際上不需要它們。
  4. 我們使用bodyclass區塊來設置Bootstrap的bg-light,正如我們在前端模板中所做的那樣。
  5. 我們使用extrastyle區塊來嵌入Bootstrap和一些CSS代碼。
    a. 好的,#header, .breadcrumbs { display: none; }在某種程度上是對第3點的重述;但知道你可以通過兩種方式禁用品牌和導覽列部分是很有用的。
    b. 當Bootstrap與Django管理後台的CSS重疊時,可能會出現一些問題,所以這裡有一些修復方法。
  6. 使用bodyheaderbodyfooter區塊來包裹管理內容。

現在我們可以訪問管理模板,我們可以進一步修改其樣式表,或者僅僅讓它與主UI共享樣式。

注意事項

我們維護了兩個不同的模板(主UI和管理員)來進行本質上相同的展示。誠然,這並非理想做法,因為我們明確違反了軟件開發的一條原則:不要重複自己(DRY)。

正如我們所討論的,這是因為Django管理界面被設計為與主UI分離。這本身並無不妥,正如跳出框架思考也無可厚非。但確實,這迫使我們使用兩個內容幾乎相同的模板。

實際上,理論上我們可以設計一個包含主UI和管理界面共用元素(如導航欄)的模板模式,並從單一來源重用它們;但考慮到本文的目的,這種做法可能稍顯過度。無論如何,我已將這個想法留給你們,供日後參考。😉

創建共享導航欄

既然主UI和管理網站的外觀已經非常相似,我們可以進一步整合,打造統一的導航體驗……甚至更進一步,將某些管理選項直接呈現在主菜單上!

以下是導航欄的代碼片段:

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <ul class="navbar-nav mr-auto">
    <li class="nav-item">
      <a
        class="nav-link text-primary"
        href="/author/"
      >
        Go to Authors
      </a>
    </li>
    <li class="nav-item">
      <a
        class="nav-link text-primary"
        href="/article/"
      >
        Go to Articles
      </a>
    </li>
    {% if user.is_authenticated %}
    <li class="nav-item dropdown">
      <a
        aria-expanded="false"
        aria-haspopup="true"
        class="font-weight-bold nav-link text-primary dropdown-toggle"
        data-toggle="dropdown"
        href="#"
        role="button"
      >
        Admin
      </a>
      <div class="dropdown-menu">
        <a class="dropdown-item" href="/admin/myapp/author/">
          Manage authors
        </a>
        <a class="dropdown-item" href="/admin/myapp/article/">
          Manage articles
        </a>
      </div>
    </li>
    {% endif %}
  </ul>
</nav>

注意dropdown-menu部分,它將負責展示管理菜單(更多資訊請參閱Bootstrap的Navbar組件)。

我們還進行了一個條件檢查,使用{% if user.is_authenticated %} /{% endif %}來決定是否顯示管理員菜單。

最後,請記住,由於我們現在維護兩個不同的主要模板,我們需要將導航欄的HTML代碼添加到兩者中,myapp/templates/myapp/template.html 以及 templates/admin/base_site.html

額外:管理員登錄屏幕

管理站點已經處理好了,還有一個懸而未決的問題:登錄屏幕。

現在我們可以將這樣的內容:

Source

…轉變成這樣的內容:

通過在templates/admin/login.html中創建以下模板,我們可以實現更接近這一目標的效果:

{% extends "admin/login.html" %}

{% load i18n static %}

{% block extrastyle %}
{{ block.super }}
<style>
#header {
  background-color: transparent !important;
}
</style>
{% endblock %}

{% block branding %}
<h1>
  <span style="color: #57C5A5 !important">ActionPlanNow.com</span>
  <br />
  <small>{% block head_title %}{% endblock %}</small>
</h1>
{% endblock %}

{% block content_title %}
<p class="lead" style="font-size: larger">
A Simple Tool for Leaders, Coaches, and Counselors.
</p>
{% endblock %}

分解

我們在這裡所做的是:

  1. {{ block.super }}標籤的存在是為了告訴模板引擎,我們並沒有覆蓋在templates/admin/base_site.html模板中定義的extrastyle內容,而是僅僅將內容附加到它上面(有關模板繼承的更多信息,請參閱)。
  2. branding塊允許我們將“Django管理”標題更改為更有趣的內容。
  3. 我們通過設置一個空定義來消除head_title塊。
  4. 我們使用content_title區塊來添加一些額外信息。

一些注意事項

Source

就像Bootstrap一樣,Django管理站點也自帶了一套jQuery,但幸運的是,Django開發者考慮到了這一點,為了避免與用戶提供的腳本和庫衝突,Django的jQuery被命名空間化為django.jQuery。因此,我們可以安全地包含自己的副本(正如我們所做的)。

在主樣式表中大量使用類定義時要小心,因為這也會影響管理站點,以意想不到的方式影響其功能。在這種情況下,您可以始終使用瀏覽器調試工具查看正在發生的事情,例如Chrome DevToolsFirefox Developer Tools(特別是Page Inspector)或Safari Developer Tools

演示和完整代碼

我們在此討論的實現將如下所示:

您可以在我的GitHub倉庫中查看整個項目代碼,luzdealba / djangovuejs

總結

儘管有些人可能合理地認為沒有太多必要改變Django的管理介面外觀,但平滑整合網站不同端點以提升使用者體驗(UX)確實是一個不錯的技巧,因為它能提供兩者間無縫的轉換,甚至更有效地控制管理介面的導航。

而這樣做其實並不困難。你需要注意的是如何包裝管理介面,以及如何將第三方庫與你自己的JavaScript程式碼和樣式表混合。幸運的是,你可以很容易地將某些元素整合到管理介面中,某些整合到主站點其餘部分,甚至有些可以同時整合到兩者中。

希望你已經有了一些關於如何進一步自定義Django的想法,這些想法可能之前並不明顯!

如果你需要一個藉口來建立一個網路應用程式,只是為了玩弄Django的管理介面,可以查看上週關於使用Django和Vue.js原型化網路應用程式的教程——非常有趣。如果你想進一步提升你的Django技能,SitePoint Premium圖書館有大量的資源供你使用。

關於使用Bootstrap自定義Django管理介面的常見問題(FAQs)

使用Bootstrap自定義Django管理介面的好處是什麼?

使用Bootstrap自定義Django Admin帶來多項益處。首先,它能提升管理界面的視覺吸引力,使其更加用戶友好且直觀。Bootstrap作為一個流行的前端框架,提供了多種設計模板,涵蓋了排版、表單、按鈕及其他界面元素。通過將其與Django Admin整合,您可以利用這些模板打造一個更美觀且功能強大的管理界面。其次,它允許您向管理界面添加自定義功能。例如,您可以加入自定義操作、過濾器及表單,以增強管理界面的實用性。最後,它能提升管理界面的響應性,使其更適用於不同設備和屏幕尺寸。

如何在Django Admin中添加自定義操作?

Django Admin允許您添加可在選定對象上執行的自定義操作。要添加自定義操作,您需定義一個函數,該函數對選定對象執行所需操作。此函數應接受三個參數:模型管理器、請求及選定對象的查詢集。定義好此函數後,您可以將其添加至模型管理器的‘actions’屬性中。這樣一來,該操作便會出現在管理更改列表頁面的操作下拉菜單中。

我能否使用Bootstrap來自定義Django Admin的外觀和感覺?

是的,您可以使用Bootstrap自定義Django Admin的外觀和感覺。Bootstrap是一個前端框架,提供各種設計模板,用於排版、表單、按鈕和其他界面組件。通過將其與Django Admin整合,您可以利用這些模板創建一個更美觀且功能更強大的管理界面。您可以自定義管理界面的顏色、字體、佈局和其他設計元素,以匹配您的品牌身份或個人喜好。

如何在Django Admin中添加自定義過濾器?

Django Admin允許您添加自定義過濾器,用於過濾管理變更列表頁面上顯示的對象。要添加自定義過濾器,您需要定義一個django.contrib.admin.SimpleListFilter的子類。該子類應定義兩個方法:lookups和queryset。lookups方法應返回一個元組列表,每個元組代表一個過濾選項。queryset方法應根據所選的過濾選項返回一個過濾後的查詢集。定義此子類後,您可以將其添加到模型管理器的’list_filter’屬性中。

我可以在不使用任何額外包的情況下將Bootstrap與Django Admin一起使用嗎?

雖然在不使用任何額外包的情況下將Bootstrap與Django Admin一起使用是可能的,但通常使用像django-admin-bootstrap這樣的包會更容易且更有效率。該包為Django Admin提供了一個基於Bootstrap的主題,使其更容易整合Bootstrap。它還提供了額外的功能,如響應式設計和自定義表單渲染,這可以進一步增強管理界面的可用性和功能。

如何在Django Admin中自定义表单字段?

Django Admin允许您自定义用于创建或编辑对象的表单字段。要自定义表单字段,您需要覆盖模型管理中的‘formfield_for_dbfield’方法。此方法应返回一个表单字段实例,该实例将用于指定的数据库字段。您可以根据需要自定义表单字段的属性、部件和验证行为。

我可以在Django Admin中添加自定义视图吗?

是的,您可以在Django Admin中添加自定义视图。要添加自定义视图,您需要在模型管理中定义一个处理视图逻辑的方法。此方法应仅接受请求作为其唯一参数并返回响应。然后,您可以通过向模型管理的‘get_urls’方法添加URL模式,将此方法映射到URL。这将使视图可通过管理界面访问。

如何自定义Django Admin中的列表显示?

Django Admin允许您自定义列表显示,即在管理更改列表页面上显示的对象表。要自定义列表显示,您可以将模型管理的‘list_display’属性设置为要显示的字段名称列表。您还可以在此列表中包含方法名称,这将调用每个对象上的相应方法并显示结果。

我能否使用Django Admin处理复杂的数据库模型?

是的,Django Admin 專為處理複雜數據庫模型而設計。它提供了一系列功能,幫助您管理複雜的數據結構,如相關對象的內聯編輯、自定義表單字段及自定義操作。然而,對於極為複雜的數據結構或高級數據庫操作,您可能需要通過自定義視圖、表單或操作來擴展 Django Admin。

如何提升 Django Admin 的性能?

提升 Django Admin 性能的方法有多種。一種方式是優化數據庫查詢。Django Admin 根據您的模型定義和後台選項自動生成數據庫查詢。但這些查詢有時可能效率不高,尤其是在處理複雜數據結構或大型數據集時。通過定制後台選項並利用 Django 的數據庫優化功能,您可以大幅減少數據庫查詢數量,從而提升後台界面的性能。另一種方法是使用緩存。Django 提供了一個強大的緩存框架,可用於緩存昂貴操作的結果或頻繁訪問的數據。利用緩存,您可以減輕數據庫的負擔,增強後台界面的響應性。

Source:
https://www.sitepoint.com/how-to-hack-redesign-customize-the-django-admin-with-bootstrap/