在前一篇文章里,我们已经构建了一个博客应用的模型,并利用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
领取专属 10元无门槛券
私享最新 技术干货