当前位置: 首页 > 后端技术 > Python

HelloDjangoPart07:创作后台开启,请开始表演吧!

时间:2023-03-26 00:25:43 Python

作者:HelloGitHub-文中涉及的示例代码已同步更新至HelloGitHub-团队仓库。在这之前,我们已经完成了django博客首页视图的编写。我们希望首页显示已发表的博客文章列表,但并没有抱怨:还没有已发表的文章!正如它所说,我们还没有发表任何文章。本节我们将使用django自带的admin后台来发布我们的博客文章。创建admin后台管理员账号要进入djangoadmin后台,首先需要创建一个超级管理员账号。我们已经在Django迁移运行数据库中创建了一个后台账号,但是如果你还没有按照前面的步骤创建账号,可以进入项目根目录,运行pipenvrunpythonmanage.pycreatesuperuser命令新建一个一:>pipenvrunpythonmanage.pycreatesuperuser用户名(留空以使用'yangxg'):admin电子邮件地址:admin@example.com密码:密码(再次):超级用户创建成功。注意:在命令行输入密码时可能输入的字符不显示,不要以为键盘坏了,按正常方式输入密码即可。在admin后台注册model,我们需要在后台注册几个自己创建的model,这样djangoadmin才能知道它们的存在。注册很简单,只需要在blogadmin.py中添加如下代码:blog/admin.pyfromdjango.contribimportadminfrom.modelsimportPost,Category,Tagadmin.site.register(Post)admin.site.register(Category)admin.site.register(Tag)运行开发服务器,访问http://127.0.0.1:8000/admin/,会进入djangoadmin后台登录页面,输入刚才创建的管理员账号密码登录后台.你可以看到我们刚刚注册的三个模型。点击Posts后面的Add按钮,进入添加Posts页面,即添加博文。然后在相关地方输入一些测试内容,添加完点击保存,这样文章就添加好了,也可以多添加几篇文章看看效果。注意每篇文章必须有一个分类,添加文章时可以选择已有的分类。如果数据库中没有分类,在选择分类时点击分类后面的+按钮,可以添加新的分类。您可能希望将图像添加到您的文章内容中,但目前无法实现。在文章中插入图片的方法将在支持Markdown语法部分进行介绍。访问http://127.0.0.1:8000/首页,可以看到你添加的文章列表。下面是我的环境效果图:自定义admin后台在使用admin后台的时候,我们发现了以下经历相关问题:admin后台本身的页面元素已经中文化了,但是我们自己的博客应用,以及Post、Category、Tag在页面中以英文显示,发布文章时,表单中各个字段的标签也是英文。在admin后台的帖子列表页面,我们只看到文章的标题,但是我们希望它显示更详细的信息,比如作者,发布时间,修改时间等。添加文章时,所有数据必须是手动填写。但是,有些数据应该是自动生成的。例如,文章发布时间和修改时间的created_time和modified_time应该在文章创建或修改时自动生成,而不是手动控制。同时,我们的博客是单人博客系统,发布者必须是文章的作者。这个也要自动设置为admin后台的登录账号。虽然django的admin应用是开箱即用的,但它也提供了丰富的自定义功能,这也是django吸引人的地方。下面我们根据自己的需要一一定制。博客应用的汉化首先我们来看一下需要汉化的地方。管理主页上的每个部分代表一个应用程序。例如,BLOGsection代表一个博客应用,sectiontitle默认显示应用名称。application部分包含已在管理后台注册的应用程序的所有模型。我们之前注册了Post、Category和Tag,所以显示这三个model,显示的名字就是model的名字。如下图所示:接下来是新建帖子页面的表单。每个字段的标签是从Post类中定义的Field名称转换而来的。比如在Post模型中定义了title字段,那么对应表单的标签就是Title。第一个是BLOG部分的标题BLOG。一个部分代表一个应用程序。很明显,这个标题是应用名称转换而来的。blog应用下有一个app.py模块,其代码如下:fromdjango.appsimportAppConfigclassBlogConfig(AppConfig):name='blog'这些是我们运行startapp创建一个时自动生成的代码博客应用程序。可以看到有一个BlogConfig类,它继承自AppConfig类。看名字就知道这是一个应用配置相关的类。我们可以通过设置这个类中一些属性的值来配置这个应用程序的一些特性。比如这里的name是用来定义app的名字,需要和application的名字保持一致,不能更改。要修改应用程序在管理后台的显示名称,请添加verbose_name属性。classBlogConfig(AppConfig):name='blog'verbose_name='blog'同时,我们在设置中注册应用的时候,直接注册了app名称blog,现在我们在BlogConfig类,所以我们要设置注册这个类:INSTALLED_APPS=['django.contrib.admin',...'blog.apps.BlogConfig',#Registerblogapplication]再次登录后台,可以看到BLOG部分的标题已显示为博客。下一步是让应用程序下注册的模型显示中文。由于application是在apps.py中配置的,所以model相关的配置要到对应的model中去。配置模型的一些特性定义在模型的内部类Meta中。比如对于Post模型,让它在admin后台显示中文,如下:verbose_name='article'verbose_name_plural=verbose_namedef__str__(self):returnself.title同样,verbose_name用于指定相应模型在admin后台的显示名称,其中verbose_name_plural用于表示多篇文章的复数显示形式.在英文中,如果有多篇文章,会显示为Posts,意思是复数,中文没有复数表达,所以定义同verbose_name。同样,也可以设置Tag和Category:classCategory(models.Model):name=models.CharField(max_length=100)classMeta:verbose_name='category'verbose_name_plural=verbose_namedef__str__(self):returnself.nameclassTag(models.Model):name=models.CharField(max_length=100)classMeta:verbose_name='Tag'verbose_name_plural=verbose_namedef__str__(self):returnself.name在admin中可以看到汉化的效果。然后就是修改post表单的标签。标签是从模型中定义的Field名称转换过来的,所以在Field中修改。类Post(models.Model):title=models.CharField('title',max_length=70)body=models.TextField('text')created_time=models.DateTimeField('创建时间')modified_time=models.DateTimeField('修改时间')excerpt=models.CharField('summary',max_length=200,blank=True)category=models.ForeignKey(Category,verbose_name='category',on_delete=models.CASCADE)tags=models.ManyToManyField(Tag,verbose_name='label',blank=True)author=models.ForeignKey(User,verbose_name='author',on_delete=models.CASCADE)可以看到我们给每个Field传递了一个position参数,参数值为字段应该显示的名称(如果不传,django会根据字段名自动生成)。此参数的名称也称为verbose_name。大多数字段参数位于第一个位置,但由于ForeignKey和ManyToManyField的第一个参数必须传递给其关联的Model,因此我们对category和tagsverbose_name字段使用关键字参数。文章列表显示更详细的信息在admin后台的文章列表页面,我们只看到文章的标题,但是我们想要显示更详细的信息,这就需要我们自定义admin了。在admin.py中添加如下代码:blog/admin.pyfromdjango.contribimportadminfrom.modelsimportPost,Category,TagclassPostAdmin(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列表页面,可以看到显示效果好多了。简化添加新文章的表单接下来,优化添加新文章时表单数据填写不合理的部分。文章的创建时间和修改时间应该是根据当前时间自动生成的,但是现在是手动填写的,文章的作者应该是后台管理员用户自动填写的,那么自动填写的这些字段在数据中不需要添加在新文章中出现的形式。之前我们在blog/admin.py中定义了一个PostAdmin来配置后台Post的一些展示形式。list_display属性控制在帖子列表页面上显示的字段。另外还有一个fields属性,用来控制表单显示的字段,刚好满足我们的需求:classPostAdmin(admin.ModelAdmin):list_display=['title','created_time','modified_time','category','author']fields=['title','body','excerpt','category','tags']这里fields定义的字段就是表单显示的字段。接下来是填充创建时间、修改时间和文章作者值。前面说过,文章的作者应该自动设置为登录后台发布这篇文章的管理员用户。发布文章的过程其实就是一个HTTP请求过程。前面提到django将HTTP请求封装在HttpRequest对象中,然后作为第一个参数传递给视图函数(这里我们看不到新文章的视图,因为djangoadmin已经自动帮我们生成了),而如果用户登录我们的站点,那么django会将这个用户实例绑定到request.user属性上,我们可以通过request.user获取到当前请求用户,然后链接到新建的文章上即可。postadmin继承自ModelAdmin,它有一个save_model方法,这个方法只有一行代码:obj.save()。它的作用是将注册到这个Modeladmin关联(这里的Modeladmin关联注册为Post)的模型实例保存到数据库中。这个方法接收四个参数,第一个,一个是request,就是本次的HTTP请求对象,第二个是obj,就是本次创建的关联对象的实例,所以通过重写这个方法,request可以be.user关联创建的Post实例,然后将Post数据保存到数据库中:classPostAdmin(admin.ModelAdmin):list_display=['title','created_time','modified_time','category','作者']fields=['title','body','excerpt','category','tags']defsave_model(self,request,obj,form,change):obj.author=request.usersuper().save_model(request,obj,form,change)最后需要填写文章的创建时间和修改时间。一种思路我们可以按照上面的思路,重写save_model方法,将创建的post对象和当前时间关联起来,但是有个问题,这样只有通过admin后台创建的文章才能自动关联这些时间,但是文章的创建不一定是在admin里面,也有可能是通过命令行。这时候我们可以通过自定义Post模型来达到目的。首先,模型中定义的每个字段都会收到一个默认关键字参数。这个参数的意思是如果模型实例保存到数据库时对应的Field没有设置值,那么Django会取default指定的默认值,保存到数据库中。因此,对于文章创建时间字段,如果初始没有指定值,则默认指定为当前时间,因此可以通过默认关键字参数指定:fromdjango.utilsimporttimezoneclassPost(models.Model):...created_time=models.DateTimeField('creationtime',default=timezone.now)...这里default可以指定为常量值或可调用对象,我们指定timezone.now函数,所以如果没有指定created_time的值,django会指定为timezone.now函数调用后的值。timezone.now是django提供的实用函数,它返回当前时间。因为timezone模块中的函数会自动帮我们处理时区,所以我们使用django提供的timezone模块,而不是Python提供的datetime模块来处理时间。那么modified_time可以默认吗?答案是否定的,因为虽然第一次保存数据时会按照默认值指定当前时间,但是第二次修改模型数据时,由于modified_time已经有了值,即,第一次使用默认值,那么第二次保存时默认值就不起作用了,如果我们不修改modified_time的值,它的值将永远是第一次保存数据库时的默认值。所以这里问题的关键在于每次保存模型时,都要修改modified_time的值。每个模型都有一个保存方法,其中包含将模型数据保存到数据库的逻辑。重写这个方法,将modified_time的值指定为模型存入数据库前的当前时间不就行了吗?代码如下:fromdjango.utilsimporttimezoneclassPost(models.Model):...defsave(self,*args,**kwargs):self.modified_time=timezone.now()super().save(*args,**kwargs)指定modified_time的值后,别忘了调用父类的save来执行将数据存回数据库的逻辑。