實例:django實現博客小項目

封面.jpg

前面已經對DTL(Django Template Language)語言基礎用法進行了講解,現在我們開始做一個博客小項目。

1. 準備工作

首先,我們整理一下整個流程,一個簡單的博客項目包含博客列表頁、博客詳情頁、博客編輯頁、添加博客頁,其頁面設計如下:

頁面設計(Page):
1.博客列表頁:所有博客文章列表、寫博客按鈕                 blog/index                          index.html
2.博客詳情頁:標題、內容、編輯按鈕                        blog/article/article_id             article_page.html
3.博客編輯頁:form表單、標題、內容、提交按鈕               blog/article/edit                   article_edit_page.html
4.添加博客頁:form表單、標題、內容、提交按鈕

事件(Action):
編輯博客提交Action:post提交(博客標題、id、內容)
新增博客提交Action:post提交(博客標題、內容)

下面,我們來梳理數據及頁面跳轉:


頁面及數據流轉

如上,由于編輯博客、新增博客非常相似,我們就將兩個頁面合并成一個。

2. 開始編碼

1.首先我們創建并配置項目的基本信息

打來終端,找到我們存放python項目的目錄python-works(這個存儲地址根據你個人喜好選擇),然后在終端中輸入一下命令:

django-admin startproject myblog

然后cd 到myblog的目錄下,執行以下命令

python3 manage.py runserver
啟動項目

用瀏覽器打開http://127.0.0.1:8000/,出現歡迎界面,說明項目創建成功:


歡迎界面

還記得如何把界面修改成中文的嗎,進入settings.py,修改以下代碼

LANGUAGE_CODE = 'zh-hans'         #將'en-us'改為'zh-hans' 

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

再刷新一下界面,界面即變成中文,此時準備工作已經做完了,接下來我們開始創建博客應用。

2.創建博客應用

仍然打開終端,進入到myblog目錄下,輸入以下命令創建一個名為blog的應用:

python3 manage.py startapp  blog

此時項目中多了一個名為blog的應用:


blog創建成功

打開settings.py文件,將blog添加到應用列表中

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',                                          #添加blog應用到應用列表中
]

此時,我們還未創建任何頁面,現在blog下的views.py中定義一個index頁,輸出一行文字,如下

from django.shortcuts import render
from django.http import HttpResponse


def index(request):
    return HttpResponse('我是博客應用頁面!')

然后,在myblog文件夾下的urls.py文件中添加一個路徑:

from django.conf.urls import url
from django.contrib import admin
from blog.views import index

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index)           #添加index的路徑
]

再重啟一下服務,我們訪問http://127.0.0.1:8000/index,界面上顯示了正確的內容

index

在前面的頁面設計中,我們想訪問的是blog/index,而不是直接訪問index,這個怎么做呢?其實在前面的文章 知識詳解2:django之項目下創建應用及注意事項 中有詳細的說明,在此不再贅述。我們現在blog下創建一個名為blog_urls.py的文件,然后輸入以下內容:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'^index/', views.index),
]

然后,將系統的urls.py文件修改為

from django.conf.urls import url, include
from django.contrib import admin
from blog.views import index


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', include('blog.blog_urls')),
]

為什么這么干,可以查看前面的文章 知識詳解2:django之項目下創建應用及注意事項

再次刷新一下頁面,訪問http://127.0.0.1:8000/blog/index,即顯示了正確的內容。至此,blog項目的準備工作做完了,接下來我們來創建頁面。

3.博客列表頁

前面已經在界面上顯示了一行文字,但是這個不是我們想要的,我們需要顯示一個界面,然后在界面上顯示數據庫的文章列表,前文 知識詳解3:django之創建Template文件 詳細講解了如何創建頁面。
首先,我們在blog文件夾下創建一個名為templates的文件夾,并添加一個index.html文件,然后修改blog下的views.py的內容,讓views返回index.html

from django.shortcuts import render
from django.http import HttpResponse


def index(request):
    return render(request, 'index.html')              #render函數有三個參數,此處只傳了兩個

此時,訪問http://127.0.0.1:8000/blog/index,顯示的是一個空頁面,這是因為我們既沒有向index.html文件中傳遞數據,也沒有在index.html中寫顯示數據的邏輯,接下來,我們準備數據源。前文 知識詳解4:django之models與數據表 中有詳細的解釋。
在blog的models.py中創建博客對象,包含title、content兩個字端(系統默認會添加一個id),models.py的代碼如下:

from django.db import models


class Article(models.Model):
    title = models.CharField(max_length=32, default='')
    # 文章正文,使用的是TextField
    # 存儲比較短的字符串可以使用 CharField,但對于文章的正文來說可能會是一大段文本,因此使用 TextField 來存儲大段文本。
    content = models.TextField(null=True)

然后在終端中執行以下命令:

python3 manage.py makemigrations

成功后繼續執行

python3 manage.py migrate
執行結果

接下來,我們為該項目創建一個名為yucanghai,密碼為root123456的超級用戶,在登錄django后臺之前,我們需要在blog下的admin.py中配置一下

from django.contrib import admin
from .models import Article


admin.site.register(Article)      #必須增加這一行,否則進入后臺什么都沒有

接下來,我們登錄后臺增加幾篇博客,如下:


博客列表

此處顯示的全部是Article object,我們調整一下models.py的代碼:

from django.db import models
from django.contrib.auth.models import User


class Article(models.Model):
    title = models.CharField(max_length=32, default='')
    # 文章正文,使用的是TextField
    # 存儲比較短的字符串可以使用 CharField,但對于文章的正文來說可能會是一大段文本,因此使用 TextField 來存儲大段文本。
    content = models.TextField(null=True)
    
    def __str__(self):    #增加該方法
        return self.title

刷新界面,顯示如下:


正確顯示.png

現在,我們需要將數據庫中的博客數據讀取出來,顯示在index.html文件中。在blog下的views.py文件中讀取數據并傳遞給index.html,調整views.py代碼如下:

from django.shortcuts import render
from django.http import HttpResponse
from . import models


def index(request):
    articles = models.Article.objects.all() 
    #這里是取所有,如果取某一個article = models.Article.objects.get(pk=1) 

    return render(request, 'index.html', {'articles': articles})

修改index.html文件,將內容顯示出來:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body>
<h3>我的博客列表:</h3>
    {% for article in articles %}    #這是DTL的語法,
        <a href=""> {{ article.title }} </a>    #此處先不添加跳轉頁面,稍后再添加
        <br/> 
    {% endfor %}    #for循環必須以這個結尾

</body>
</html>

好,我們的博客首頁已經完成了,接下來做博客詳情頁。

4.博客詳情頁

在templates文件夾下新建一個名為article_page.html的頁面,這個頁面的作用就是顯示博客的詳細內容,這個頁面的數據就是上一頁面傳遞過來的博客對象,我們先編寫顯示代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>博客詳情頁</title>
</head>
<body>
    <h3>標題: {{ article.title }}</h3>
    </br>
    <h3>內容: {{ article.content }}</h3>
</body>
</html>

現在的問題是要從views中將article對象傳遞給該頁面,所以我們在view.py中增加:

from django.shortcuts import render
from django.http import HttpResponse
from . import models


def index(request):
    articles = models.Article.objects.all()
    return render(request, 'index.html', {'articles': articles})


def article_page(request, article_id):        #根據博客id獲取博客內容
    article = models.Article.objects.get(pk=article_id)
    return render(request, 'article_page.html', {'article': article})

根據上一個頁面傳遞的article_id獲取到這個article對象,并傳遞給html頁面顯示出來,那這個article_id該怎么傳遞過來呢?參照前面的頁面設計中,我們的設計是訪問博客詳情頁的地址是 http://127.0.0.1:8000/blog/article/1 ,這個id可以從訪問的URL中傳遞,我們來修改傳遞的url,打開blog_urls.py文件:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'^index/', views.index),
    url(r'^article/(?P<article_id>[0-9])$', views.article_page),
]

這里正則表達式為何這么些,會在我的文章中有專門的模塊來講,知道這個article_id是這么傳遞的即可,運行一下服務器,我們訪問一下http://127.0.0.1:8000/blog/article/1 是不是可以顯示正確的內容了呢。這里需要注意的是DTL下,models自動生成的id的下標是從1開始的,而不是0.
兩個頁面做好了,我們現在需要在index.html中增加點擊標題的跳轉鏈接

第一種方式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body>
<h3>我的博客列表:</h3>
    {% for article in articles %}
        <a href="../article/{{ article.id }}"> {{ article.title }} </a>
        <br/>
    {% endfor %}

</body>
</html>

再運行一下,刷新頁面,可以正常跳轉了。但是,這種跳轉邏輯是最基礎的,但是當應用中有很多頁面的時候,這種方式顯的不靈活,現在看第二種方法。

第二種方式:

首先,在項目的urls.py中為博客應用增加一個namespace:

from django.conf.urls import url, include
from django.contrib import admin
from blog.views import index


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/', include('blog.blog_urls', namespace='blog')),
]

然后,在blog_urls.py中為博客詳情頁增加一個namespace:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'^index/', views.index),
    url(r'^article/(?P<article_id>[0-9]+)$', views.article_page, name='article_page'),
]

這兩步操作相當于給博客應用和博客詳情頁面增加一個別名,為后面調用提供一種快捷方式。我們再回到index.html中,修改href:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body>
<h3>我的博客列表:</h3>
    {% for article in articles %}
        <a href="{% url 'blog:article_page' article.id %}"> {{ article.title }} </a>
        <br/>
    {% endfor %}

</body>
</html>

我們將href修改成DTL的另外一種表達方式,href="{% url 路徑 參數 %}",其中路徑就是我們前面定義的不同層級namespace由大到小排列,如果沒有參數可以不寫。
至此,我們的博客首頁和詳情頁已經完善,現在我們需要在博客首頁增加一個新增按鈕用于跳轉到發博客頁面,在博客詳情頁增加一個編輯按鈕,用于跳轉到博客編輯頁面,我們先將index.html修改如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body>
<h3>我的博客列表:</h3>
    {% for article in articles %}
        <a href="{% url 'blog:article_page' article.id %}"> {{ article.title }} </a>
        <br/>
    {% endfor %}

    </br>
    </br>
    <a href="">寫博客</a>

</body>
</html>

博客詳情頁article_page.html修改如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>博客詳情頁</title>
</head>
<body>
    <h3>標題: {{ article.title }}</h3>
    </br>
    <h3>內容: {{ article.content }}</h3>
    </br>
    </br>
    <a href="">編輯</a>

</body>
</html>

新增博客和編輯博客頁面相似,我們就使用同一個頁面,接下來我們編寫編輯頁面。

5.編輯博客頁

首先,我們在templates下新建編輯頁面article_edit_page.html頁面

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>編輯博客頁面</title>
</head>
<form action="" method="post">
    <label>博客標題
       <input type="text" name="title">
    </label>
    </br>
    <label>博客內容
       <input type="text" name="content">
    </label>
    </br>
    <input type="submit" value="提交">
</form>
<body>

</body>
</html>

這里是定義了一個form表單,用于填寫博客標題、內容,并提交,在form中定義了一個action,這個是當提交按鈕點擊時觸發的事件,暫時我們先空著。接下來,我們在views中增加編輯頁面的響應函數:

from django.shortcuts import render
from django.http import HttpResponse
from . import models


def index(request):
    articles = models.Article.objects.all()
    return render(request, 'index.html', {'articles': articles})


def article_page(request, article_id):
    article = models.Article.objects.get(pk=article_id)
    return render(request, 'article_page.html', {'article': article})


def article_edit_page(request):
    return render(request, 'blog/article_edit_page.html')  #暫時不傳遞參數

然后在blog下的blog_urls.py中增加編輯頁面的訪問路徑:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'^index/', views.index),
    url(r'^article/(?P<article_id>[0-9]+)$', views.article_page, name='article_page'),
    url(r'^article/edit/$', views.article_edit_page),
]

OK,我們訪問一下http://127.0.0.1:8000/blog/article/edit/ ,此時編輯頁面就顯示出來了,這個頁面暫時是空頁面,并為傳遞任何參數:

編輯頁

下面,我們需要增加提交按鈕的響應事件,提交按鈕點擊時需要有一個響應函數,并有訪問該函數的路徑,所以我們在viwes中增加響應函數:

from django.shortcuts import render
from django.http import HttpResponse
from . import models


def index(request):
    articles = models.Article.objects.all()
    return render(request, 'index.html', {'articles': articles})


def article_page(request, article_id):
    article = models.Article.objects.get(pk=article_id)
    return render(request, 'article_page.html', {'article': article})


def article_edit_page(request):
    return render(request, 'article_edit_page.html')


def article_edit_page_action(request):
    title = request.POST.get('title', '默認標題')     ##get是根據參數名稱從form表單頁獲取內容
    content = request.POST.get('content', '默認內容')
    ##保存數據
    models.Article.objects.create(title=title, content=content)
    ##數據保存完成,返回首頁
    articles = models.Article.objects.all()
    return render(request, 'index.html', {'articles': articles})

再為響應函數增加訪問路徑,打開blog下的blog_urls.py,修改如下:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'^index/', views.index),
    url(r'^article/(?P<article_id>[0-9]+)$', views.article_page, name='article_page'),
    url(r'^article/edit/$', views.article_edit_page, name='article_edit_page'),
    url(r'^article/edit/action$', views.article_edit_page_action, name='article_edit_page_action'), 
]

為action增加一個訪問路徑,并定義一個namespace,接下來,我們在article_edit_page.html中設置action:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>編輯博客頁面</title>
</head>
<form action="{% url 'blog:article_edit_page_action' %}" method="post">
    {% csrf_token %}   ##這個必須添加,否則訪問時報403錯誤
    <label>博客標題
       <input type="text" name="title">
    </label>
    </br>
    <label>博客內容
       <input type="text" name="content">
    </label>
    </br>
    <input type="submit" value="提交">
</form>
<body>

</body>
</html>

和前面一樣,為action增加響應函數的路徑,這里不需要參數。csrf_token是一種網絡的攻擊方式,具體可以參考CSRF Token介紹與應對策略。現在需要為首頁的寫博客按鈕增加跳轉邏輯,打開index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body>
<h3>我的博客列表:</h3>
    {% for article in articles %}
        <a href="{% url 'blog:article_page' article.id %}"> {{ article.title }} </a>
        <br/>
    {% endfor %}

    </br>
    </br>
    <a href=" {% url 'blog:article_edit_page' %} ">寫博客</a>

</body>
</html>

此時刷新界面,在首頁點擊“寫博客”即可跳轉到編輯博客頁面,填寫內容提交會保存數據并跳轉到首頁面。

現在需要編寫博客詳情頁的編輯按鈕的功能,點擊編輯按鈕需要將這篇文章的id傳遞到編輯頁面,在編輯頁面填充該博客。既然寫新博客和編輯都是跳轉到同一個頁面,而編輯時需要傳遞參數,而寫博客不需要傳遞參數,為了兼容,我們都傳遞一個參數,寫新博客時傳遞一個0,以做區別,首先,我們修改views下的編輯頁面響應函數:

from django.shortcuts import render
from django.http import HttpResponse
from . import models


def index(request):
    articles = models.Article.objects.all()
    return render(request, 'index.html', {'articles': articles})


def article_page(request, article_id):
    article = models.Article.objects.get(pk=article_id)
    return render(request, 'article_page.html', {'article': article})


def article_edit_page(request, article_id):
    #str方法將參數轉化為字符串,避免因傳遞類型差異引起的錯誤
    # 0代表是新增博客,否則是編輯博客,編輯博客時需要傳遞博客對象到頁面并顯示
    if str(article_id) == '0':
        return render(request, 'article_edit_page.html')
    article = models.Article.objects.get(pk=article_id)
    return render(request, 'article_edit_page.html', {'article': article})


def article_edit_page_action(request):
    title = request.POST.get('title', '默認標題')     ##get是根據參數名稱從form表單頁獲取內容
    content = request.POST.get('content', '默認內容')
    ##保存數據
    models.Article.objects.create(title=title, content=content)
    ##數據保存完成,返回首頁
    articles = models.Article.objects.all()
    return render(request, 'index.html', {'articles': articles})

這里是傳遞的有參數的,我們需要修改blog_urls下修改博客詳情頁的訪問路徑,增加參數

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'^index/', views.index),
    url(r'^article/(?P<article_id>[0-9]+)$', views.article_page, name='article_page'),
    url(r'^article/edit/(?P<article_id>[0-9]+)$', views.article_edit_page, name='article_edit_page'),  #路徑中傳遞頁碼
    url(r'^article/edit/action$', views.article_edit_page_action, name='article_edit_page_action'),
]

然后修改index.html,在點擊寫博客時傳遞0

 <a href=" {% url 'blog:article_edit_page' 0 %} ">寫博客</a>

修改article_page.html,為編輯按鈕增加參數:

a href=" {% url 'blog:article_edit_page' article.id %} ">編輯</a>

接下來,我們修改article_page_edit.html,當從博客詳情頁進入時顯示博客內容,從首頁進入時不顯示內容:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>編輯博客頁面</title>
</head>
<form action="{% url 'blog:article_edit_page_action' %}" method="post">
    {% csrf_token %}

    {% if article %}
       <label>博客標題
           <input type="text" name="title" value="{{ article.title }}">
        </label>
        </br>
        <label>博客內容
           <input type="text" name="content", value="{{ article.content }}">
        </label>
        </br>
        <input type="submit" value="提交">
    {% else %}
     <label>博客標題
           <input type="text" name="title">
        </label>
        </br>
        <label>博客內容
           <input type="text" name="content">
        </label>
        </br>
        <input type="submit" value="提交">
    {% endif %}
</form>
<body>

</body>
</html>

很好理解是不是,有博客就顯示,沒有就不顯示。現在問題來了,在編輯頁面,點擊提交按鈕到底是新增還是保存呢,現在我們需要在views中修改action的響應函數:

from django.shortcuts import render
from django.http import HttpResponse
from . import models


def index(request):
    articles = models.Article.objects.all()
    return render(request, 'index.html', {'articles': articles})


def article_page(request, article_id):
    article = models.Article.objects.get(pk=article_id)
    return render(request, 'article_page.html', {'article': article})


def article_edit_page(request, article_id):
    #str方法將參數轉化為字符串,避免因傳遞類型差異引起的錯誤
    # -1代表是新增博客,否則是編輯博客,編輯博客時需要傳遞博客對象到頁面并顯示
    if str(article_id) == '0':
        return render(request, 'article_edit_page.html')
    article = models.Article.objects.get(pk=article_id)
    return render(request, 'article_edit_page.html', {'article': article})


def article_edit_page_action(request):
    title = request.POST.get('title', '默認標題')     ##get是根據參數名稱從form表單頁獲取內容
    content = request.POST.get('content', '默認內容')
    article_id = request.POST.get('article_id_hidden', '0') ##隱藏參數,參數是在web中定義
    ##保存數據
    ##如果是0,標記新增,使用create方法,否則使用save方法
    ##新增是返回首頁,編輯是返回詳情頁
    if str(article_id) == '0':
        models.Article.objects.create(title=title, content=content)
        ##數據保存完成,返回首頁
        articles = models.Article.objects.all()
        return render(request, 'index.html', {'articles': articles})
    article = models.Article.objects.get(pk=article_id)
    article.title = title
    article.content = content
    article.save()
    return render(request, 'article_page.html', {'article': article})

然后我們修改article_page_edit.html頁面,處理傳遞id和不傳遞id時提交事件的響應方法:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>編輯博客頁面</title>
</head>
<form action="{% url 'blog:article_edit_page_action' %}" method="post">

    {% csrf_token %}

    {% if article %}
       <input type="hidden" name="article_id_hidden"  value="{{ article.id }}">
       <label>博客標題
           <input type="text" name="title" value="{{ article.title }}">
        </label>
        </br>
        <label>博客內容
           <input type="text" name="content", value="{{ article.content }}">
        </label>
        </br>
        <input type="submit" value="提交">
    {% else %}
     <input type="hidden" name="article_id_hidden"  value="0">
     <label>博客標題
           <input type="text" name="title">
        </label>
        </br>
        <label>博客內容
           <input type="text" name="content">
        </label>
        </br>
        <input type="submit" value="提交">
    {% endif %}
</form>
<body>

</body>
</html>

運行一下程序,是不是新增和編輯功能都正常了呢,??!好了,整個博客小項目編寫完成了。

點擊下載完整的項目代碼

3.小結

這個小demo非常基礎,主要是講解DTL語言的用法。從應用創建的設置,到應用中頁面的顯示、事件的處理,在后面的文章中我會寫比較復雜的項目。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容