Python學習之路17-Django入門

《Python編程:從入門到實踐》筆記。html

從本篇開始將是該書的最後一個項目,將用3篇文章來介紹Django的基礎。完成一個「學習筆記」的小網站。python

1. 前言

在本篇中,咱們將:linux

  • 用Django來開發一個名爲「學習筆記」(Learning Log)的項目;
  • 爲這個項目制定規範,而後爲應用程序使用的數據定義模型;
  • 使用Django的管理系統來輸入一些初試數據,再學習編寫視圖和模板,讓Django可以爲咱們的網站建立網頁。

不過在開始以前,請先新建一個虛擬環境並安裝Django。若是沒有虛擬環境,經過pip安裝的全部庫都會保存到python的site-packages目錄下。開發多個項目時,都會用同一個python,而某些項目並不須要其中的全部第三方庫,但若是將這些不須要庫的刪除,又會影響到其餘項目。並且,若是A項目須要Django2.0.4,B項目須要Django2.0.0,這該怎麼辦?此時就須要虛擬環境。它其實就至關於一個新的文件夾,將新項目與其餘項目隔離,本項目的庫不與其餘項目的庫相關聯,相似於操做系統的多用戶概念。程序員

若是使用的是Python 3,可使用命令:web

python -m venv ll_env
複製代碼

若是該命令不成功,多是沒有安裝virtualenv模塊:正則表達式

pip install virtualenv
複製代碼

而後建立並激活虛擬環境:sql

virtualenv ll_env
 # linux:
source ll_env/bin/activate
# windows:
ll_env\Scripts\activate
 # 停用:
deactivate
複製代碼

管理虛擬環境的庫還有不少,有興趣的話能夠到網上搜一搜。shell

若是你使用的是新版的PyCharm,那麼它在新建項目的時候默認就會建立新的虛擬環境。數據庫

激活虛擬環境後就能夠安裝Django了:django

pip install django
複製代碼

2. 創建項目

2.1 在Django中建立項目

2.1.1 生成項目

在虛擬環境中執行以下命令:

# 主要最後有個實心句號!
# 這個句點讓新項目使用合適的目錄結構,這樣開發完成後能夠輕鬆地將應用程序部署到服務器
django-admin startproject learning_log .
複製代碼

執行上述命令後,將多出一個manage.py文件和一個learning_log文件夾,固然還有自己的一個ll_env文件夾。

而在learning_log文件夾中,又有四個文件:__init__.pysettings.pyurls.pywsgi.py

  • manage.py是一個簡單的程序,它接收命令並將其交給Django的相關部分去運行;
  • settings.py指定Django如何與你的系統交互以及如何管理項目,其實就是配置文件;
  • urls.py告訴Django應建立哪些網頁來響應瀏覽器請求;
  • wsgi.py是web server gateway interface(Web服務器網關接口)的縮寫,幫助Django提供它建立的文件。

至於__init__.py,它是個空文件,Python的每一個模塊下必需要有這個文件。

2.1.2 建立數據庫

Django將大部分與項目相關的信息都存儲在數據庫中,全部還須要建立一個供Django使用的數據庫。依然是在虛擬環境下執行以下命令:

python manage.py migrate
複製代碼

在PyCharm中的話,能夠經過點擊工具欄Tools中的Run manage.py Task(Ctrl+Alt+R),在彈出的命令行中直接輸入原命令中manage.py後面的部分,後面的命令也能夠這樣執行([appname]是自動提示)。

"migrate"這個單詞實際上是遷移的意思,並非「建立(create)」。之因此使用這個詞,是由於通常將修改數據庫的過程稱爲遷移數據庫(筆者數據庫學得渣,這段解釋直接從書裏照搬的,但願哪位大神在留言區解釋一波)。若是是剛建立的項目,而且第一次執行,將會獲得以下輸出:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  -- snip --
  Applying sessions.0001_initial... OK
複製代碼

從第2行結果能夠看出,Django將建立和修改數據庫看作是對數據庫的遷移,Apply all migrations確保數據庫結構與當前代碼匹配(好比你修改了類的結構,添加了屬性,這就至關於修改了數據表)。

執行命令後,項目的根目錄下會多出一個名爲db.sqlite3的數據庫文件。SQLite是一種使用單個文件的輕量級數據庫,經常使用於開發簡單應用程序,它讓你不用太關注數據庫管理的問題。

2.1.3 運行項目

依然在項目的虛擬環境下輸入以下命令:

python manage.py runserver
複製代碼

獲得以下輸出:

Performing system checks...

System check identified no issues (0 silenced).
April 21, 2018 - 20:46:48
Django version 2.0.4, using settings 'learning_log.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
複製代碼

如今在瀏覽器中地址欄輸入localhost:8000 (或者127.0.0.1:8000),將獲得以下頁面:

這是最新版的Django的默認啓動界面。

2.2 建立應用程序(APP)

2.2.1 建立模型

Django項目由一系列應用程序組成,它們協同工做,讓項目成爲一個總體。咱們在項目根目錄下執行以下命令,建立一個名爲learning_logs的應用程序:

python manage.py startapp learning_logs
複製代碼

執行命令後,根目錄下會多出一個名爲learning_logs的文件夾(筆者第一次接觸Django的時候發現這玩意兒竟然叫作APP,和平時用的手機上的各類APP相差也太大了,很不適應),它的結構以下:

重要的是其中的models.pyadmin.pyviews.py文件,咱們將使用models.py來定義咱們要在應用程序中管理的數據。另外兩個文件稍後再介紹。

打開models.py文件,發現其中自帶兩行代碼:

from django.db import models

# Create your models here.
複製代碼

在代碼層面,模型就是一個類,和以前的文章中的類同樣,包含屬性和方法。下面建立一個「主題」類:

from django.db import models

class Topic(models.Model):
    """用戶學習的主題"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        """返回模型的字符串表示"""
        return self.text
複製代碼

Model類是Django中的一個定義了模型基本功能的類。Topic類只有兩個屬性textdate_added。模型中如CharFieldDateTimeField這些字段還有不少,Django自動根據數據庫的不一樣調用不一樣的SQL語句建立數據表,即屏蔽底層數據庫的差別。

同時還重寫了__str__()方法,之因此說「重寫」是由於每一個類都有這個方法,當直接將一個類A放入print()之類的語句中時,就會調用A__str__()方法。若是沒有重寫這個方法,通常會輸出這個對象的內存地址之類的,你們能夠蘇隨便寫個類試一試。

2.2.2 激活模型

使用模型前,必須將APP包含到項目中,打開settings.py文件,將APP添加到INSTALLED_APPS中:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 添加你的app,上面的都是自帶的
    "learning_logs.apps.LearningLogsConfig",
]
複製代碼

這裏有個須要注意的地方,因爲這本書的2016年出版的,當時Django尚未到達2.0版本,因此在書中,註冊APP是這樣寫的:

INSTALLED_APPS = [
    -- snip --
    # 添加你的app,上面的都是自帶的
    "learning_logs",
]
複製代碼

如今官方文檔中是按照第一種方式註冊APP,而且,最新版的Django在新建APP後,在APP的目錄下還多了一個apps.py文件,該文件默認有一個根據APP名稱建立的類,此處爲LearningLogsConfig,內容以下:

from django.apps import AppConfig

class LearningLogsConfig(AppConfig):
    name = 'learning_logs'
複製代碼

回到主線,在終端中輸入:

python manage.py makemigrations learning_logs
 # 輸出
Migrations for 'learning_logs':
  learning_logs\migrations\0001_initial.py
    - Create model Topic
複製代碼

makemigrationsDjango肯定該如何修改數據庫,使其可以存儲與咱們定義的新模型相關聯的數據。從輸出能夠看出,在APP目錄下的migrations文件夾中建立了一個名爲0001_initial.py的遷移文件,該文件將在數據庫中爲模型Topic建立一個表。

最後,在命令行中輸入:

python manage.py migrate
 # 輸出:
Running migrations:
  Applying learning_logs.0001_initial... OK
複製代碼

總結:每當須要修改模型時,都採起以下三個步驟:修改models.py,對你的APP調用makemigrations,讓Django遷移項目migrate

2.2.3 Django管理網站

Django自帶管理後臺。首先爲網站建立一個超級用戶。在中斷輸入:

python manage.py createsuperuser
複製代碼

隨後按提示輸入用戶名和密碼便可,郵箱地址能夠留空(直接回車)。

Django自動在管理網站中添加一些模型,如UserGroup,但對於咱們建立的模型,必須手工註冊。

注意前面提到的和models.py在同一目錄的admin.py文件,這就是註冊自行編寫的模型的地方,在該文件中加入後兩行代碼:

# 第一行代碼是自帶的
from django.contrib import admin
from learning_logs.models import Topic

admin.site.register(Topic)
複製代碼

如今登陸Django自帶的網站管理頁面 http://localhost:8000/admin/ 登陸剛纔建立的超級用戶和密碼後將出現以下界面:

在這裏你能夠管理用戶和組,以及和模型Topic相關的數據。

如今先手動添加兩個主題:點擊Add建立ChessRock Climbing

2.2.4 定義模型Entry

爲使能在每一個主題下添加條目,須要定義Entry模型,EntryTopic的關係是多對一。一樣是在models.py中添加模型:

from django.db import models

class Topic(models.Model):
    -- snip --
    
class Entry(models.Model):
    """學到的有關某個主題的具體知識"""
    # 因爲和「主題」是多對一的關係,因此「主題」是「條目」的外鍵
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
    text = models.TextField()
    date_added = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "entries"

    def __str__(self):
        """返回模型的字符串表示"""
        # 因爲條目包含的文本可能很長,只顯示前50個字符
        return self.text[:50] + "..."
複製代碼

注意其中嵌套了一個Meta類,它用於管理模型的額外信息。它讓咱們可以設置一個特殊屬性,讓Django在須要時使用Entries來表示多個條目。若是沒有這個類,Django將使用Entrys來表示多個條目(保證英語語法正確......不得不說,本書做者仍是很細心的)。

添加了新模型,因此須要再次遷移數據庫,過程就是前面講的三個步驟中的後兩步。而後在admin.py中註冊Entry

爲了讓這個網站有一些初試數據,添加三個條目:兩個Chess的,一個Rock Climbing的。在管理頁面中點擊EntriesAdd按鈕,你將看到一個下拉列表,用於選擇Topic,還有個文本框,用於輸入內容。隨便輸入一點內容就能夠,具體內容再也不詳細列出。

2.2.5 Django shell

輸入一些數據後,可經過交互式終端會話以編程方式查看這些數據。這種交互式環境稱爲Django shell,經常使用語測試項目和排除故障。如下是在shell中的一些操做:

(ll_env)learning_log$ python manage.py shell # 啓動shell
>>> from learning_logs.models import Topic
>>> Topic.objects.all() # 得到模型Topic的全部實例
<QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]>  # 返回了一個查詢集QuerySet
 >>> topics = Topic.objects.all() # 查詢每一個Topic對象
>>> for topic in topics:
...     print(topic.id, topic)
...
1 Chess
2 Rock Climbing
 >>> t = Topic.objects.get(id=1) # 根據id查看Chess模型的具體內容
>>> t.text
'Chess'
>>> t.date_added
datetime.datetime(2018, 4, 22, 2, 4, 3, 723045, tzinfo=<UTC>)
 >>> t.entry_set.all() # 查看該主題下的全部條目,經過外鍵查詢,進行了人爲換行
<QuerySet [<Entry: The opening is the first part of the game, roughly...>,
<Entry: In the opening phase of the game, it's important t...>]>
複製代碼

爲了經過外鍵獲取數據,注意查詢時的語法:模型小寫名稱+下劃線+set,如第19行代碼。編寫用戶可請求的網頁時將使用這樣的語法。若是代碼在shell中的行爲符合預期,那麼它們在項目文件中也能正確工做。

3. 建立網頁:學習筆記主頁

這一部分,書中內容和新版的Django出入比較多。

3.1 映射URL

Django建立網頁的過程一般分三個階段:定義URL、編寫視圖和編寫模板

URL模式描述了URL是如何設計的,讓Django知道如何將瀏覽器請求與網站URL匹配,以肯定返回哪一個網頁。每一個URL都被映射到特定的視圖——視圖函數獲取並處理網頁所需的數據。視圖函數一般調用一個模板,後者生成瀏覽器可以理解的網頁。

目前,基礎URL(http://localhost:8000/ )返回默認的Django頁面,如今修改這個映射,將其映射到咱們本身編寫的主頁。

打開learning_log文件夾中的urls.py,將看到以下內容:

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

# 書中的內容和實際的內容有些出入,如下是書中內容:
# 新版Django簡化了URL路由寫法
# from django.conf.urls import include, url
# from django.contrib import admin
# 
# from django.conf.urls import include, url
# from django.contrib import admin
#
# urlpatterns = [
# url(r'^admin/', include(admin.site.urls)),
# ]
複製代碼

變量urlpatterns包含項目中的APP的URL,admin.site.urls模塊定義了可在管理網站中請求的全部URL。如今添加代碼:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("", include("learning_logs.urls")),
    # 書中代碼:
    # url(r"", include("learning_logs.urls", namespace="learning_logs")),
]
複製代碼

注意:書中在此處的include()函數中傳入了關鍵字參數namespace="learning_logs",但在新版中,命名空間(namespace) 是在APP的urls.py中設置的:在urlpatterns變量前新建一個值爲"learning_logs"app_name變量。

namespacelearning_logs的URL同項目中的其餘URL區分開,對項目進行擴展時,這樣作十分有用。

還須要在learning_logs中建立另外一個urls.py文件:

"""定義learning_logs的URL模式"""
from django.urls import path
# 從當前的urls.py模塊所在的文件夾中導入視圖
from . import views

# 該變量包含該APP中可請求的網頁
urlpatterns = [
    # 主頁
    path("", views.index, name="index"),
]
複製代碼

path()的第一個參數是正則表達式,第二個參數是要調用的視圖函數(當請求的URL和第一個參數匹配時調用),第三個參數爲這個URL模式指定一個名字,至關於將這個模式保存在變量index中,之後每當須要提供這個主頁的鏈接時都使用這個名字,而不用再編寫URL。

3.2 編寫視圖

視圖函數接收請求中的信息,準備好生成網頁所需的數據,再將這些數據發送給瀏覽器,在發送以前,還套用了網頁的模板(若是有模板的話)。

當咱們在建立APP時,它的文件夾中有一個views.py文件,該文件默認只有一個導入語句,導入了函數render(),如今編寫這個文件:

from django.shortcuts import render

def index(request):
    """學習筆記主頁"""
    # 將請求的數據套用到模板中,而後返回給瀏覽器
    # 第一個參數是原始請求對象,第二是可用於建立網頁的模板
    return render(request, "learning_logs/index.html")
複製代碼

3.3 編寫模板

模板定義了網頁的結構,指定了網頁時什麼樣的。每當網頁被請求時,Django將填入相關的數據。模板中能訪問視圖提供的任何數據。

如今建立上面代碼中的index.html模板:在learning_logs文件夾中新建一個templates文件夾,再在這個文件夾中新建一個和APP同名的文件夾,即learning_logs文件夾,最後,在這個learning_logs文件夾中新建index.html文件。看起來好像有點多餘,但這是Django可以解析的目錄結構。index.html文件的內容以下:

<p>Learning Log</p>

<p>Learning Log helps you keep track of your learning, 
    for any topic your're learning about.</p>
複製代碼

如今當你訪問http://localhost:8000 時,將看到以下主頁:

建立網頁的過程看起來可能很複雜,實際上這就是一個簡單的MVCModelViewController)模型,但在Django中被稱爲MVTModelViewTemplate

這種方式使代碼結構清晰,修改方便,也讓咱們能各司其職,好比,數據庫專家就專一於Model,程序員專一於ViewWeb設計人員專一於Template

4. 建立其餘網頁

咱們繼續擴展咱們的項目。建立兩個用於顯示數據的網頁,其中給一個列出全部的主題,另外一個顯示特定主題的全部條目。對於每一個網頁都指定URL模式,編寫一個視圖函數,並編寫一個模板。但這麼作以前,咱們先建立一個父模板,項目中的其餘模板都將繼承它。

4.1 模板繼承

4.1.1 父模板base.html

<p>
  <a href="{% url 'learning_logs:index' %}">Learning Log</a>
</p>

{% block content %}{% endblock content %}
複製代碼

該文件存儲在index.html所在的目錄中。該文件包含全部頁面都有的元素,其餘模板繼承自它。目前爲止,全部頁面共有的元素還只有頂端的標題。

咱們將這個標題設置爲到主頁的連接。爲了建立這個連接,使用了模板標籤(花括號加百分號的組合),其中learning_logs是項目的命名空間,index就是這個項目主頁的URL模式名(注意翻看前面小節的代碼)。

還建立了一個塊標籤(block),這個塊名爲content,是一個佔位符,其中包含的信息將由子模板指定。

4.1.2 子模板

重新編寫index.html,使其繼承base.html

{% extends "learning_logs/base.html" %}

{% block content %}
    <p>Learning Log helps you keep track of your learning, 
        for any topic you're learning about.</p>
{% endblock content %}
複製代碼

第1行代碼導入父模板中的全部內容;extends後面跟父模板所在的路徑,雖然這倆文件都在同一目錄下,但不能省略掉前面的learning_logs/

3到6行代碼插入了一個名爲content的塊標籤,不是從父模板繼承的內容都包含在content塊中。第6行代碼指出內容定義的結束位置。

注意:在大型項目中,一般有一個用於整個網站的父模板base.html,且網站的每一個主要部分都有一個父模板。每一個主要部分的父模板又都繼承自base.html,而網站的每一個網頁都繼承相應部分的父模板。

4.2 主題頁面

該頁面顯示用戶建立的全部主題。他是第一個須要使用數據的頁面。

4.2.1 定義URL模式

在APP的urls.py中添加能轉到topics.html的URL模式

urlpatterns = [
    # 主頁
    path("", views.index, name="index"),
    path("topics/", views.topics, name="topics")
]
複製代碼

4.2.2 添加視圖

views.py中添加相應的函數:

from django.shortcuts import render
from .models import Topic

def index(request):
    -- snip --

def topics(request):
    """顯示全部的主題"""
    topics = Topic.objects.order_by("date_added")
    # 一個上下文字典,傳遞給模板
    context = {"topics": topics}
    return render(request, "learning_logs/topics.html", context)
複製代碼

4.2.3 建立模板

建立一個模板,用於顯示全部的主題:

{% extends "learning_logs/base.html" %}

{% block content %}
    <p>Topics</p>

    <ul>
        {% for topic in topics %}
            <li>{{ topic }}</li>
        {% empty %}
            <li>No topics have been added yet.</li>
        {% endfor %}
    </ul>
{% endblock content %}
複製代碼

在這個模板中,咱們使用了一個至關於for循環的模板標籤,它遍歷字典context中的列表topics。而且注意,該模板中沒有出現context字樣,至關於模板自動從context取得topics的內容。

模板中使用的代碼與Python代碼存在一些重要差異:Python使用縮進來指出哪些代碼行是for循環的組成部分,而在模板中,每一個for循環都必須明確的指出結束爲止。

要在模板中打印變量,須要將變量名用雙花括號括起來,每次循環時,該代碼都會被替換爲topic的當前值。

還使用了空模板標籤(empty),它告訴Django在列表topics爲空時該怎麼辦。

這些花括號都不會出如今網頁中,它們只是用於告訴Django咱們使用了一個模板變量。

修改父模板base.html,使其包含能轉到主題頁面的鏈接(第3行爲添加的代碼,第2行最後添加了一個連字符):

<p>
    <a href="{% url 'learning_logs:index' %}">Learning Log</a> -
    <a href="{% url 'learning_logs:topics' %}">Topics</a>
</p>
複製代碼

如今再次輸入http://localhost:8000 將看到咱們添加的Topics連接,點擊它,將跳轉到以下頁面:

4.3 特定主題頁面

該頁面用於顯示該主題下的全部條目。和上面的步驟同樣,定義URL模式,編寫views.py中的處理函數,編寫網頁模板。

4.3.1 URL模式

urlpatterns = [
    -- snip --
    path("topics/<int:topic_id>/", views.topic, name="topic"),
]
複製代碼

4.3.2 添加視圖

def topic(request, topic_id):
    """顯示單個主題及其全部的條目"""
    # 經過Topic的id得到全部條目
    topic = Topic.objects.get(id=topic_id)
    # 前面的減號表示降序排序
    entries = topic.entry_set.order_by("-date_added")
    context = {"topic": topic, "entries": entries}
    return render(request, "learning_logs/topic.html", context)
複製代碼

第4,6行的代碼叫作查詢,若是你和筆者同樣是個初學者,比起代碼都寫完了,最後發現查詢語句有問題,再來修改,那麼先在Django shell中運行下代碼看看結果,這樣更高效。

4.3.3 模板topic.html

{% extends "learning_logs/base.html" %}

{% block content %}

  <p>Topic: {{ topic }}</p>

  <p>Entries:</p>
  <ul>
    {% for entry in entries %}
      <li>
        <p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
        <p>{{ entry.text|linebreaks }}</p>
      </li>
    {% empty %}
      <li>
        There are no entries for this topic yet.
      </li>
    {% endfor %}
  </ul>

{% endblock content %}
複製代碼

在Django模板中,豎線(|)表示模板過濾器(對模板變量的值進行修改的函數)。過濾器date: 'M d, Y H:i'以這樣的格式顯示時間戳:April 22, 2018 16:09。接下來的一行顯示txt的完整值,而不只僅是entry的前50個字符。過濾器linebreaks將包含換行符的長條目轉換成瀏覽器可以理解的格式,以避免顯示一個不換行的文本塊。

4.3.4 修改topics.html模板

-- snip --
{% for topic in topics %}
  <li>
    <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
  </li>
{% empty %}
-- snip --
複製代碼

4.4 效果

如今咱們回到http://localhost:8000/topics/ 頁面,隨便點擊一個主題,好比第一個,將獲得如下界面:

5. 小結

上述內容主要有:

  • 在虛擬環境中安裝Django,建立項目,並覈實該項目已正確地建立;
  • 在項目中建立APP;
  • 建立表示APP數據的模型;
  • 建立數據庫,以及在修改模型後,Django能夠爲遷移數據庫提供什麼樣的幫助;
  • 建立超級用戶,並使用管理網絡輸入初試數據;
  • Django shell;
  • 定義URL、建立視圖函數、網頁模板(包括繼承)。

下一篇中,咱們將:

  • 建立對用戶友好而直觀的網頁,讓用戶無需經過管理網站就能添加新的主題和條目(我的感受不必這麼作,Django自帶的管理系統挺好用的),以及編輯既有的條目;
  • 添加一個用戶註冊系統,讓用戶可以建立帳戶。

迎你們關注個人微信公衆號"代碼港" & 我的網站 www.vpointer.net ~

相關文章
相關標籤/搜索