Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第七部分(Page 12)

编写你的第一个 Django app,第七部分(Page 12)转载请注明链接地址

本节教程承接第六部分(page 11)的教程。我们继续开发 web-poll应用,并专注于自定义django的自动生成的admin站点,这点我们在第二部分(page 7)中探讨过。

自定义admin表单

通过在admin.site.register(Question)中注册Question模型,django可以构建一个默认的表单形式。通常,你会希望自定义表单的外观和工作方式。你会在注册对象时告诉django你想使用的选项。

我们来看看编辑表单中的字段顺序是如何工作的。使用下面的代码替换admin.site.register(Question)

# polls/admin.py
from django.contrib import admin

from .models import Question


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

admin.site.register(Question, QuestionAdmin)

你会遵循这个模式 —— 创建一个admin类的模型,然后把它传递给admin.site.register()的第二个参数 —— 当你需要为一个模型修改admin选项时。

上面的更改使“Publication date”字段出现在“Question”字段之前:

这里仅有两个字段,给人的印象不够深刻,但对于有几十个字段的admin表单来说,选择一个有直观顺序则是一个有重要作用的细节。

说到有几十个字段的表单,有可能会想把表单分割成字段集合:

# polls/admin.py
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)

fieldsets中每个元组的第一个元素是字段集合的标题。这是我们的表单现在的外观:

添加关联对象

现在我们有了Question管理页面,但是一个Question有多个Choice,并且管理页面不显示choice。

然而,有两种方式解决这个问题。第一种是在admin中注册choice,就像我们之前对Question做的一样。这很简单:

# polls/admin.py
from django.contrib import admin

from .models import Choice, Question
# ...
admin.site.register(Choice)

现在在django 的admin中 “Choices” 是有一个有效的选项。 “Add choice”表单就像下面的样子:

在这个表单中, “Question” 字段是一个包含数据库中的各个问题的选择框。django知道 ForeignKey(这里少一个链接)对应admin中的<select>框。在我们的例子中,此时只有一个quesiton存在。

同时要注意链接到下一个”Question“的 “Add Another”链接。每个和其他对象有ForeignKey(这里少一个链接)关系的对象都有这个功能。当你点击了“Add Another”,会出现一个有“Add Another”表单的弹出窗口。如果你在这个窗口中添加一个question并点击“Save”,Django会把这个question保存到数据库,并把它作为一个可选择的choice,动态添加它到你看到的“Add choice”表单中。

但是,实际上,这是一种向系统中添加Choice对象的很低效的方式。更好的方法是,在你创建Question对象的时,直接添加许多Choice。让满五年来实现它。

Choice模型中移除register()调用。然后,编辑Question注册代码:

# polls/admin.py
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个choice提供足够的字段。”

加载“Add question”页面,看看它现在是什么样子:

它的工作方式是这样的:有三个有联系的Choice —— 由extra指定 —— 并且每次你回到一个已创建对象的 “Change” 页面,你都会得到三个空白的Choice。

在当前三个Choice的底部,你会看到一个 “Add another Choice” 链接。如果你点击了它,会添加一个新的Choices。如果你想一出一个已添加的Choice,你可以在已添加的Choice中的右上角点击 X。注意,你无法移除最开始的三个Choice。这张图展示了一个添加的Choice:

还有一个小问题,为了显示关联的Choice对象,大量的屏幕空间用来显示所有的字段。因为这个原因,Django提供了以表格的形式显示内嵌关系的对象;你只需要修改ChoiceInline的声明即可:

# polls/admin.py
class ChoiceInline(admin.TabularInline):
    #...

使用TabularInline(替代了StackedInline),关联的对象显示成更紧凑的、基于表格的格式:

注意有一个额外的“Delete”列,它可以移除使用“Add Another Choice”按钮添加并保存的行。

自定义admin变更列表

现在问题管理页面看起来看好,我们在对“change list”页面做一些小改动 —— 该页面显示系统中的所有Quesiton。

下面是它现在的样子

默认情况下,Django显示每个对象的str()。但有时候,如果我们显示单个字段会更有帮助。要实现这个功能,可以使用list_display(这里少一个链接)管理选项,它是一个字段名的元组,在change list页面像列一样显示:

# polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date')

只是为了更好一些,我们把第二节中的was_published_recently()也加入进来:

# polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date', 'was_published_recently')

现在问题change list 页面看起来会像下面:

你可以点击列的头部来以这个值来排序 —— 除了本例中was_published_recently的头部,因为不支持以随意一个方法的输出来排序。另外要注意的是was_published_recently列的头部默认是方法的名字(下划线替换成空格),每行包行输出的字符串形式。

你可以通过给方法添加一些属性来解决这个问题,如下:

# polls/models.py
class Question(models.Model):
    # ...
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'

更多关于这些方法属性的信息,请查看list_display(这里少一个链接)

再次编辑polls/admin.py文件,对Question change list页面做一个改进:使用list_filter(这里少一个链接)添加过滤器。把下面的行添加到QuestionAdmin中:

list_filter = ['pub_date']

这样就添加了一“Filter”侧边栏,可以让人通过pub_date字段对change list进行过滤:

过滤器的显示类型依赖于你使用的过滤字段的类型。因为pub_date是一个DateTimeField(这里少一个链接),django会给出合适的过滤选项: “Any date”, “Today”, “Past 7 days”, “This month”, “This year”.

现在一切进展顺利。我们添加一些搜索功能:

search_fields = ['question_text']

这样会在change list顶部添加一个搜索框。当有人输入搜索内容,django会搜索question_text字段。你可以许多你喜欢的字段 —— 虽然它在后台使用一个LIKE查询,将搜索字段的数量限制为合理的数目,会让你的数据库在搜索时更容易。

现在还是一个告诉你change list提供方便的分页功能的好机会。默认每页显示100项。 Change list pagination(这里少一个链接), search boxes(这里少一个链接), filters(这里少一个链接), date-hierarchies(这里少一个链接), column-header-ordering(这里少一个链接) 它们会按照你认为的那样一起工作。

自定义admin的外观

很明显,在每个admin页面的顶部显示“Django administration”会显得很愚蠢。它仅起到了占位符文本的作用。

尽管,使用django的模板系统这个很容易改变。django damin是有django自己提供的,其接口使用了django自己的模板系统。

自定义你的项目的模板

在你的项目目录(包含manage.py的那个)下创建一个templates目录。模板可以保存在django可以找到的文件系统中的任何位置,(运行服务器的用户也是运行django的用户)。不管怎样。把模板保存在项目中是一个应该遵循的好习惯。

打开你的设置文件(mysite/settings.py),并在 TEMPLATES(少一个链接)中添加一个DIRS(少一个链接)项:

# mysite/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(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)。我们会在reusable apps tutorial中讨论我们为什么要这样做。

现在在 templates中创建一个名字是admin的目录,并从django自己的安装目录中(django/contrib/admin/templates)的的django admin模板目录中把admin/base_site.html复制到这里来。

django的安装目录在哪?
如果你找不到django安装目录在你文件系统中的位置,可以运行下面的命令

$ python -c "import django; print(django.__path__)"

然后,只要编辑并把 {{ site_header|default:_('Django administration') }}(包括花括号)替换成你自己的想要使用的站点名。你会得到一段类似下面的代码:

{% 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 }} 的文本。 {%{{ 标签是django模板语言的一部分。当django渲染admin/base_site.html 时,模板语言会评估以生成最终的HTML页,就像我们在第三部分中看到的一样。

注意,任何django admin模板都可以被重写。要重写一个模板,和重写base_site.html的操作一样 —— 从默认目录中把模板文件复制到你指定的目录中,并修改即可。

自定义你的应用的模板

细心的读者可能会问:但如果DIRS(少一个链接)默认是空目录,django如何找到默认的admin模板?答案是,由于APP_DIRS(少一个链接)设置成了True,django会自动在每个应用包中查找一个templates/子目录,以作备用(不要忘记django.contrib.admin是一个应用。)

我们的poll应用不是很复杂,也不需要自定义admin模板。但如果它变得越来越复杂,并因为一些功能需要修改django的标准admin模板,明智的方法是修改应用的模板,而不是项目中的模板。使用这种方式,你可以在任何新项目中使用polls应用,并确保它可以找到它需要的自定义模板。

更多关于Django如何找到它的模板请查看template loading documentation(少一个链接)

自定义admin默认页

和上面类似的是,你可能想自定义django admin默认页的外观。
默认情况下,它会按照字母的顺序显示INSTALLED_APPS中所有在admin应用注册过的应用。你可能会想对布局做一个大的更改。毕竟,默认页可能是admin中最重要的页面,它应该易于使用。

要自定义的模板是admin/index.html。(和前面对admin/index.html做的 操作一样 —— 从默认目录中复制它到你的自定义模板目录)。编辑这个文件,你会看到它使用了一个叫app_list的模板变量。这个变量包含所有已安装的django app。你可以用你认为最好的方式将链接硬编码到特定对象的admin页面,来替代这个变量。

下一步做什么

初学者教程到此结束。在此期间,你可能想要去有一些快速链接 where to go from here.
如果你熟悉python打包技术,并对如何将一个app变成一个“可重用的app”,请查看Advanced tutorial: How to write reusable apps(少一个链接)

posted on 2018-02-04 22:32  R_e  阅读(958)  评论(0编辑  收藏  举报

导航