用django來(lái)開發(fā)自己的博客

[TOC]

Django的MVC模式/MTV模式

Django緊緊地遵循MVC模式和敬,可以稱得上是一種MVC框架朝捆。 以下是Django中M炕贵、V和C各自的含義:

  • M:數(shù)據(jù)存取部分护糖,由django數(shù)據(jù)庫(kù)層處理晶疼;
  • V:選擇顯示哪些數(shù)據(jù)要顯示以及怎樣顯示的部分,由視圖和模板處理吧恃;
  • C:根據(jù)用戶輸入委派視圖的部分臊泰,由Django框架根據(jù)URLconf設(shè)置,對(duì)給定URL調(diào)用適當(dāng)?shù)腜ython函數(shù)蚜枢。

由于C由框架自行處理缸逃,而Django里更關(guān)注的是模型(Model)、模板(Template)和視圖(Views)厂抽,Django也被稱為MTV框架需频。在MTV開發(fā)模式中:

  • M:代表模型(Model),即數(shù)據(jù)存取層筷凤。該層處理與數(shù)據(jù)相關(guān)的所有事務(wù):如何存取昭殉、如何驗(yàn)證有效性、包含哪些行為以及數(shù)據(jù)之間的關(guān)系等藐守;
  • T:代表模板(Template)挪丢,即表現(xiàn)層。該層處理與表現(xiàn)相關(guān)的決定:如何在頁(yè)面或其他類型文檔中進(jìn)行顯示卢厂。
  • V:代表視圖(View)乾蓬,即業(yè)務(wù)邏輯層。該層包含存取模型及調(diào)取恰當(dāng)模板的相關(guān)邏輯慎恒。你可以把它看作模型與模板之間的橋梁任内。

(以上摘自《The Django Book》)

完整的開發(fā)過(guò)程

1. 創(chuàng)建開發(fā)環(huán)境

mkvirtualenv myblog  # 創(chuàng)建虛擬環(huán)境
pip install django  # 安裝最新版的django-1.10.6

2. 創(chuàng)建項(xiàng)目和app

django-admin.py startproject myblog
django-admin.py startapp blog  # 一個(gè)項(xiàng)目可以包含多個(gè)app

當(dāng)前目錄結(jié)構(gòu)如下圖所示


目錄結(jié)構(gòu)1.png

目錄結(jié)構(gòu)介紹

  • blog: app的目錄
  • migrations: 包含對(duì)模型定義與修改的遷移記錄;
  • admin.py: 對(duì) Django 站點(diǎn)管理的定制融柬;
  • apps.py: 包含對(duì) App 的配置死嗦;
  • models.py: 應(yīng)包含定義的模型;
  • tests.py: 包含單元測(cè)試粒氧;
  • views.py: 應(yīng)包含各種視圖越除。
  • myblog: 整個(gè)項(xiàng)目的配置目錄
  • settings.py: 項(xiàng)目的配置文件
  • urls.py: 總的urls配置文件
  • **wsgi.py **: 部署服務(wù)器時(shí)用到的文件

新定義的app加到settings.py中的INSTALL_APPS中
編輯setting文件,如下圖


深度截圖20170304154138.png

3. 連接數(shù)據(jù)庫(kù)

暫時(shí)先用默認(rèn)的sqlite3數(shù)據(jù)庫(kù),不需要特別的配置摘盆。以后需要再改成別的數(shù)據(jù)庫(kù)翼雀。


4. 建立模型

  • 文章模型
class Article(models.Model):
    STATUS = (
        ('0', '發(fā)布'),
        ('1', '草稿')
    )
    title = models.CharField(max_length=64, unique=True, verbose_name='標(biāo)題')
    abstract = models.TextField(verbose_name='摘要', max_length=54, blank=True, null=True, help_text="可選項(xiàng),若為空格則摘取正文前54個(gè)字符")
    body = models.TextField(verbose_name='內(nèi)容')
    # on_delete 當(dāng)指向的表被刪除時(shí)骡澈,將該項(xiàng)設(shè)為空
    category = models.ForeignKey('Category', verbose_name='分類', null=True, on_delete=models.SET_NULL)
    tags = models.ManyToManyField('Tag', verbose_name='標(biāo)簽', blank=True)
    url = models.CharField(max_length=255, verbose_name='鏈接', unique=True)
    status = models.CharField(default='0', max_length=1, choices=STATUS, verbose_name='文章狀態(tài)')
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='創(chuàng)建時(shí)間')
    last_modified_time = models.DateTimeField(auto_now=True, verbose_name='最近修改時(shí)間')

    class Meta:
        # Meta 包含一系列選項(xiàng)锅纺,這里的ordering表示排序, - 表示逆序
        # 即當(dāng)從數(shù)據(jù)庫(kù)中取出文章時(shí)掷空,以文章最后修改時(shí)間逆向排序
        verbose_name = '文章(Article)'
        verbose_name_plural = verbose_name
        ordering = ['-last_modified_time']

    def __str__(self):
        return self.title
  • 分類模型
class Category(models.Model):
    name = models.CharField(max_length=20, verbose_name='類名')
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='創(chuàng)建時(shí)間')
    last_modified_time = models.DateTimeField(auto_now=True, verbose_name='最近修改時(shí)間')

    class Meta:
        verbose_name = '分類(Category)'
        verbose_name_plural = verbose_name
        ordering = ['-created_time']

    def __str__(self):
        return self.name
  • 標(biāo)簽?zāi)P?/li>
class Tag(models.Model):
    name = models.CharField(max_length=20, verbose_name='標(biāo)簽名')
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='創(chuàng)建時(shí)間')
    last_modified_time = models.DateTimeField(auto_now=True, verbose_name='最近修改時(shí)間')

    class Meta:
        verbose_name = '標(biāo)簽(Tag)'
        verbose_name_plural = verbose_name
        ordering = ['-created_time']

    def __str__(self):
        return self.name
  • 評(píng)論模型
class Comment(models.Model):
    user_name = models.CharField(max_length=64, verbose_name='評(píng)論者名字')
    content = models.TextField(verbose_name='評(píng)論內(nèi)容')
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='評(píng)論時(shí)間')
    article = models.ForeignKey('Article', verbose_name='評(píng)論所屬文章', on_delete=models.CASCADE)

    def __str__(self):
        return self.content[:20]

模型只是利用 Django 提供的 ORM 完成對(duì)實(shí)際表結(jié)構(gòu)的映射肋殴,因此在完成模型定義后,我們需要將其真正同步到實(shí)際的數(shù)據(jù)庫(kù)中去坦弟。新版本的 Django 中护锤,該操作需要分成兩步,

  1. 生成 migrations:
    python manage.py makemigrations blog
    深度截圖20170304162344.png
  2. 進(jìn)行數(shù)據(jù)庫(kù)同步
    python manage.py migrate
    深度截圖20170304162405.png

可以用python manage.py shell進(jìn)入django shell對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作
如下圖所示

深度截圖20170304164013.png


5. 設(shè)置后臺(tái)管理

from django.contrib import admin
from .models import Article, Category, BlogComment, Tag

# Register your models here.

admin.site.register([Article, Category, BlogComment, Tag])

6. 設(shè)置分頁(yè)

Django提供了一些類來(lái)幫助你管理分頁(yè)的數(shù)據(jù)酿傍,這些類位于django/core/paginator.py中烙懦。
分頁(yè)需要使用到的的 API ,Django 官方文檔對(duì)此有十分詳細(xì)的介紹赤炒,請(qǐng)參考django文檔中分頁(yè)教程氯析。
盡管可以把分頁(yè)邏輯直接寫在視圖內(nèi),但是為了通用性莺褒,我們使用一點(diǎn)點(diǎn) Django 更加高級(jí)的技巧——模板標(biāo)簽(TemplateTags)掩缓。

模板標(biāo)簽介紹

為了使用模板標(biāo)簽,Django 要求我們先建立一個(gè) templatetags 文件夾遵岩,并在里面加上 init.py文件以指示 python 這是一個(gè)模塊(python 把含有該問(wèn)價(jià)的文件夾當(dāng)做一個(gè)模塊你辣,具體請(qǐng)參考任何一個(gè)關(guān)于 python 模塊的教程)。并且 templatetags 文件夾和你的 model.py尘执,views.py 文件是同級(jí)的舍哄,也就是說(shuō)你的目錄結(jié)構(gòu)看起來(lái)應(yīng)該是這樣:

app/
    __init__.py
    models.py
    templatetags/
        __init__.py
        app_extras.py
    views.py

分頁(yè)代碼

首先來(lái)回顧一下 Django 的模板系統(tǒng)是如何工作的,回想一下視圖函數(shù)的工作流程誊锭,視圖函數(shù)接收一個(gè) Http 請(qǐng)求表悬,經(jīng)過(guò)一系列處理,通常情況下其會(huì)渲染某個(gè)模板文件丧靡,把模板文件中的一些用 {{ }} 包裹的變量替換成從該視圖函數(shù)中相應(yīng)變量的值签孔。事實(shí)上在此過(guò)程中 Django 悄悄幫我們做了一些事情,它把視圖函數(shù)中的變量的值封裝在了一個(gè) Context (一般翻譯成上下文)對(duì)象中窘行,只要模板文件中的變量在 Context 中有對(duì)應(yīng)的值饥追,它就會(huì)被相應(yīng)的值替換。
因此罐盔,我們的程序可以這樣做:首先把取到的文章列表(官方術(shù)語(yǔ)是一個(gè) queryset)分頁(yè)但绕,用戶請(qǐng)求第幾頁(yè),我們就把第幾頁(yè)的文章列表傳遞給模板文件;另外還要根據(jù)上面的需求傳遞頁(yè)碼值給模板文件捏顺,這樣只要把模板文件中的變量替換成我們傳遞過(guò)去的值六孵,那么就達(dá)到本文開篇處那樣的分頁(yè)顯示效果了。
paginate.py

from django import template
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
// 這是分頁(yè)功能涉及的一些類和異常幅骄,官方文檔對(duì)此有詳細(xì)介紹劫窒。當(dāng)然從命名也可以直接看出它們的用途:Paginator(分頁(yè)),PageNotAnInteger(頁(yè)碼不是一個(gè)整數(shù)異常)拆座,EmptyPage(空的頁(yè)碼號(hào)異常)

register = template.Library()
// 這是定義模板標(biāo)簽要用到的

@register.simple_tag(takes_context=True)
// 這個(gè)裝飾器表明這個(gè)函數(shù)是一個(gè)模板標(biāo)簽主巍,takes_context = True 表示接收上下文對(duì)象,就是前面所說(shuō)的封裝了各種變量的 Context 對(duì)象挪凑。
def paginate(context, object_list, page_count):
    //context是Context 對(duì)象孕索,object_list是你要分頁(yè)的對(duì)象,page_count表示每頁(yè)的數(shù)量
    left = 3 # 當(dāng)前頁(yè)碼左邊顯示幾個(gè)頁(yè)碼號(hào) -1躏碳,比如3就顯示2個(gè)
    right = 3 # 當(dāng)前頁(yè)碼右邊顯示幾個(gè)頁(yè)碼號(hào) -1
    paginator = Paginator(object_list, page_count) # 通過(guò)object_list分頁(yè)對(duì)象
    page = context['request'].GET.get('page') # 從 Http 請(qǐng)求中獲取用戶請(qǐng)求的頁(yè)碼號(hào)
    try:
        object_list = paginator.page(page) # 根據(jù)頁(yè)碼號(hào)獲取第幾頁(yè)的數(shù)據(jù)
        context['current_page'] = int(page) # 把當(dāng)前頁(yè)封裝進(jìn)context(上下文)中
        pages = get_left(context['current_page'], left, paginator.num_pages) + get_right(context['current_page'], right, paginator.num_pages)
        // 調(diào)用了兩個(gè)輔助函數(shù)搞旭,根據(jù)當(dāng)前頁(yè)得到了左右的頁(yè)碼號(hào),比如設(shè)置成獲取左右兩邊2個(gè)頁(yè)碼號(hào)菇绵,那么假如當(dāng)前頁(yè)是5肄渗,則 pages = [3,4,5,6,7],當(dāng)然一些細(xì)節(jié)需要處理,比如如果當(dāng)前頁(yè)是2咬最,那么獲取的是pages = [1,2,3,4]
    except PageNotAnInteger:
        // 異常處理翎嫡,如果用戶傳遞的page值不是整數(shù),則把第一頁(yè)的值返回給他
        object_list = paginator.page(1)
        context['current_page'] = 1 # 當(dāng)前頁(yè)是1
        pages = get_right(context['current_page'], right, paginator.num_pages)
    except EmptyPage:
        // 如果用戶傳遞的 page 值是一個(gè)空值丹诀,那么把最后一頁(yè)的值返回給他
        object_list = paginator.page(paginator.num_pages)
        context['current_page'] = paginator.num_pages # 當(dāng)前頁(yè)是最后一頁(yè)钝的,num_pages的值是總分頁(yè)數(shù)
        pages = get_left(context['current_page'], left, paginator.num_pages)
    context['article_list'] = object_list # 把獲取到的分頁(yè)的數(shù)據(jù)封裝到上下文中
    context['pages'] = pages # 把頁(yè)碼號(hào)列表封裝進(jìn)去
    context['last_page'] = paginator.num_pages # 最后一頁(yè)的頁(yè)碼號(hào)
    context['first_page'] = 1 # 第一頁(yè)的頁(yè)碼號(hào)為1
    try:
        // 獲取 pages 列表第一個(gè)值和最后一個(gè)值,主要用于在是否該插入省略號(hào)的判斷铆遭,在模板文件中將會(huì)體會(huì)到它的用處硝桩。注意這里可能產(chǎn)生異常,因?yàn)閜ages可能是一個(gè)空列表枚荣,比如本身只有一個(gè)分頁(yè)碗脊,那么pages就為空,因?yàn)槲覀冇肋h(yuǎn)不會(huì)獲取頁(yè)碼為1的頁(yè)碼號(hào)(至少有1頁(yè)橄妆,1的頁(yè)碼號(hào)已經(jīng)固定寫在模板文件中)
        context['pages_first'] = pages[0]
        context['pages_last'] = pages[-1] + 1
        // +1的原因是為了方便判斷衙伶,在模板文件中將會(huì)體會(huì)到其作用。
    except IndexError:
        context['pages_first'] = 1 # 發(fā)生異常說(shuō)明只有1頁(yè)
        context['pages_last'] = 2 # 1 + 1 后的值
    return ''  # 必須加這個(gè)害碾,否則首頁(yè)會(huì)顯示個(gè)None

def get_left(current_page, left, num_pages):
    """
    輔助函數(shù)矢劲,獲取當(dāng)前頁(yè)碼的值得左邊兩個(gè)頁(yè)碼值,要注意一些細(xì)節(jié)慌随,比如不夠兩個(gè)那么最左取到2芬沉,為了方便處理躺同,包含當(dāng)前頁(yè)碼值,比如當(dāng)前頁(yè)碼值為5丸逸,那么pages = [3,4,5]
    """
    if current_page == 1:
        return []
    elif current_page == num_pages:
        l = [i - 1 for i in range(current_page, current_page - left, -1) if i - 1 > 1]
        l.sort()
        return l
    l = [i for i in range(current_page, current_page - left, -1) if i > 1]
    l.sort()
    return l

def get_right(current_page, right, num_pages):
    """
    輔助函數(shù)蹋艺,獲取當(dāng)前頁(yè)碼的值得右邊兩個(gè)頁(yè)碼值,要注意一些細(xì)節(jié)黄刚,比如不夠兩個(gè)那么最右取到最大頁(yè)碼值捎谨。不包含當(dāng)前頁(yè)碼值。比如當(dāng)前頁(yè)碼值為5憔维,那么pages = [6,7]
    """
    if current_page == num_pages:
        return []
    return [i + 1 for i in range(current_page, current_page + right - 1) if i < num_pages - 1]

模板文件

templates/blog/pagination.html

<div id="pagenavi" class="noselect">
    {% if article_list.has_previous %} # 判斷是否還有上一頁(yè)涛救,有的話要顯示一個(gè)上一頁(yè)按鈕
        <a class="previous-page" href="?page={{ article_list.previous_page_number }}">
            <span class="icon-previous"></span>上一頁(yè)
        </a>
    {% endif %}

    # 頁(yè)碼號(hào)為1永遠(yuǎn)顯示
    {% if first_page == current_page %} # 當(dāng)前頁(yè)就是第一頁(yè)
        <span class="first-page current">1</span>
    {% else %} # 否則的話,第一頁(yè)是可以點(diǎn)擊的埋同,點(diǎn)擊后通過(guò)?page=1的形式把頁(yè)碼號(hào)傳遞給視圖函數(shù)
        <a href="?page=1" class="first-page">1</a>
    {% endif %}

    {% if pages_first > 2 %} # 2以前的頁(yè)碼號(hào)要被顯示成省略號(hào)了
        <span>...</span>
    {% endif %}

    {% for page in pages %} # 通過(guò)for循環(huán)把pages中的值顯示出來(lái)
        {% if page == current_page %} # 是否當(dāng)前頁(yè)州叠,按鈕會(huì)顯示不同的樣式
            <span class="current">{{ page }}</span>
        {% else %}
            <a href="?page={{ page }}">{{ page }}</a>
        {% endif %}
    {% endfor %}

    # pages最后一個(gè)值+1的值小于最大頁(yè)碼號(hào)棵红,說(shuō)明有頁(yè)碼號(hào)需要被省略號(hào)替換
    {% if pages_last < last_page %}
        <span>...</span>
    {% endif %}

    # 永遠(yuǎn)顯示最后一頁(yè)的頁(yè)碼號(hào)凶赁,如果只有一頁(yè)則前面已經(jīng)顯示了1就不用再顯示了
    {% if last_page != 1 %}
        {% if last_page == current_page %}
            <span class="current">{{ last_page }}</span>
        {% else %}
            <a href="?page={{ last_page }}">{{ last_page }}</a>
        {% endif %}
    {% endif %}

    # 還有下一頁(yè),則顯示一個(gè)下一頁(yè)按鈕
    {% if article_list.has_next %}
        <a class="next-page" href="?page={{ article_list.next_page_number }}">
            下一頁(yè)<span class="icon-next"></span>
        </a>
    {% endif %}
</div>

至此逆甜,整個(gè)分頁(yè)功能就完成了虱肄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市交煞,隨后出現(xiàn)的幾起案子咏窿,更是在濱河造成了極大的恐慌,老刑警劉巖素征,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件集嵌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡御毅,警方通過(guò)查閱死者的電腦和手機(jī)根欧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)端蛆,“玉大人凤粗,你說(shuō)我怎么就攤上這事〗穸梗” “怎么了嫌拣?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)呆躲。 經(jīng)常有香客問(wèn)我异逐,道長(zhǎng),這世上最難降的妖魔是什么插掂? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任灰瞻,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘箩祥。我一直安慰自己院崇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布袍祖。 她就那樣靜靜地躺著底瓣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蕉陋。 梳的紋絲不亂的頭發(fā)上捐凭,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音凳鬓,去河邊找鬼茁肠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛缩举,可吹牛的內(nèi)容都是我干的垦梆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼仅孩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼托猩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起辽慕,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤京腥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溅蛉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體公浪,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年船侧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了欠气。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勺爱,死狀恐怖晃琳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情琐鲁,我是刑警寧澤卫旱,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站围段,受9級(jí)特大地震影響顾翼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奈泪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一适贸、第九天 我趴在偏房一處隱蔽的房頂上張望灸芳。 院中可真熱鬧,春花似錦拜姿、人聲如沸烙样。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谒获。三九已至,卻和暖如春壁却,著一層夾襖步出監(jiān)牢的瞬間批狱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工展东, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赔硫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓盐肃,卻偏偏與公主長(zhǎng)得像爪膊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恼蓬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容