惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
M
MIT News - Artificial intelligence
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog
Apple Machine Learning Research
Apple Machine Learning Research
罗磊的独立博客
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
有赞技术团队
有赞技术团队
博客园 - 叶小钗
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Visual Studio Blog
美团技术团队
F
Fortinet All Blogs
I
Intezer
IT之家
IT之家
T
Threat Research - Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Know Your Adversary
Know Your Adversary
P
Proofpoint News Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AWS News Blog
AWS News Blog
P
Palo Alto Networks Blog
S
Securelist
L
LINUX DO - 热门话题
Security Archives - TechRepublic
Security Archives - TechRepublic
N
Netflix TechBlog - Medium
TaoSecurity Blog
TaoSecurity Blog
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Project Zero
Project Zero
U
Unit 42
博客园_首页
博客园 - 司徒正美
S
Security Affairs
V
V2EX
T
Threatpost
T
Tailwind CSS Blog
GbyAI
GbyAI
O
OpenAI News
K
Kaspersky official blog
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
H
Hacker News: Front Page
V2EX - 技术
V2EX - 技术
Security Latest
Security Latest

追梦人物的博客

0x05:Merkle Tree & Patricia Trie 0x04:ECDSA - 以太坊设计与实现 LeetCode 105 从前序与中序遍历构造二叉树 迭代算法原理 + 完整证明 0x03:Address - 以太坊设计与实现 0x02:Secp256k1 - 以太坊设计与实现 0x00:专栏开篇 - 以太坊设计与实现 0x01:RLP 编码 - 以太坊设计与实现 Bellman-Ford 算法原理及其在 DeFi 套利中的应用 - 追梦人物的博客 迪杰斯特拉(Dijkstra)最短路径算法原理、实现与证明 - 追梦人物的博客 实战:CEX-DEX 稳定币套利监控程序开发 - 追梦人物的博客 CEX-DEX 稳定币套利模型 - 追梦人物的博客 Uniswap 手续费和协议费机制剖析 - 追梦人物的博客 Uniswap 流动性机制及相关数学原理分析 - 追梦人物的博客 uv 替代 pyenv + pipx + poetry 环境管理实践 Rust 项目从创建到发布 - 追梦人物的博客 VS Code 调试 Python - 追梦人物的博客 比特币跨市场套利的数学模型 - 追梦人物的博客 ERC-20 相关知识点总结 - 追梦人物的博客 Django 老项目如何从 SQLite 迁到 PostgreSQL 自动生成接口文档 - HelloDjango - django REST framework 教程 单元测试 - HelloDjango - django REST framework 教程 限制接口访问频率 - HelloDjango - django REST framework 教程 API 版本管理 - HelloDjango - django REST framework 教程 如何在 Windows 下搭建高效的 django 开发环境 拓展Python Markdown - 追梦人物的博客 加缓存为接口提速 - HelloDjango - django REST framework 教程 基于 drf-haystack 实现文章搜索接口 - HelloDjango 评论接口 - HelloDjango - django REST framework 教程 实现分类、标签、归档日期接口 - HelloDjango - django REST framework 教程 在接口返回Markdown解析后的内容 - HelloDjango - django REST framework 教程 文章详情接口 - HelloDjango - django REST framework 教程 分页 - HelloDjango - django REST framework 教程 使用视图集简化代码 - HelloDjango - django REST framework 教程 用类视图实现首页 API - HelloDjango - django REST framework 教程 实现博客首页文章列表 API - HelloDjango - django REST framework 教程 初始化 RESTful API 风格的博客系统 - HelloDjango django-rest-framework 是什么鬼? - HelloDjango - django REST framework 教程 结束 or 开始? - HelloDjango Coverage.py 统计测试覆盖率 - HelloDjango - Django博客教程(第二版) 单元测试:测试评论应用 - HelloDjango - Django博客教程(第二版) 单元测试:测试 blog 应用 - HelloDjango Django Haystack 全文检索与关键词高亮 - HelloDjango Django 博客实现简单的全文搜索 - HelloDjango - Django博客教程(第二版) 开启 Django 博客的 RSS 功能 - HelloDjango 统计各个分类和标签下的文章数 - HelloDjango - Django博客教程(第二版) 稳定易用的 Django 分页库,完善分页功能 - HelloDjango 通过 Django Pagination 实现简单分页 - HelloDjango 在脚本中使用 ORM:Faker 批量生成测试数据 - HelloDjango Django 官方推荐的姿势:类视图 - HelloDjango - Django博客教程(第二版) Django 使用 union 合并不同模型(Model) 的查询集(QuerySet) 开发博客文章阅读量统计功能 - HelloDjango - Django博客教程(第二版) 使用 Docker 让部署 Django 项目更加轻松 - HelloDjango 使用 Certbot 向 Let's Encrypt 免费申请 HTTPS 证书 - HelloDjango 使用 Fabric 自动化部署 - HelloDjango 博客代码开源啦 - 追梦人物的博客 (赠书)推荐一本django书籍:Django企业开发实战 - 追梦人物的博客 Nginx+Gunicorn+Supervisor 部署 Django 博客应用 - HelloDjango 优化博客功能细节,提升使用体验 - HelloDjango - Django博客教程(第二版) 交流的桥梁:评论功能 - HelloDjango - Django博客教程(第二版) 分类、归档和标签页 - HelloDjango - Django博客教程(第二版) 页面侧边栏:使用自定义模板标签 - HelloDjango - Django博客教程(第二版) 自动生成文章摘要 - HelloDjango - Django博客教程(第二版) Markdown 文章自动生成目录,提升阅读体验 - HelloDjango - Django博客教程(第二版) 让博客支持 Markdown 语法和代码高亮 - HelloDjango 开发博客文章详情页 - HelloDjango - Django博客教程(第二版) 博客从“裸奔”到“有皮肤” - HelloDjango - Django博客教程(第二版) Django 的接客之道 - HelloDjango - Django博客教程(第二版) Django 迁移、操作数据库 - HelloDjango - Django博客教程(第二版) 创建 Django 博客的数据库模型 - HelloDjango 开始进入 django 开发之旅 - HelloDjango "空空如也"的博客应用 - HelloDjango - Django博客教程(第二版) 一种自顶而下的Python装饰器设计方法 - 追梦人物的博客 Python提取支付宝和微信支付二维码 - 追梦人物的博客 2018 - 我的学生生涯最后一年回顾 - 追梦人物的博客 批量清除todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 筛选练习参考答案 - Vue 2.x Todo 教程练习参考答案 删除todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 编辑todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 添加todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 标为完成练习参考答案 - Vue 2.x Todo 教程练习参考答案 入门仪式_Hello_Vue练习参考答案 - Vue 2.x Todo 教程练习参考答案 组件化todo应用 - Vue 2.x Todo 教程 批量清除todo - Vue 2.x Todo 教程 本地存储 - Vue 2.x Todo 教程 筛选 - Vue 2.x Todo 教程 还剩多少todo未完成 - Vue 2.x Todo 教程 自定义指令实现自动聚焦 - Vue 2.x Todo 教程 全部标为完成 - Vue 2.x Todo 教程 删除todo - Vue 2.x Todo 教程 编辑todo - Vue 2.x Todo 教程 添加todo - Vue 2.x Todo 教程 标为完成 - Vue 2.x Todo 教程 UI - Vue 2.x Todo 教程 显示todo列表 - Vue 2.x Todo 教程 入门仪式:Hello Vue - Vue 2.x Todo 教程 在学习django-rest-framework时收集的学习资料推荐 - 追梦人物的博客 区块链理论与应用研究小组成员招募书 - 追梦人物的博客 Python界网红,豆瓣工程师董伟明加了我的QQ后 - 追梦人物的博客 招募Django学习小组项目组核心成员 - 追梦人物的博客
创作后台开启,请开始你的表演 - HelloDjango - Django博客教程(第二版)
2019-08-14 · via 追梦人物的博客

在此之前我们完成了 django 博客首页视图的编写,我们希望首页展示发布的博客文章列表,但是它却抱怨:暂时还没有发布的文章!如它所言,我们确实还没有发布任何文章,本节我们将使用 django 自带的 admin 后台来发布我们的博客文章。

创建 admin 后台管理员账户

要想进入django admin 后台,首先需要创建一个超级管理员账户。我们在 Django 迁移、操作数据库 中已经创建了一个后台账户,但如果你没有按照前面的步骤创建账户的话,可以进入项目根目录,运行 pipenv run python manage.py createsuperuser 命令新建一个:

> pipenv run python manage.py createsuperuser

用户名 (leave blank to use 'yangxg'): admin
电子邮件地址: admin@example.com
Password:
Password (again):
Superuser created successfully.

注意:

在命令行输入密码时可能不会显示输入的字符,不要以为键盘坏了,照正常的方式输入密码即可。

在 admin 后台注册模型

要在后台注册我们自己创建的几个模型,这样 django admin 才能知道它们的存在,注册非常简单,只需要在 blog\admin.py 中加入下面的代码:

blog/admin.py

from django.contrib import admin
from .models import Post, Category, Tag

admin.site.register(Post)
admin.site.register(Category)
admin.site.register(Tag)

运行开发服务器,访问 http://127.0.0.1:8000/admin/ ,就进入了到了django admin 后台登录页面,输入刚才创建的管理员账户密码就可以登录到后台了。

django admin 后台

可以看到我们刚才注册的三个模型了,点击 Posts 后面的增加按钮,将进入添加 Post 的页面,也就是新增博客文章。然后在相关的地方输入一些测试用的内容,增加完后点击保存,这样文章就添加完毕了,你也可以多添加几篇看看效果。注意每篇文章必须有一个分类,在添加文章时你可以选择已有分类。如果数据库中还没有分类,在选择分类时点击 Category 后面的 + 按钮新增一个分类即可。

django admin 后台新增文章

你可能想往文章内容中添加图片,但目前来说还做不到。在支持 Markdown 语法部分中将介绍如何在文章中插入图片的方法。

访问 http://127.0.0.1:8000/ 首页,你就可以看到你添加的文章列表了,下面是我所在环境的效果图:

博客首页显示的文章列表

定制 admin 后台

使用 admin 后台的时候,我们发现了下面的一些体验相关的问题:

  • admin 后台本身的页面元素是已经汉化了的,但是我们自己的 blog 应用,以及 Post、Category、Tag 在页面中显示却是英文的,以及发布文章的时候,表单各字段的 label 也是英文的。
  • 在 admin 后台的 post 列表页面,我们只看到了文章的标题,但是我们希望它显示更加详细的信息,例如作者、发布时间、修改时间等。
  • 新增文章时,所有数据都要自己手动填写。但是,有些数据应该是自动生成。例如文章发布时间 Created time 和修改时间 Modified time,应该在创建或者修改文章时自动生成,而不是手动控制。同时我们的博客是单人博客系统,发布者肯定是文章作者,这个也应该自动设定为 admin 后台的登录账户。

虽然 django 的 admin 应用开箱即用,但也提供了丰富的定制功能,这正是 django 吸引人的地方,下面我们根据需求来一个个定制。

汉化 blog 应用

首先来看一下需要汉化的地方,admin 首页每个版块代表一个 app,比如 BLOG 版块表示 blog 应用,版块标题默认显示的就是应用名。应用版块下包含了该应用全部已经注册到 admin 后台的 model,之前我们注册了 Post、Category 和 Tag,所以显示的是这三个 model,显示的名字就是 model 的名字。如下图所示:

BLOG 应用版块

其次是新增 post 页面的表单,各个字段的 label 由定义在 Post 类的 Field 名转换而来,比如 Post 模型中定义了 title 字段,则对应表单的 label 就是 Title。

首先是 BLOG 版块的标题 BLOG,一个版块代表一个应用,显然这个标题使用应用名转换而来,在 blog 应用下有一个 app.py 模块,其代码如下:

from django.apps import AppConfig


class BlogConfig(AppConfig):
    name = 'blog'

这些是我们在运行 startapp 创建 blog 应用时自动生成的代码,可以看到有一个 BlogConfig 类,其继承自 AppConfig 类,看名字就知道这是和应用配置有关的类。我们可以通过设置这个类中的一些属性的值来配置这个应用的一些特性的。比如这里的 name 是用来定义 app 的名字,需要和应用名保持一致,不要改。要修改 app 在 admin 后台的显示名字,添加 verbose_name 属性。

class BlogConfig(AppConfig):
    name = 'blog'
    verbose_name = '博客'

同时,我们此前在 settings 中注册应用时,是直接注册的 app 名字 blog,现在在 BlogConfig 类中对 app 做了一些配置,所以应该将这个类注册进去:

INSTALLED_APPS = [
    'django.contrib.admin',
    ...

    'blog.apps.BlogConfig',  # 注册 blog 应用
]

再次登录后台,就可以看到 BLOG 版块的标题已经显示为博客了。

接下来是让应用下注册的 model 显示为中文,既然应用是在 apps.py 中配置,那么和 model 有关的配置应该去找相对应的 model 。配置 model 的一些特性是通过 model 的内部类 Meta 中来定义。比如对于 Post 模型,要让他在 admin 后台显示为中文,如下:

class Post(models.Model):
    ...
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    class Meta:
        verbose_name = '文章'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

同样地,这里通过 verbose_name 来指定对应的 model 在 admin 后台的显示名称,这里 verbose_name_plural 用来表示多篇文章时的复数显示形式。英语中,如果有多篇文章,就会显示为 Posts,表示复数,中文没有复数表现形式,所以定义为和 verbose_name一样。

同样的可以把 Tag 和 Category 也设置一下:

class Category(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
        verbose_name = '分类'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Tag(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
        verbose_name = '标签'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

在 admin 就可以看到汉化后的效果了。

然后就是修改 post 的表单的 label,label 由定义在 model 中的 Field 名转换二来,所以在 Field 中修改。

class Post(models.Model):
    title = models.CharField('标题', max_length=70)
    body = models.TextField('正文')
    created_time = models.DateTimeField('创建时间')
    modified_time = models.DateTimeField('修改时间')
    excerpt = models.CharField('摘要', max_length=200, blank=True)
    category = models.ForeignKey(Category, verbose_name='分类', on_delete=models.CASCADE)
    tags = models.ManyToManyField(Tag, verbose_name='标签', blank=True)
    author = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE)

可以看到我们给每个 Field 都传入了一个位置参数,参数值即为 field 应该显示的名字(如果不传,django 自动根据 field 名生成)。这个参数的名字也叫 verbose_name,绝大部分 field 这个参数都位于第一个位置,但由于 ForeignKeyManyToManyField 第一个参数必须传入其关联的 Model,所以 category、tags 这些字段我们使用了关键字参数 verbose_name

文章列表显示更加详细的信息

在 admin 后台的文章列表页面,我们只看到了文章的标题,但是我们希望它显示更加详细的信息,这需要我们来定制 admin 了,在 admin.py 添加如下代码:

blog/admin.py

from django.contrib import admin
from .models import Post, Category, Tag

class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'created_time', 'modified_time', 'category', 'author']

# 把新增的 Postadmin 也注册进来
admin.site.register(Post, PostAdmin)
admin.site.register(Category)
admin.site.register(Tag)

刷新 admin Post 列表页面,可以看到显示的效果好多了。

定制后的 admin 文章列表页

简化新增文章的表单

接下来优化新增文章时,填写表单数据的不合理的地方。文章的创建时间和修改时间应该根据当前时间自动生成,而现在是由人工填写,还有就是文章的作者应该自动填充为后台管理员用户,那么这些自动填充数据的字段就不需要在新增文章的表单中出现了。

此前我们在 blog/admin.py 中定义了一个 PostAdmin 来配置 Post 在 admin 后台的一些展现形式。list_display 属性控制 Post 列表页展示的字段。此外还有一个 fields 属性,则用来控制表单展现的字段,正好符合我们的需求:

class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'created_time', 'modified_time', 'category', 'author']
    fields = ['title', 'body', 'excerpt', 'category', 'tags']

这里 fields 中定义的字段就是表单中展现的字段。

接下来是填充创建时间,修改时间和文章作者的值。之前提到,文章作者应该自动设定为登录后台发布此文章的管理员用户。发布文章的过程实际上是一个 HTTP 请求过程,此前提到,django 将 HTTP 请求封装在 HttpRequest 对象中,然后将其作为第一个参数传给视图函数(这里我们没有看到新增文章的视图,因为 django admin 已经自动帮我们生成了),而如果用户登录了我们的站点,那么 django 就会将这个用户实例绑定到 request.user 属性上,我们可以通过 request.user 取到当前请求用户,然后将其关联到新创建的文章即可。

Postadmin 继承自 ModelAdmin,它有一个 save_model 方法,这个方法只有一行代码:obj.save()。它的作用就是将此 Modeladmin 关联注册的 model 实例(这里 Modeladmin 关联注册的是 Post)保存到数据库。这个方法接收四个参数,其中前两个,一个是 request,即此次的 HTTP 请求对象,第二个是 obj,即此次创建的关联对象的实例,于是通过复写此方法,就可以将 request.user 关联到创建的 Post 实例上,然后将 Post 数据再保存到数据库:

class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'created_time', 'modified_time', 'category', 'author']
    fields = ['title', 'body', 'excerpt', 'category', 'tags']

    def save_model(self, request, obj, form, change):
        obj.author = request.user
        super().save_model(request, obj, form, change)

最后还剩下文章的创建时间和修改时间需要填充,一个想法我们可以沿用上面的思路,复写 save_model 方法,将创建的 post 对象关联当前时间,但是这存在一个问题,就是这样做的话只有通过 admin 后台创建的文章才能自动关联这些时间,但创建文章不一定是在 Admin,也可能通过命令行。这时候我们可以通过对 Post 模型的定制来达到目的。

首先,Model 中定义的每个 Field 都接收一个 default 关键字参数,这个参数的含义是,如果将 model 的实例保存到数据库时,对应的 Field 没有设置值,那么 django 会取这个 default 指定的默认值,将其保存到数据库。因此,对于文章创建时间这个字段,初始没有指定值时,默认应该指定为当前时间,所以刚好可以通过 default 关键字参数指定:

from django.utils import timezone

class Post(models.Model):
    ...
    created_time = models.DateTimeField('创建时间', default=timezone.now)
    ...

这里 default 既可以指定为一个常量值,也可以指定为一个可调用(callable)对象,我们指定 timezone.now 函数,这样如果没有指定 created_time 的值,django 就会将其指定为 timezone.now 函数调用后的值。timezone.now 是 django 提供的工具函数,返回当前时间。因为 timezone 模块中的函数会自动帮我们处理时区,所以我们使用的是 django 为我们提供的 timezone 模块,而不是 Python 提供的 datetime 模块来处理时间。

那么修改时间 modified_time 可以用 default 吗?答案是不能,因为虽然第一次保存数据时,会根据默认值指定为当前时间,但是当模型数据第二次修改时,由于 modified_time 已经有值,即第一次的默认值,那么第二次保存时默认值就不会起作用了,如果我们不修改 modified_time 的值的话,其值永远是第一次保存数据库时的默认值。

所以这里问题的关键是每次保存模型时,都应该修改 modified_time 的值。每一个 Model 都有一个 save 方法,这个方法包含了将 model 数据保存到数据库中的逻辑。通过覆写这个方法,在 model 被 save 到数据库前指定 modified_time 的值为当前时间不就可以了?代码如下:

from django.utils import timezone

class Post(models.Model):
    ...

    def save(self, *args, **kwargs):
        self.modified_time = timezone.now()
        super().save(*args, **kwargs)

要注意在指定完 modified_time 的值后,别忘了调用父类的 save 以执行数据保存回数据库的逻辑。

-- EOF --