這篇應該是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'), # ... ]這樣,咱們的發佈博客、存儲草稿和正文的後端部分就完成了。在下篇博客中,將繼續介紹這三個功能的前端頁面實現~