Django实战专题:开发专业博客(2)之母子类别导航和添加富文本编辑器CKEditor

在前一篇文章里,我们已经构建了一个博客应用的模型,并利用Django的通用视图开发了博客管理后台,实现了文章的增删查改。本文将对该博客应用做出2个改进,一是实现母子类别导航,二是添加富文本编辑器CKEditor,实现图文编辑和正文显示代码。

何为母子类别导航?

我们希望用一个Category模型(如下所示)实现类似 ‘Python>Django’的母子类别导航。每个类别可能有母类别,也可能没有。一篇文章可能属于一个母类别,也可能属于一个子类别。我们希望点击Django时,能显示所有属于Django类别的文章,而点击Python时能显示所有属于Python类及其子类的文章。就这么点事,我们需要用到一种非常重要的技术,QuerySet的合并。

classCategory(models.Model):

"""文章分类"""

name = models.CharField('分类名',max_length=30,unique=True)

slug = models.SlugField('slug',max_length=40)

parent_category = models.ForeignKey('self',verbose_name="父级分类",blank=True,null=True,on_delete=models.CASCADE)

defget_absolute_url(self):

returnreverse('blog:category_detail',args=[self.slug])

defhas_child(self):

ifself.category_set.all().count() >:

return True

def__str__(self):

returnself.name

classMeta:

ordering = ['name']

verbose_name ="分类"

verbose_name_plural = verbose_name

QuerySet的合并

查询属于某一类别的文章,我们可以使用Article.objects.filter()方法,这个方法查询的结果数据类型是QuerySet类型,而不是List类型。当一个类别有子类别时,我们需要分别查询属于每个子类的文章数据集QuerySet,然后利用union方法把它们合并,最后通过分页显示。为什么要用union方法? 因为QuerySet类型不是List类型,不能用extend或append方法。正确方法如下所示。

classCategoryDetailView(DetailView):

model = Category

defget_context_data(self,**kwargs):

context =super().get_context_data(**kwargs)

ifself.object.has_child():

articles = Article.objects.filter()

categories =self.object.category_set.all()

forcategoryincategories:

queryset = Article.objects.filter(category=category.id).order_by('-pub_date')

articles.union(queryset)

else:

articles = Article.objects.filter(category=self.object.id).order_by('-pub_date')

paginator = Paginator(articles,3)

page =self.request.GET.get('page')

page_obj = paginator.get_page(page)

context['page_obj'] = page_obj

context['paginator'] = paginator

context['is_paginated'] =True

returncontext

模板文件templates/blog/category_detail.html的代码如下所示。

{% extends "blog/base.html" %}

{% block content %}

类别:

{% if category.parent_category %}

{{ category.parent_category.name }}/

{% endif %}

{{ category }}

博客文章清单

{#注释: page_obj不要改。Article可以改成自己对象#}

{% if page_obj %}

{% for article in page_obj %}

{{ article.title }}{{ article.pub_date | date:"Y-m-j" }}

{% endfor %}

{#注释:下面代码一点也不要动#}

{% if is_paginated %}

{% if page_obj.has_previous %}

Previous

{% else %}

class="page-link">Previous

{% endif %}

{% for i in paginator.page_range %}

{% if page_obj.number == i %}

class="page-link">{{ i }}class="sr-only">(current)

{% else %}

{{ i }}

{% endif %}

{% endfor %}

{% if page_obj.has_next %}

Next

{% else %}

class="page-link">Next

{% endif %}

{% endif %}

{% else %}

{#注释:这里可以换成自己的对象#}

No article yet.

{% endif %}

{% endblock %}

前端显示效果如下所示。点击Python基础时,能显示所有属于Python基础类别的文章列表,而点击Python时能显示所有属于Python类及其子类的文章(包括Django类)。如果你不使用QuerySet的合并,那么当你点击Python时,只会显示属于Python类的文章。

富文本编辑器CKEditor

前文里我也提到过博客正文的编辑器太过简单,不能做图文编辑,也不能显示代码。网上很多人推荐CKEditor,我也就来试了试,安装后使用效果确实不错。

安装后的效果如下所示。

如何安装使用CKEditor

网上有很多Django中使用CKEeditor的教程,都很有用。在这里我只想指出一点不同,网上教程大多是在自带后台admin里使用CKEditor,而本例是在admin外使用ckeditor。安装及设置方法如下。

1. 安装前的准备

如果你需要上传和显示图片,请先确保已安装了pillow图片库,并按

文一

设置STATIC和MEDIA文件夹。

2. 安装ckeditor

使用pip install django-ckeditor安装ckeditor, 在项目文件夹下(而不是app文件夹下)新建static文件夹, 使用python manage.py collectstatic下载ckeditor所需的js和css文件。

3. 设置settings.py

在settings.py里添加CKEDITOR的设置,如下所示。我们指定了图片上传文件夹"blog_uploads", 最后图片会上传到/media/blog_uploads/文件夹里。由于我们还选择了RESTRICT_BY_USER和RESTRICT_BY_DATE, 最后图片实际上传地址如下所示:

/media/blog_uploads/Chris/2018/09/09/img_4961.JPG

CKEDITOR_CONFIGS可以设置显示在工具栏toolbar的按钮。

CKEDITOR_UPLOAD_PATH ='blog_uploads/'

CKEDITOR_JQUERY_URL ='https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js'

CKEDITOR_IMAGE_BACKEND ='pillow'

CKEDITOR_ALLOW_NONIMAGE_FILES =False

CKEDITOR_BROWSE_SHOW_DIRS =True

CKEDITOR_RESTRICT_BY_USER =True

CKEDITOR_RESTRICT_BY_DATE =True

CKEDITOR_CONFIGS = {

'default': {

'toolbar': (['Source','-','Preview','-',],

['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print','SpellChecker',],

['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat','-',

"CodeSnippet",'Subscript','Superscript'],

['NumberedList','BulletedList','-','Blockquote'],

['Link','Unlink',],

['Image','Table','HorizontalRule','Smiley','SpecialChar',],

['Format','Font','FontSize','TextColor','BGColor',],

['Bold','Italic','Underline','Strike',],

['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],

),

'extraPlugins':'codesnippet',

'width':'auto',

}

}

4. 模型中使用ckeditor

我们只需将body的TextField改成RickTextUploadingField。如果你不需要上传图片,可以直接使用RickTextField。

fromckeditor_uploader.fieldsimportRichTextUploadingField

classArticle(models.Model):

"""文章模型"""

STATUS_CHOICES = (

('d','草稿'),

('p','发表'),

)

title = models.CharField('标题',max_length=200,unique=True)

slug = models.SlugField('slug',max_length=60,blank=True)

body = RichTextUploadingField('正文')

5. 表单中使用ckeditor

因为我们使用到了表单,所以表单的输入widget还需要改为CKEditorUploadingWidget.

fromdjangoimportforms

from.modelsimportArticle

fromckeditor_uploader.widgetsimportCKEditorUploadingWidget

classArticleForm(forms.ModelForm):

classMeta:

model = Article

exclude = ['author','views','slug','pub_date']

widgets = {

'title': forms.TextInput(attrs={'class':'form-control'}),

'body': CKEditorUploadingWidget(attrs={'class':'form-control'}),

'status': forms.Select(attrs={'class':'form-control'}),

'category': forms.Select(attrs={'class':'form-control'}),

'tags': forms.CheckboxSelectMultiple(attrs={'class':'multi-checkbox'}),

}

6. 模板中使用{{ form.media }}调入ckeditor静态文件

模板中如果不使用{{ form.media }}调入ckeditor静态文件(js, css和图片), 那么前端你将看不到漂亮的用户界面。

{% csrf_token %}

{{ form.media }}

{{ form }}

......

7. 修改staff_member_required装饰器变为login_required。

这一点是在admin内和admin外使用ckeditor最的不同。如果需要使用文件上传,ckeditor默认只有员工(staff member)才有这个权限。如果你需要admin外的用户也能上传图片或文件,你需要将staff_member_required装饰器改为login_required。

你需要按site-packages - > ckeditor_uploader -> templates -> urls.py的源码,把staff_member_required装饰器改为login_required。

8. 显示代码

只需要在CKEDITOR_CONFIGS中加入codesnipppet的plugin即可。

'extraPlugins':'codesnippet',

同时找到static -> ckeditor -> ckeditor -> config.js把codesnippet注册一下。

CKEDITOR.editorConfig =function( config ) {

// Define changes to default configuration here. For example:

// config.language = 'fr';

// config.uiColor = '#AADC6E';

config.extraPlugins:"codesnippet";

};

安装好后即可通过codeshippet插入不用语言代码了,下面是python代码显示效果。

小结

本文讲解了如何实现母子类别导航,重点讲解了QuerySet的合并。我们还安装了CKEditor富文本编辑器,实现了图文编辑和代码显示功能。计划下篇教程中讲解如何添加评论和点赞功能,就看本文有没有30个赞啦。

大江狗

2018.9.10

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180910G1XOJQ00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券