如何使用Bootstrap重新设计Django Admin界面

Django的管理后台确实很棒——功能全面、操作简便、设计安全、稳定可靠……但它的外观略显丑陋,当你希望将其与网站其他部分的风格融为一体时,这可能是个缺点。让我们来解决这个问题。

如果它没坏……

The default Django admin. (Source)

假设你刚用Django和Vue.js原型化了一个Web应用。在很多情况下,直接使用Django的admin进行后台管理,甚至在适当设置权限后将其移交给客户,是完全可行的。毕竟,它运行良好,并且可以使用内置工具进行深度定制以适应多种场景。

那么,为何还要费心呢?

改变管理后台外观的理由

然而,有多个合理的理由促使我们进一步整合其外观:

  • 品牌化:希望使用自己公司的名称和颜色而非“Django管理”并无不妥(顺便提一下,这符合Django的BSD许可证规定)。
  • 主站点与管理后台的无缝集成:您可能希望在浏览网站的同时,能够通过一个共用的导航栏在后台功能间流畅切换。
  • 美化界面:尽管管理后台看起来尚可,自版本2起已采用响应式网页设计原则(在移动端和桌面端均表现良好),但一个精心设计的样式表仍能大幅提升其外观。
  • 功能跳过:您或许还想为管理后台定制下拉菜单,仅展示实际使用的选项,并从用户界面中隐藏不常用的功能,从而优化用户体验

A Practical Example

以本例为例,避免重复,我们将继续开发之前为《使用Django和Vue.js原型化Web应用》文章所启动的简易发布Web应用。

简而言之:

  • a Django app with two models:
  • 文章包含字段名称作者(关联)、内容别名
  • 作者:包含字段名称别名
  • A single view called frontend that queries all registries in both models.
  • A single template called template.
  • 在本节中,我们将实现一个响应式且可扩展的界面,结合使用Vue.jsVue 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. 我们清空了 `branding` 和 `breadcrumbs` 块的内容,因为我们并不真正需要它们。
  4. 我们使用 `bodyclass` 块来设置Bootstrap的 `bg-light`,正如我们在 `frontend` 模板中所做的那样。
  5. 我们使用 `extrastyle` 块来嵌入Bootstrap及一些CSS代码。
    a. 好的,`#header, .breadcrumbs { display: none; }` 是对第3点的某种重申;但了解可以通过两种方式禁用品牌和面包屑部分是很有用的。
    b. 当Bootstrap与Django管理后台的CSS重叠时,可能会出现一些问题,因此这里提供了一些修复。
  6. 使用 `bodyheader` 和 `bodyfooter` 块来包裹管理内容。

现在我们能够访问管理后台模板,我们可以进一步定制其样式表,或者仅保持与主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 }}标签是为了告诉模板引擎,我们并非要覆盖extrastyle的内容(该内容在templates/admin/base_site.html模板中定义),而是简单地向其追加内容(更多信息请参见模板继承)。
  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管理界面的外观,但平滑整合网站不同端点确实能提升用户体验,因为它能实现两者间的无缝切换,甚至对管理界面导航进行更精细的控制。

而且这样做并不难。关键在于如何包装管理界面,以及如何将第三方库与自定义的JavaScript代码和样式表融合。幸运的是,你可以轻松地将某些元素整合到管理界面、主站其他部分或两者之中。

希望你已经获得了关于如何更深层次定制Django的灵感,这些方法或许之前并不那么显而易见!

如果你需要一个理由来构建一个网络应用,只为探索Django管理界面的可能性,不妨查看上周关于使用Django和Vue.js进行Web应用原型设计的教程——乐趣无穷。若想进一步提升Django技能,SitePoint Premium图书馆有大量资源等你发掘。

关于使用Bootstrap自定义Django管理界面的常见问题(FAQs)

自定义Django Admin使用Bootstrap有何益处?

利用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根据您的模型定义和admin选项自动生成数据库查询。但这些查询有时可能效率不高,尤其是对于复杂数据结构或大型数据集。通过定制admin选项并利用Django的数据库优化特性,您可以大幅减少数据库查询数量,从而提升管理界面的性能。另一种方法是使用缓存。Django提供了一个强大的缓存框架,用于缓存昂贵操作的结果或频繁访问的数据。通过缓存,您可以减轻数据库的负担,提高管理界面的响应速度。

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