这篇应该是2017年的最后一篇博客了,在这里首先祝你们元旦快乐!html
从这篇博客开始,将会介绍Blogs App的功能实现,包括数据模型的创建、相关功能的视图函数的实现、前端页面的设计等,这意味着咱们即将能够书写真正的博客了。前端
首先来创建咱们的Blogs App,方法同创建Users App,即在manage.py目录下输入如下命令:python
python3 manage.py startapp blogs
# blogs/models.py # -*- coding=utf-8 -*- from django.db import models from users.models import Users from ckeditor_uploader.fields import RichTextUploadingField from django.utils import timezone class Blog(models.Model): title = models.CharField(max_length=32,verbose_name=u'标题') auther = models.ForeignKey(Users,default='') category = models.ForeignKey(Category,null=True) content = RichTextUploadingField(verbose_name=u'内容') createdate = models.DateTimeField('Create time',default=timezone.now()) modifydate = models.DateTimeField('Modify time',default=timezone.now()) readcount = models.IntegerField(default=0) # Add comment count commentcount = models.IntegerField(default=0,null=True,blank=True) draft = models.BooleanField(default=True) @classmethod def create(cls,title,authername,category,content,createdate,modifydate): blog = cls(title=title,auther=authername,category=category,content=content,createdate=createdate,modifydate=modifydate) return blog def __unicode__(self): return self.titleBlog model的字段以下:
title: 博客标题,最长32jquery
auther: 做者,是Users的外键数据库
category: 类别,是Category的外键django
content: 博客的正文。这里使用了CKeditor编辑器提供的富文本域来存储博客的正文,以便以后的图文混排操做。CKeditor的部分将在后面介绍。后端
createdate: 博客的发表时间,这里的时间用的是timezone.now(),即便用settings.py中定义的时区来决定当前的时间。安全
modifydate: 博客的修改时间session
readcount: 博客的阅读数app
commentcount: 博客拥有的评论数
draft: 草稿标记,当为True时表示博客为草稿状态,将不会在主页出现;当为False时博客为正文状态,将在正文出现。
下面是Category model和Comment model:
# blogs/models.py # ... class Category(models.Model): categoryname = models.CharField(max_length=8,unique=True) blogcount = models.IntegerField() def __unicode__(self): return self.categoryname class Comment(models.Model): attachedblog = models.ForeignKey(Blog) content = models.TextField() auther = models.ForeignKey(Users,default='') createtime = models.DateTimeField('Comment Create time') @classmethod def create(cls,attachedblog,content,authername,createtime): comment = cls(attachedblog=attachedblog,auther=authername,content=content,createtime=createtime) return comment def __unicode__(self): return self.content
Comment model相对复杂一些:
attachedblog:每篇评论都要属于一篇博客,该字段代表评论属于哪篇博客
content:评论内容
auther:评论的做者
createtime:评论的发表时间
在创建好这些model后,和Users App同样,咱们来作一些基本的配置工做:在blogs/apps.py和blogs/admin.py中注册这些model和App,并将Blogs App加入到myblog/settings.py中。
# blogs/apps.py from django.apps import AppConfig class BlogsConfig(AppConfig): name = 'blogs'
# blogs/admin.py from django.contrib import admin from .models import Blog,Category # Register your models here. admin.site.register(Blog) admin.site.register(Category)
# myblog/settings.py # ... INSTALLED_APPS = [ 'blogs.apps.BlogsConfig', 'users.apps.UsersConfig', # ... ] # ...在完成了这些琐碎的小配置后,咱们就能够开始编写咱们的视图函数了。Blogs App涉及到的视图函数相对比较复杂,所以在这篇博客中先介绍三个核心功能的后端实现:浏览、发布博客以及保存草稿。
首先来看浏览功能的实现。浏览功能,即根据Blog的id获得其相关内容并显示在页面上,相对比较简单,代码以下:
# blogs/views.py def content(request,blogId): blog = Blog.objects.get(id=blogId) comment = Comment.objects.filter(attachedblog=blog) request.session['currblogId'] = blogId blog_title = blog.title blog_content = blog.content blogContent = { 'blog_title':blog_title, 'content':blog_content, 'comment_list':comment } blog.readcount+=1 blog.save() return render(request,'blogs/content.html',blogContent)
# blogs/urls.py from django.conf.urls import url from . import views from django.conf.urls import include from users import urls as userurls app_name='blogs' urlpatterns = [ url(r'^(?P<blogId>[0-9]*)$', views.content, name='content'), # ... ]在content函数中,经过blogId能够得到到惟一的Blog对象,进而拿到博客的标题、正文和从属于该博客的全部评论。随后,使用session记录下正在访问的博客的id,以便后面发布评论使用。在得到了博客的相关信息后,即可以将其放在一个字典里传给前端以供显示;而后,咱们要使该博客的访问量+1,并更新在数据库中。
<!-- blogs/templates/blogs/content.html --> {% extends "blogTemplate.html" %} {% block content %} <div class="content"> <h2>{{ blog_title }}</h2> {{ content|safe }} </div> <p><a href="{% url 'index' %}">返回首页</a></p> {% endblock %} {% block comment %} {% if comment_list %} {% for comment in comment_list %} <ul class="comment"> <li> {% if comment.auther.username == "anony" %} <h4>匿名用户 {{ comment.createtime|date:"Y-m-d H:i:s" }}</h4> {% else %} <img src="{{ comment.auther.logoimage.url }}" width="64" height="64" /> <h4>{{ comment.auther }} {{ comment.createtime|date:"Y-m-d H:i:s" }}</h4> {% endif %} </li> <li>{{ comment.content|safe }}</li> </ul> <hr/> {% endfor %} {% else %} <ul class="comment"> <p>尚未人发表评论</p> </ul> {% endif %} <span>评论 </span> <form action="{% url 'blogs:saveComment' %}" method="post" onsubmit="return toVaild()"> {% csrf_token %} <ul class="comment"> <li><textarea name="blogcomment"></textarea></li> <li><input type="submit" value="提交"></li> </ul> </form> {% endblock %}content页面要显示两部份内容,其一是博客主体内容,包括博客的标题和正文,其二是博客的相关评论,以及发布评论的表单。在博客的正文部分有一个地方须要注意,就是咱们的博客是以富文本形式存储在数据库中的,所以博客正文包含不少html标记。在显示的时候咱们须要用|safe标记来代表这个内容是安全的,能够将内容转义以获得正确的格式。若是没有加上|safe,咱们看到的就是带有html标记的原始数据。
下面来让咱们看一下如何发布博客。为了实现博客的图文混排,咱们选用CKeditor这一款编辑器做为博客的编辑器。这款编辑器是一种免费的富文本编辑器,体积小巧,而且配置方便,支持可见即所得的图文混排,如图所示:
此外,CKeditor还支持多种配置,以实现更强大的功能。在这里我选用了默认的配置。
为了使用CKeditor,咱们须要到官网上下载CKeditor,而后把解压后获得的ckeditor目录拷贝到咱们在myblog目录下创建的static目录下。此外,咱们还要安装Django的ckeditor库django-ckeditor,这样咱们就能够在工程中使用ckeditor了(static的部分见本系列第三篇)。须要注意的是,ckeditor须要python的pillow库做为处理图像的后端,所以咱们还须要安装pillow。
为了使用ckeditor,咱们须要在myblog/settings.py中添加一些配置,以下所示:
# myblog/settings.py # ... INSTALLED_APPS = [ 'ckeditor', 'ckeditor_uploader', # ... ] # ... CKEDITOR_JQUERY_URL = '/static/js/jquery-3.2.1.min.js' CKEDITOR_UPLOAD_PATH = 'blogimage/' CKEDITOR_IMAGE_BACKEND = 'pillow' # ...
咱们须要把ckeditor和它的上传器加入到INSTALLED_APPS中,此外咱们还要将ckeditor的上传图片路径和依赖的jquery库路径写在settings.py中。
在配置好CKeditor后,咱们就能够开始编写发布博客以及存储草稿的view函数了。咱们须要一个ModelForm来发布博客,所以咱们创建blogForm.py文件,并建好咱们的BlogForm表单:
# blogs/blogForm.py #-*- coding=utf-8 -*- from django import forms from django.forms import ModelForm from .models import Blog class BlogForm(ModelForm): class Meta: model = Blog fields = ['title','category','content']
# blogs/views.py def addBlog(request): if request.method == 'POST': if request.session.has_key('currentblogId'): tmpBlog = Blog.objects.get(id=request.session['currentblogId']) if tmpBlog: form = BlogForm(request.POST,instance=tmpBlog) tmpBlog = form.save(commit=False) tmpBlog.draft = False tmpBlog.save() result_info = 'Success' else: form = BlogForm(request.POST) if form.is_valid(): newBlog = form.save(commit=False) newBlog.auther = Users.objects.get(username=request.session['username']) newBlog.draft = False category = Category.objects.get(categoryname=newBlog.category) category.blogcount = category.blogcount+1 category.save() newBlog.save() result_info = 'Success' del request.session['currentblogId'] else: form = BlogForm(request.POST) if form.is_valid(): newBlog = form.save(commit=False) newBlog.auther = Users.objects.get(username=request.session['username']) newBlog.draft = False category = Category.objects.get(categoryname=newBlog.category) category.blogcount = category.blogcount + 1 category.save() newBlog.save() result_info = 'Success' return HttpResponseRedirect(reverse('blogs:addblogResult', kwargs={'info': result_info})) else: form = BlogForm() return render(request, 'blogs/addblog.html', {'form':form}) def addBlogResult(request,info): tipMessage='' if info == 'Success': tipMessage = '博文已成功发表!' else: tipMessage = info parameters = {'info':tipMessage} return render(request, 'blogs/addblogResult.html', parameters) def saveDraft(request): if request.method == 'POST': blogId = request.session.get('currentblogId','-1') if blogId!='-1': try: tmpBlog = Blog.objects.get(id=blogId) form = BlogForm(request.POST, instance=tmpBlog) tmpBlog = form.save(commit=False) tmpBlog.draft = True tmpBlog.save() result_info = u'文章已保存于草稿箱中。' return HttpResponse(result_info) except Blog.DoesNotExist: form = BlogForm(request.POST) if form.is_valid(): newBlog = form.save(commit=False) newBlog.auther = Users.objects.get(username=request.session['username']) category = Category.objects.get(categoryname=newBlog.category) category.blogcount = category.blogcount+1 category.save() newBlog.save() request.session['currentblogId'] = newBlog.id result_info = u'文章已保存于草稿箱中。' return HttpResponse(result_info) return HttpResponse('test') else: form = BlogForm(request.POST) if form.is_valid(): newBlog = form.save(commit=False) newBlog.auther = Users.objects.get(username=request.session['username']) category = Category.objects.get(categoryname=newBlog.category) category.blogcount = category.blogcount + 1 category.save() newBlog.save() request.session['currentblogId'] = newBlog.id result_info = u'文章已保存于草稿箱中。' return HttpResponse(result_info) else: return HttpResponse('test') else: return HttpResponse('test')发布博客的函数分为三部分:addBlog用于发布博客,addBlogResult是在发布博客成功后要跳转的页面,而saveDraft是将博客存储为草稿的函数。
首先来看saveDraft函数的实现。该函数要考虑到两种状况:一、博客的第一次保存;二、博客已存为草稿下的再次保存。对于博客的第一次保存,咱们能够直接调用BlogForm的方法进行保存,而且在保存以后将该篇博客的id存储在session['currentblogId']中,以便状况2的使用;对于已经存储为草稿的博客,咱们只需根据session['currentblogId']的值获得当前博客,并再次调用save方法保存便可。在这个函数中,咱们使用了一种新的方法来初始化BlogForm:form = BlogForm(request.POST,instance=tmpBlog),这是由于咱们能够用一个已存在的对象来初始化一个ModelForm。另外,因为咱们稍后在前端会以Ajax的方式来调用这个函数,所以咱们没有使用render函数来渲染某个页面,而只是简单地返回了一个HttpResponse对象。
再来看addBlog函数的实现。该函数也要考虑两种状况:一、博客已存为草稿后发布;二、博客没有存储为草稿直接发布。对于第一种状况,咱们能够直接从session['currentblogId']中获得当前博客的id值,再将其draft的值改成False后调用save方法保存。注意这里的第一次save函数的调用,咱们传入了commit=False的选项,这表示这次save并不会真正加载到数据库中,在以后咱们还能够对对象的字段值进行修改后再真正commit到数据库中。另一个须要注意的地方是,咱们不只要在Blog表里插入数据,也要更新category model的blogcount值,使之+1。
addBlogResult函数没什么可说的,就是根据addBlog的结果进行的跳转页面。
以上三个函数的url配置以下:
# blog/urls.py urlpatterns = [ # ... url(r'^addblog/$', views.addBlog, name='addblog'), url(r'^addblogResult/(?P<info>.*)$', views.addBlogResult, name='addblogResult'), url(r'^saveDraft/$',views.saveDraft,name='saveDraft'), # ... ]这样,咱们的发布博客、存储草稿和正文的后端部分就完成了。在下篇博客中,将继续介绍这三个功能的前端页面实现~