《Django》入门 Notes(下)

@Seymour0314 来源官方文档https://docs.djangoproject.com/zh-hans/4.2/intro/

本文是Django 后端技术初级入门教程。

编程语言:Python

第 6 部分

  • 本教程从 教程第 5 部分结束的地方开始。

    我们已经建立了一个经过测试的网络投票应用程序,现在我们将添加一个样式表和一个图像。

  • 除了服务端生成的 HTML 以外,网络应用通常需要一些额外的文件——比如图片,脚本和样式表——来帮助渲染网络页面。

    在 Django 中,我们把这些文件统称为“静态文件”。

  • 对于小项目来说,这个问题没什么大不了的,因为你可以把这些静态文件随便放在哪,只要服务程序能够找到它们就行。然而在大项目——特别是由好几个应用组成的大项目——中,处理不同应用所需要的静态文件的工作就显得有点麻烦了。

  • 这就是 django.contrib.staticfiles 存在的意义:

    它将各个应用的静态文件(和一些你指明的目录里的文件)统一收集起来,

    这样一来,在生产环境中,这些文件就会集中在一个便于分发的地方。

6.1 自定义 应用 的界面和风格

  • 首先,在你的 polls 目录下创建一个名为 static 的目录。

    • Django 将在该目录下查找静态文件,这种方式和 Diango 在 polls/templates/ 目录下查找 template 的方式类似。

    • Django 的 STATICFILES_FINDERS 设置包含了一系列的查找器,它们知道去哪里找到 static 文件。

      • AppDirectoriesFinder 是默认查找器中的一个,

        它会在每个 INSTALLED_APPS 中指定的应用的子文件中寻找名称为 static 的特定文件夹,

        就像我们在 polls 中刚创建的那个一样。管理后台采用相同的目录结构管理它的静态文件。

  • 在你刚创建的 static 文件夹中创建一个名为 polls 的文件夹,再在 polls 文件夹中创建一个名为 style.css 的文件。

    换句话说,你的样式表路径应是 polls/static/polls/style.css

    因为 AppDirectoriesFinder 的存在,

    你可以在 Django 中以 polls/style.css 的形式引用此文件,类似你引用模板路径的方式。

    • 静态文件命名空间

    虽然我们 可以 像管理模板文件一样,把 static 文件直接放入 polls/static (而不是创建另一个名为 polls 的子文件夹),不过这实际上是一个很蠢的做法。Django 只会使用第一个找到的静态文件。如果你在 其它 应用中有一个相同名字的静态文件,Django 将无法区分它们。我们需要指引 Django 选择正确的静态文件,而最好的方式就是把它们放入各自的 命名空间 。也就是把这些静态文件放入 另一个 与应用名相同的目录中。

  • 将以下代码放入样式表:

    polls/static/polls/style.css

    1
    2
    3
    li a {
    color: green;
    }
  • 下一步,在 对应的的文件头添加以下内容:

    polls/templates/polls/index.html

    1
    2
    3
    {% load static %}

    <link rel="stylesheet" href="{% static 'polls/style.css' %}">

    {% static %} 模板标签会生成静态文件的绝对路径。

    这就是你开发所需要做的所有事情了。

    image-20230512101159792
  • 启动服务器(如果它正在运行中,重新启动一次):

    1
    ...\> py manage.py runserver

    重新载入 http://localhost:8000/polls/

    你会发现有问题的链接是绿色的 (这是 Django 自己的问题标注方式),这意味着你追加的样式表起作用了。

    image-20230512101240072

6.2 添加一个背景图

接下来,我们将为图像创建一个子目录。

  • polls/static/polls/ 目录中创建 images 子目录。

    在此目录中,添加您想用作背景的任何图像文件。

    出于本教程的目的,我们使用了一个名为“background.png”的文件,

    它的完整路径为“polls/static/polls/images/background.png”。

    注意:我这里测试使用的是jpg格式!

  • 然后,在样式表中添加对图像的引用:

    polls/static/polls/style.css

    1
    2
    3
    body {
    background: white url("images/background.png") no-repeat;
    }

    浏览器重载 http://localhost:8000/polls/,你将在屏幕的左上角见到这张背景图。

    image-20230512102107329

    警告

    {% static %} 模板标签在静态文件(例如样式表)中是不可用的,因为它们不是由 Django 生成的。

    你应该始终使用 相对路径 在你的静态文件之间相互引用,

    因为这样你可以更改 STATIC_URL(由 static 模板标签使用来生成 URL),而无需修改大量的静态文件。

这些只是 基础

更多关于设置和框架的资料,参考 静态文件解惑静态文件指南

部署静态文件 介绍了如何在真实服务器上使用静态文件。

当你熟悉静态文件后,阅读 此教程的第 7 部分 来学习如何自定义 Django 自动生成后台网页的过程。

第 7 部分

本教程从 教程第 6 部分 结束的地方开始。

我们继续修改在线投票应用,这次我们专注于自定义我们在 教程第 2 部分初见过的 Django 自动生成后台的过程。

7.1 自定义后台表单

通过 admin.site.register(Question) 注册 Question 模型,Django 能够构建一个默认的表单用于展示。

通常来说,你期望能自定义表单的外观和工作方式。你可以在注册模型时将这些设置告诉 Django。

  • 让我们通过重排列表单上的字段来看看它是怎么工作的。

    用以下内容替换 admin.site.register(Question)

    polls/admin.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from django.contrib import admin

    from .models import Question


    class QuestionAdmin(admin.ModelAdmin):
    fields = ["pub_date", "question_text"]


    admin.site.register(Question, QuestionAdmin)

    更改对比:

    image-20230512102642172 image-20230512103023119

  • 你需要遵循以下流程:

    创建一个模型后台类,

    接着将其作为第二个参数传给 admin.site.register() ——在你需要修改模型的后台管理选项时这么做。

    以上修改使得 “Publication date” 字段显示在 “Question” 字段之前:

    image-20230512102844334

    这在只有两个字段时显得没啥卵用,

    但对于拥有数十个字段的表单来说,为表单选择一个直观的排序方法就显得你的针很细了。

    说到拥有数十个字段的表单,你可能更期望将表单分为几个字段集:

    polls/admin.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from django.contrib import admin

    from .models import Question


    class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
    (None, {"fields": ["question_text"]}),
    ("Date information", {"fields": ["pub_date"]}),
    ]


    admin.site.register(Question, QuestionAdmin)
    image-20230512103111825

    fieldsets元组中的第一个元素是字段集的标题。以下是我们的表单现在的样子:

    效果:

    image-20230512103234752

7.2 添加关联的对象

  • 好了,现在我们有了投票的后台页。不过,一个 Question 有多个 Choice,但后台页却没有显示多个选项。

    有两个方法可以解决这个问题。

  • (方法一)第一个就是仿照我们向后台注册 Question 一样注册 Choice

    polls/admin.py

    1
    2
    3
    4
    5
    6
    from django.contrib import admin

    from .models import Choice, Question

    # ...
    admin.site.register(Choice)
    image-20230512103532211

    现在 “Choices” 在 Django 后台页中是一个可用的选项了。

    “添加选项”的表单看起来像这样:

    image-20230512103605727 image-20230512103628352

    在这个表单中,“Question” 字段是一个包含数据库中所有投票的选择框。

    Django 知道要将 ForeignKey 在后台中以选择框 <select> 的形式展示。

    此时,我们只有一个投票。

    还请注意“问题”旁边的“添加另一个问题”链接。

    每个与另一个具有ForeignKey关系的对象都可以免费获得此链接。

    当你点击“添加另一个问题”时,你会看到一个带有“添加问题”表单的弹出窗口。如果你在该窗口中添加问题并点击“保存”,Django会将问题保存到数据库中,并将其动态添加为你正在查看的“添加选项”表单上的选定选项。

    不过,这是一种很低效地添加“选项”的方法。更好的办法是在你创建“投票”对象时直接添加好几个选项。让我们实现它。

  • (方法二)移除调用 register() 注册 Choice 模型的代码。

    随后,像这样修改 Question 的注册代码:

    polls/admin.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    from django.contrib import admin

    from .models import Choice, Question


    class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3


    class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
    (None, {"fields": ["question_text"]}),
    ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
    ]
    inlines = [ChoiceInline]


    admin.site.register(Question, QuestionAdmin)

    这会告诉 Django:“Choice 对象要在 Question 后台页面编辑。默认提供 3 个足够的选项字段。”

    加载“添加投票”页面来看看它长啥样:

    image-20230512104313357

    它看起来像这样:

    有三个关联的选项插槽——由 extra 定义,且每次你返回任意已创建的对象的“修改”页面时,你会见到三个新的插槽。

    在三个插槽的末端,你会看到一个“添加新选项”的按钮。

    如果你单击它,一个新的插槽会被添加。

    如果你想移除已有的插槽,可以点击插槽右上角的X。以下图片展示了一个已添加的插槽:

    image-20230512104421724
  • 不过,仍然有点小问题。

    它占据了大量的屏幕区域来显示所有关联的 Choice 对象的字段。

    对于这个问题,Django 提供了一种表格式的单行显示关联对象的方法。

    要使用它,只需按如下形式修改 ChoiceInline 申明:

    polls/admin.py

    1
    2
    class ChoiceInline(admin.TabularInline):
    ...

    通过 TabularInline (替代 StackedInline ),关联对象以一种表格式的方式展示,显得更加紧凑:

    image-20230512104611002

    请注意,有一个额外的“删除?”列,允许删除使用“添加另一个选项”按钮添加的行和已保存的行。

7.3 自定义后台更改列表

  • 现在投票的后台页看起来很不错,让我们对“更改列表”页面进行一些调整——改成一个能展示系统中所有投票的页面。

    以下是它此时的外观:

    image-20230512180544903

    默认情况下,Django 显示每个对象的 str() 返回的值。但有时如果我们能够显示单个字段,它会更有帮助。

  • 为此,使用 list_display后台选项,它是一个包含要显示的字段名的元组,在更改列表页中以列的形式展示这个对象:

    另外,让我们把 教程第 2 部分中的 was_published_recently() 方法也加上:

    polls/admin.py

    1
    2
    3
    class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ["question_text", "pub_date", "was_published_recently"]

    现在修改投票的列表页看起来像这样:

    image-20230512180954199

    你可以点击列标题来对这些行进行排序——除了 was_published_recently 这个列,因为没有实现排序方法。

    顺便看下这个列的标题 was_published_recently,默认就是方法名(用空格替换下划线),该列的每行都以字符串形式展示出处。

  • 你可以通过在该方法上(在 polls/models.py 中)使用 display()装饰器来改进,如下图所示:

    polls/models.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from django.contrib import admin


    class Question(models.Model):
    # ...
    @admin.display(
    boolean=True,
    ordering="pub_date",
    description="Published recently?",
    )
    def was_published_recently(self):
    now = timezone.now()
    return now - datetime.timedelta(days=1) <= self.pub_date <= now

    更多关于可通过装饰器设置的属性的信息,请参见 list_display

    image-20230512181647755
  • 再次编辑文件 polls/admin.py,优化 Question 变更页:过滤器,使用 list_filter

    将以下代码添加至 QuestionAdmin

    polls/admin.py

    1
    list_filter = ["pub_date"]
    image-20230512181427536

    这样做添加了一个“过滤器”侧边栏,允许人们以 pub_date 字段来过滤列表:

    image-20230512181720442

    展示的过滤器类型取决你你要过滤的字段的类型。

    因为 pub_date 是类 DateTimeField,Django 知道要提供哪个过滤器:“任意时间”,“今天”,“过去7天”,“这个月”和“今年”。

  • 这已经弄的很好了。让我们再扩充些功能:

    1
    search_fields = ["question_text"]

    在列表的顶部增加一个搜索框。当输入待搜项时,Django 将搜索 question_text 字段。

    你可以使用任意多的字段——由于后台使用 LIKE 来查询数据,将待搜索的字段数限制为一个不会出问题大小,会便于数据库进行查询操作。

    image-20230512182208186
  • 现在是给你的修改列表页增加分页功能的好时机。

    默认每页显示 100 项。

    变更页分页, 搜索框, 过滤器, 日期层次结构, 和 列标题排序 均以你期望的方式合作运行。

7.4 自定义后台界面和风格

在每个后台页顶部显示“Django 管理员”显得很滑稽。这只是一串占位文本。

不过,你可以通过 Django 的模板系统来修改。

Django 的后台由自己驱动,且它的交互接口采用 Django 自己的模板系统。

自定义你的 工程的 模板

  • 在你的工程目录(指包含 manage.py 的那个文件夹)内创建一个名为 templates 的目录。

    模板可放在你系统中任何 Django 能找到的位置。(谁启动了 Django,Django 就以他的用户身份运行。)

    不过,把你的模板放在工程内会带来很大便利,推荐你这样做。

  • 打开你的设置文件(mysite/settings.py,牢记),在 TEMPLATES设置中添加 DIRS选项:

    mysite/settings.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    TEMPLATES = [
    {
    "BACKEND": "django.template.backends.django.DjangoTemplates",
    "DIRS": [BASE_DIR / "templates"],
    "APP_DIRS": True,
    "OPTIONS": {
    "context_processors": [
    "django.template.context_processors.debug",
    "django.template.context_processors.request",
    "django.contrib.auth.context_processors.auth",
    "django.contrib.messages.context_processors.messages",
    ],
    },
    },
    ]

    DIRS 是一个包含多个系统目录的文件列表,用于在载入 Django 模板时使用,是一个待搜索路径。

    组织模板

    就像静态文件一样,我们 可以 把所有的模板文件放在一个大模板目录内,这样它也能工作的很好。

    但是,属于特定应用的模板文件最好放在应用所属的模板目录(例如 polls/templates),而不是工程的模板目录(templates)。我们会在 创建可复用的应用教程 中讨论 为什么 我们要这样做。

    Django 的源文件在哪里?

    如果你不知道 Django 源码在你系统的哪个位置,运行以下命令:

    1
    ...\> py -c "import django; print(django.__path__)"
  • 接着,用你网页站点的名字编辑替换文件内的 {{ site_header|default:_('Django administration') }} (包含大括号)。

    完成后,你应该看到如下代码:

    1
    2
    3
    {% block branding %}
    <h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
    {% endblock %}

    我们会用这个方法来教你复写模板。

    在一个实际工程中,你可能更期望使用 django.contrib.admin.AdminSite.site_header 来进行简单的定制。

    这个模板文件包含很多类似 {% block branding %}{{ title }} 的文本。

    `