上一篇我們還留了一個小問題沒有解決,其實這個問題我是特意留到這一篇來講的,請往下看 ⇩
現在我們的博客已經具備了基本的頁面,但是如果我想發表一篇新的文章的話,還得到數據庫裏手動添加。
而且如果我們想修改一篇文章的話也很麻煩,這時候一個後臺管理界面就很有用了。
一、需求分析
在添加後臺管理界面之前,讓我們想一想需要一些什麼功能:
- 能夠添加文章
- 能夠修改文章
- 能夠刪除文章
還有一些功能,比如在線編輯、實時預覽、markdown支持我們以後再添加。
現在我們已經把功能列出來了,接下來就是分析一下需要什麼頁面:
- 查看所有文章的頁面,在這個頁面裏應該有修改和刪除文章的選項,還要有添加新文章的選項
- 點擊修改文章應該出現編輯頁面
- 點擊刪除文章應該出現確認頁面
- 點擊添加文章應該出現文章編輯頁面,這個和修改文章的頁面應該是相同的
然後在修改、添加、刪除文章後應該返回文章列表。
二、顯示文章頁面
確定下來頁面我們就可以開始敲代碼了,按照頁面之間的邏輯關係,我們先完成顯示文章列表的頁面。
這個頁面和我們的首頁很相似,我們只需要添加幾個按鈕即可:
{% extends 'myblog/base.html' %} {% block title %}Post List{% endblock %} {% block main %} <ul class="list-unstyled"> {% for post in pagedata.post_list %} <li> <div class="row"> <div class="col-lg-10"> <h1><a href="{{post.get_absolute_url}}">{{post.title}}</a></h1> <span class="d-block">{{post.tags}}</span> <span class="d-block">{{post.get_format_date}}</span> <p>{{post.get_brief_content}}</p> </div> <div class="col-lg-2"> <div class="row"> <div class="col"><a href="#">修改</a></div> </div> <div class="row"> <div class="col"><a href="#">刪除</a></div> </div> </div> </div> </li> {% endfor %} </ul> {% endblock %}
再把鏈接和視圖添加上去,運行一下看看效果:
三、添加文章功能
添加一個新建文章的按鈕:
現在按鈕的位置都添加好了,我們分別爲每個按鈕添加鏈接,首先是添加文章的按鈕,這個鏈接是一個固定的值,我們可以直接寫到頁面中去(當然,等以後頁面多了肯定不能這麼寫,不過現在只有幾個頁面,所以問題不大)。
<div class="add-article"> <a class="text-success" href="/myblog/addArticle">添加文章</a> </div>
然後再創建一個視圖和模板,再添加鏈接:
修改 urls.py文件:
# myblog/urls.py from django.urls import re_path from . import views urlpatterns = [ re_path(r'^index/{0,1}$', views.index), re_path(r'^article/\d{4,4}/\d{1,2}/(?P<title>.+)/{0,1}$', views.article), re_path(r'^articles/list/{0,1}$', views.article_list), re_path(r'^addArticle/{0,1}$', views.addArticle), ]
創建一個模板文件 add_article.html:
{% extends 'myblog/base.html' %} {% block title %}Add Article{% endblock %} {% block main %} <div class="row"> <div class="col"> <form action="/myblog/addArticle" method="post"> {% csrf_token %} <div class="row my-3"> <div class="col"> <label for="title">標題</label> <input type="text" name="title" id="title"> </div> </div> <div class="row my-3"> <div class="col"> <label for="tags">標籤</label> <input type="text" name="tags" id="tags"> </div> </div> <div class="row my-3"> <div class="col"> <label for="content">正文</label> <textarea class="d-block" type="text" name="content" id="content"></textarea> </div> </div> <div class="row my-3"> <div class="col"> <button type="submit">提交</button> </div> </div> </form> </div> </div> {% endblock %}
再添加一個視圖函數 addArticle:
def addArticle(request): if request.method == 'GET': return render(request, 'myblog/add_article.html') elif request.method == 'POST': title = request.POST['title'] key = abs(hash(title)) tags = request.POST['tags'] content = request.POST['content'] date = datetime.datetime.now().strftime('%Y-%m-%d') Post(key=key, title=title, tags=tags, content=content, date=date).save() return HttpResponseRedirect('/myblog/articles/list/')
當請求方式爲 get請求時,addArticle函數會返回 add_article頁面:
當請求方式爲 post時,也就是點擊提交後,addArticle函數會接收頁面提交的數據,並將其保存到數據庫中,然後重定向到 article_list頁面查看添加結果。
注意到在保存到數據庫時多了一個 key參數。
這就是我們用來替換 title用來查找文章的值,key是由 title經過哈希運算得到的值,我們可以認爲 key和 title是一一對應的。
這樣文章鏈接就可以根據 key來生成。
要應用這個變化,我們還需要修改一些文件:
首先修改文章的鏈接:
re_path(r'^article/\d{4,4}/\d{1,2}/(?P<key>\d+)/{0,1}$', views.article),
然後修改 Post類的 get_absolute_url方法:
def get_absolute_url(self): return f'/myblog/article/{self.date.year}/{self.date.month}/{self.key}'
注意:
這裏鏈接以 ” / “開頭說明該鏈接是相對於網站根目錄的鏈接,即最終的鏈接是:
http://127.0.0.1:8000/myblog/article/2018/8/1/45555555544
如果不以 ” /“開頭則說明該鏈接是相對於當前頁面的鏈接,假設當前頁面是
http://127.0.0.1:8000/myblog/index
那麼最終的鏈接是:
http://127.0.0.1:8000/myblog/myblog/article/2018/8/1/45555555544
可以看到,myblog重複了兩次,由於當前頁面的不確定性,所以我們在所有的頁面裏都使用帶反斜槓的鏈接。
改了上面兩處還不夠,我們還需要改一下 article視圖函數,其實就是將 title改爲了 key:
def article(request, key): post = Post.objects.filter(key=key).get() return render( request, 'myblog/article.html', {'pagedata': {'post': post} } )
接下來訪問一下文章詳情頁面 :
這樣我們就把添加文章的功能給做好了,接下來我們再來完成修改文章的功能。
四、修改文章功能
首先我們先添加一個鏈接:
re_path(r'^modifyArticle/{0,1}$', views.modifyArticle)
再修改 articles_list.html模板文件,爲每篇文章添加對應的修改鏈接:
<div class="row"> <div class="col text-info"> <a href="{{post.get_modify_url}}">修改</a> </div> </div>
再給 Post類添加 get_modify_url方法:
def get_modify_url(self): return f'/modifyArticle/{self.key}'
做完這些我們還需要添加一個 modify_article模板:
{% extends 'myblog/base.html' %} {% block title %}Modify Article{% endblock %} {% block main %} <div class="row"> <div class="col"> <form action="{{pagedata.post.get_modify_url}}" method="post"> {% csrf_token %} <div class="row my-3"> <div class="col"> <label for="title">標題</label> <input type="text" name="title" id="title" value="{{pagedata.post.title}}"> </div> </div> <div class="row my-3"> <div class="col"> <label for="tags">標籤</label> <input type="text" name="tags" id="tags" value="{{pagedata.post.tags}}"> </div> </div> <div class="row my-3"> <div class="col"> <label for="content">正文</label> <textarea class="d-block" type="text" name="content" id="content">{{pagedata.post.content}}</textarea> </div> </div> <input type="hidden" name="key" value="{{pagedata.post.key}}"> <div class="row my-3"> <div class="col"> <button type="submit">提交</button> </div> </div> </form> </div> </div> {% endblock %}
這個模板和 add_article模板基本一致,我們只做了一些小的改動。
我們爲每個 input標籤添加了一個 value,value的值就是文章對應屬性的值。
最後我們還需要添加一個視圖函數 modifyArticle來處理請求:
def modifyArticle(request, key): if request.method == 'GET': post_list = Post.objects.filter(key=key) if len(post_list) == 0: return HttpResponse('文章不存在') else: return render( request, 'myblog/modify_article.html', {'pagedata': {'post':post_list[0]} } ) elif request.method == 'POST': title = request.POST['title'] key = request.POST['key'] tags = request.POST['tags'] content = request.POST['content'] date = datetime.datetime.now().strftime('%Y-%m-%d') post = Post.objects.filter(key=key)[0] post.title = title post.tags = tags post.content = content post.date = date post.save() return HttpResponseRedirect('/myblog/articles/list/')
我們來測試一下:
現在只剩下刪除功能沒有做了,刪除功能其實很簡單,只需要添加一個確認頁面,然後在數據庫裏執行刪除操作即可,這些我就不再多寫了。
最後看看整體效果:
不過我們得後臺管理還有很多問題,比如:沒有進行身份驗證。 在下一篇我們會爲博客添加身份認證機制