Django官方文檔

編寫第一個Django應用(第一部分)

經過一個調查問卷的例子來學習Django,它主要包括兩部分:html

  • 一個公共站點,容許用戶查看問卷並投票
  • 一個管理員站點,容許添加、修改和刪除問卷

假設你已經安裝了Django,可使用python -m django --version來檢測。前端

建立一個項目

經過命令行來建立一個項目:django-admin startproject mysitepython

該命令會在當前目錄建立一個mysite目錄,切記不要用django這個關鍵字來給項目命名。mysql

建立獲得的目錄結構以下:web

mysite/
	manage.py
	mysite/
		__init__.py
		settings.py
		urls.py
		wsgi.py

接下來一一解釋這些文件的做用:sql

  • 外部的mysite/根目錄是整個項目的容器,它的名字並不重要,你能夠自行重命名;
  • manage.py:一個命令行的組件,提供多種方式來和Django項目進行交互
  • 內部的mysite/目錄是項目實際的Python包,它的名字就是未來import時要用的,好比mysite.urls
  • mysite/settings.py:當前項目的配置文件
  • mysite/urls.py:當前項目的url聲明語句
  • mysite/wsgi.py:WSGI兼容的web服務器的entry-point

開發服務器

運行開發服務器python manage.py runserver, 默認條件下即可以在瀏覽器中訪問http://127.0.0.1:8000/, 固然也能夠更改端口python manage.py runserver 8080, 這樣的話在瀏覽器中也須要對應更改端口號。shell

若是想更改服務器的ip,也能夠python manage.py runserver 0:8000數據庫

服務器的自動從新加載機制django

大多數狀況下,當你更改了項目代碼後,開發服務器會自動從新加載Python代碼,這樣就不用重啓服務器了。固然,一些添加文件的操做時不會觸發從新加載。瀏覽器

建立polls應用

manage.py所處的目錄中建立應用python manage.py startapp polls

該命令會建立以下目錄結構:

polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py

編寫第一個view

打開polls/views.py文件,而後編寫以下代碼:

from django.http import HttpResponse

def index(request):
	return HttpResponse("hello, world. You're at the polls index.")

這是Django中最簡單的view,爲了調用這個view,咱們須要將其映射到一個URL上,所以咱們須要一個URLconf

爲了建立一個URLConf,建立urls.py文件,此時應用的目錄結構以下:

polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
urls.py
views.py

polls/urls.py文件中編寫以下代碼:

from django.urls import path
from . import views

urlpatterns = {
path('', views.index, name='index'),
}

下一步,在項目的urls.py文件中添加一個include():

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

urlpatterns = {
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
}

這裏的include()函數容許引用其餘的URLConf,不管什麼時候Django遇到include()函數,它將從url字符串中將當前urlconf匹配的部分砍掉,而後將剩餘url字符串傳遞到include所指定的urlconf做進一步處理

path()有四個參數:

route

route是一個包含了url模式的字符串,當處理一個request的時候,Django從urlpattern中的第一個模式開始,順序向下匹配url模式,直到遇到第一個匹配的項。匹配的內容只包含域名後面的部分,不包含get或者post參數,好比http://www.example.com/myapp/將只拿/myapp/urlpattern中的模式進行匹配,http://www.example.com/myapp/?page=3一樣也只拿/myapp/進行匹配

view

當遇到一個匹配的url模式,Django就會調用指定的view函數,並將HttpResponse做爲第一個參數傳遞過去,同時url中捕獲的其他關鍵參數也會傳遞過去

kwargs

固然除了規定好的HttpResponse參數和url中捕獲的關鍵字參數,還有一些關鍵字參數也能夠傳遞給指定的view,可是本教程不會使用這個功能

name

給URL命名能夠避免歧義

寫第一個Django應用(第二部分)

2.4.1 數據庫啓動 打開mysite/settings.py文件,這是個常規的Python模塊,其中包含的模塊級變量表明着Django的設置

Django默認使用SQLite數據庫,這個數據庫已經包含在了Python之中,因此不須要安裝其餘的任何東西

若是想使用其他的數據庫,安裝合適的數據庫插件(database bindings)並更改配置文件中的DATABASE屬性, ENGINE:取值的範圍'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql'或者'django.db.backends.oracle'等等 NAME:數據庫的名字

編輯mysite/settings.py文件,設置TIME_ZONE爲本身的時區

另外,注意INSTALLED_APPS設置包含的應用

默認狀況下INSTALLED_APPS包含如下應用: django.contrib.admin 管理員站點 django.contrib.auth 認證系統,用於登陸註冊的 django.contrib.contenttypes 一個content types的框架 django.contrib.sessions 一個會話框架 django.contrib.message 一個消息框架 django.contrib.staticfiles 一個處理靜態文件的框架 爲了知足常見的需求,這些應用默認狀況都加入到了Django項目中,每一個應用都會用到若干個數據庫表,經過如下命令來建立這些表格: python manage.py migrate 這個migrate命令查看INSTALLED_APPS設置,而後根據mysite/settings.py文件中的數據庫設置來建立任何須要的數據庫。

2.4.2 建立models 建立models就是在定義數據庫的佈局,包括額外的元數據

在一個簡單的poll應用中,建立兩個models:Question和Choice,一個Question包含一個問題和發佈日期,一個Choice有兩個字段:選項的文本和一個投票計數器,每一個選項和一個問題相關聯。

這些概念都經過Python的類來實現。編輯polls/models.py文件: from django.db import models

class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published')

class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) 這些代碼都很簡單,每一個model都是django.db.models.Model的子類,而且都有一些類變量,每一個都表明着數據庫字段,每一個字段都是Field類的實例,分別表示字段的不一樣類型

每一個Field實例的名字都是字段的名字,不過是給機器讀的,在建立實例時能夠經過參數指定給人閱讀的名字,好比上述中的date published就是以這種方式命名的

一些Field類須要一些參數,好比CharField必需要指定max_length,又好比有些Field能夠指定默認值default=0

除此以外還能夠經過ForeignKey來指定外鍵。Django支持經常使用的數據庫關係:一對一,一對多,多對一

2.4.3 激活models 這些少許代碼能夠給Django帶來不少信息,有了這些信息,Django能夠爲應用建立一個數據庫模式,也能夠建立能夠訪問Question和Choice類的API

接下來要把polls這個應用的引用添加到INSTALLED_APPS中去,PollsConfig類存在於polls/apps.py文件中,因此它的點路徑爲'polls.apps.PollsConfig',編輯mysite/settings.py文件,把這個點路徑添加到INSTALLED_APPS設置中去 INSTALLED_APPS={ 'polls.apps.PollsConfig', ... } 如今Django已經包含了polls應用,輸入以下命令: python manage.py makemigrations polls 該命令告訴Django Model代碼已經更改,所作的更改將被存儲爲migrations

migrations以文件的形式被存儲在硬盤上, Django把對models的更改都存儲爲遷移。能夠去polls/migrations/0001_initial.py查看

接下來使用以下命令來運行migrations,並自動部署數據庫模式,這就被稱爲migrate python manage.py migrate

將以上全部的操做總結爲三個步驟: 更改models 運行python manage.py makemigrations來爲這些改變建立migrations 運行python manage.py migrate來將這些更改應用到數據庫

爲何要將兩個命令分開?主要是爲了作好版本的控制,這些之後再詳細講解

2.4.4 使用API 本小節進入Python交互式shell來使用Django提供的API 輸入以下命令來啓動Python shell: python manage.py shell 進入shell後便可開始使用Django爲數據庫提供的API: from polls.models import Choice, Question Question.objects.all()

from django.utils import timezone q = Question(question_text="What's new?", pub_date=timezone.now()) q.save() q.id q.question_text q.pub_date Question.objects.all() 固然,類對象的顯式輸出不適合人閱讀,能夠重寫Question類進行更改,代碼以下: from django.db import models

class Question(models.Model): def str(self): return self.question_text ...

class Choice(models.Model): def str(self): return self.choice_text ... 這樣的話,在Python shell中打印出Question的類對象就能夠顯示出問題文本了。

下面將嘗試給Question類添加一個自定義的方法: import datetime

from django.db import models from django.db import timezone

class Question(models.Model): def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1) 保存這些更改,而後從新進入Python shell: from polls.models import Choice, Question Question.objects.all() Question.ojbects.filter(id=1) Question.objects.filter(question_text__startswith='What')

from django.utils import timezone

current_year = timezone.now().year Question.objects.get(pub_date__year=current_year)

Question.objects.get(id=2)

Question.objects.get(pk=1)

q = Question.objects.get(pk=1) q.was_published_recently()

q = Question.objects.get(pk=1) q.choice_set.all() q.choice_set.create(choice_text='Not much', votes=0) q.choice_set.create(choice_text='The sky', votes=0) c = q.choice_set.create(choice_text='Just hacking again', votes=0) c.question q.choice_set.all() q.choice_set.count() c = q.choice_set.filter(choice_text__startswith='Just hacking') c.delete()

2.4.5 介紹Django的Admin 這是一個管理員應用,Django默認支持的 建立一個管理員用戶 python manage.py createsuperuser 輸入用戶名、郵箱、密碼等信息 而後啓動開發服務器,並進入管理員頁面http://127.0.0.1:8000/admin/,而後登錄進入管理員站點 能夠看到管理員站點能夠對用戶組和用戶進行可視化編輯 那麼如何對Question和Choice也進行可視化編輯呢,須要將poll應用添加到管理員站點中去 打開polls/admin.py,輸入以下代碼: from django.contrib import admin from .models import Question

admin.site.register(Question) 而後開始探索管理員的功能,發現能夠經過可視化操做Question,好比添加問題。因爲在model中已經定義了各個字段的類型,因此管理員頁面中的各個字段的輸入組件會自動生成。好比字符串字段對應輸入框,時間字段對應日期選擇控件。

2.5 編寫第一個Django應用(第三部分) 編寫更多的view 接下來給polls/views.py文件編寫更多的函數 def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id): response = "You're looking at results of question %s." return HttpResponse(response % question_id)

def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id) 而後將這些新的view函數添加到對應的path()上去: from django.urls import path from . import views

urlpatterns = { path('', views.index, name='index'), path('int:question_id/', views.detail, name='detail'), path('int:question_id/results/', views.results, name='results'), path('int:question_id/vote/', views.vote, name='vote'), } 這裏的int:question_id捕獲名爲question_id的整型參數。 記得別加上.html後綴,這樣看起來很笨。

這樣就完成了對每一個問題詳情、結果、投票的查看。

2.5.3 編寫views來真正作些事情 每個view主要作兩件事情:返回一個HttpResponse對象,包含了請求頁面的內容,或者觸發諸如Http404的異常

view能夠從數據庫中讀取記錄

它可使用一個Django自帶的或者第三方的Python模板系統,它能夠生成一個PDF文件,輸出XML,建立一個ZIP壓縮文件等等,在此過程當中,可使用任何Python模塊。

Django須要的只有一個HttpResponse,或者是一個異常exception

因爲很方便,因此使用Django本身的數據庫。下面的例子展現最近的5個poll問題,用冒號分割 from django.http import HttpResponse from .models import Question

def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] output = ','.join([q.question_text for q in latest_question_list]) return HttpResponse(output) 這種在views中定義前端頁面的方式過於呆板。Django採用templates來實現後臺和前端頁面的分離。 首先在polls目錄下建立templates目錄,Django將在每一個應用的templates目錄下尋找前端頁面。 項目設置中的TEMPLATES設置描述了Django如何加載並渲染模板。默認的設置文件配置了DjangoTemplates屬性,其中APP_DIRS設置爲True,DjangoTemplates會在每一個INSTALLED_APPS下的templates中尋找模板文件。

在剛剛建立的templates目錄中再建立一個目錄polls,並在其中建立一個index.html文件,換句話說,模板文件應該在polls/templates/polls/index.html 因爲上面已經介紹了app_direcotries模板加載原理,在Django中引用模板能夠直接使用polls/index.html

將下面的代碼放置在模板中: {% if latest_question_list %}

<ul> {% for question in latest_question_list %} <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available. </p> {% endif %} 接下來也把index的view函數更新下: from django.http import HttpResponse from django.template import loader from .models import Question

def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] template = loader.get_template('polls/index.html') context = { 'latest_question_list': latest_question_list, } return HttpResponse(template.render(context, request)) 代碼加載polls/index.html前端頁面,並將context傳遞到前臺頁面。context是一個字典,它將模板變量映射爲Python對象

快捷方式:render() 常見的三部曲:加載模板、填充context和返回HttpResponse對象。Django提供了render()這種快捷方式: from django.shortcuts import render from .models import Question

def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context) 根據上面的代碼,render接受request對象爲第一個參數,接受一個模板名字做爲第二個參數,而後接受一個字典做爲第三個參數。

2.5.4 觸發異常 前面說到,view要麼返回一個HttpResponse對象要麼返回異常,這裏介紹如何返回異常 以訪問問題詳情爲例: from django.http import Http404 from django.shortcuts import render from .models import Question

def detail(request, question_id): try: question = Question.objects.get(pk=question_id) except Question.DoesNotExist: raise Http404("Question does not exist") return render(request, 'polls/detail.html', {'question': question}) 很簡單,若是取不到對應id的問題,就觸發404異常 那麼如何在前臺頁面顯示這個question呢,只須要在一個控件中注入question便可: {{ question }}

快捷方式: get_object_or_404() 因爲上面的操做很常見,因此Django也提供了一種快捷方式來減小平時的代碼量: from django.shortcuts import get_object_or_404, render from .models import Question

def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question}) 經過代碼能夠看到,get_object_or_404在沒有獲取到數據對象時,會獲得一個異常對象,這個異常對象顯示到前臺頁面會自動顯示404

除此以外:還有get_list_or_404(),區別就是get()和filter()的區別了

2.5.5 使用模板系統 回到poll應用的detail()函數,對其進行顯示:

<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul> 模板系統使用點號.來訪問變量屬性 for choice in question.choice_set.all會自動解釋爲Python代碼for choice in queston.choice_set.all()

2.5.6 移除模板中硬編碼URL 好比:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> 在上述代碼中polls/屬於硬編碼,將其改成: <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li> 這裏的detail是一種url的引用,能夠在urls.py中找到該名稱對應的url地址: ... path('<int:question_id>/', views.detail, name='detail') ... 而'detail'後面的question.id變量則是會替代該url地址中的<int:question_id>,從而造成一個完整的url

移除硬編碼的URL有什麼好處? 若是前端頁面多處須要使用某個具體地url地址,則均可以經過名字來引用,當該url地址須要更改時,只須要更改urls.py中對應的url地址便可,避免在前端頁面每一個地方都去修改

2.5.7 URL名字的命名空間 若是不一樣的應用中存在同名的url模式,那麼前端頁面在引用的時候光寫url模式的名字會引發歧義,因此在urls.py中須要指定app的名字 from django.urls import path from . import views

app_name = 'polls' urlpatterns = { ... } 這樣,在對前端頁面引用處作相應修改

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

2.6 編寫第一個Django應用(第四部分) 寫一個簡單的form表單

<h1>{{ question.questioin_text }}</h1>

{% if error_message %}

<p><strong>{{ error_message }}</strong></p> {% endif%}

<form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} {% for choice in question.choice_set.all %} <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}"> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label> <br> {% endfor %} <input type="submit" value="Vote"> </form> 上面的代碼中: 爲問題的每一個選項都設計了一個單選框,每一個選項的id和問題的選項序號有關,forloop.counter是進入for循環體的次數;每一個選項的name都爲choice 表單的跳轉連接爲投票地址,並以post方式提交,這樣很是重要 csrf(Cross Site Request Forgeries),是一個數據保護,加上標籤便可 接下來編寫vote的響應函數 from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_objects_or_404, render from django.urls import reverse from .models import Choice, Question

def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): return render(request, 'polls/detail.html', {'question': question, 'error_message': "You didn't select a choice"}) else: selected_choice.votes += 1 selected_choice.save() return HttpResponseRedirect(reverse('polls:results', args=(question.id, ))) 第一,後臺獲取前臺post提交的數據,使用POST[name]的形式,其中name指的是前端控件的name 第二,save()是對數據作即時保存 第三,一旦成功處理post請求數據,必定要使用HttpResponseRedirect()函數從新導向頁面,它能夠在用戶點擊後退按鈕時避免數據提交兩次。HttpResponseRedirect()只接受一個參數,就是要重定向頁面的連接 第四,reverse是Django中的一個函數,它的做用也是避免硬編碼

接下來,當用戶投票完成後,就會進入投票結果頁面,下面編寫reuslts的處理函數 from django.shortcuts import get_object_or_404, render

def results(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question}) 這裏的代碼和detail()中幾乎同樣了,惟一不一樣的就是模板名字不一樣,不過沒有關係,稍後會對其進行修復

如今建立polls/results.html模板文件:

<h1>{{ question.question_text }}</h1>

<ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote {{ choice.votes|pluralize }}</li> {% endfor %} </ul>

<a href="{% url 'polls:detail' question.id %}">Vote again?</a> 如今能夠去/polls/1/進行投票了

2.6.2 使用通用的view:代碼越少越好 不少的view都是在獲取數據並顯示在前端頁面上,爲了下降代碼的冗餘性,Django提供了通用view機制 三部曲:轉換urlconf、刪除無用的舊的views、基於Django通用views引入新的views

修改urlconf 打開polls/urls.py,修改以下: from django.urls import path from . import views

app_name = 'polls' uslpatterns = { path('', views.IndexView.as_view(), name='index'), path('int:pk/', views.DetailView.as_view(), name='detail'), path('int:pk/results/', views.ResultsView.as_view(), name='results'), path('int:question_id/vote/', views.vote, name='vote'), }

修改views 刪除以前的index, detail和results三個view,取而代之使用Django通用views,打開polls/views.py from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from .models import Choice, Question

class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_question_list'

def get_queryset(self): return Question.objects.order_by('-pub_date')[:5]

class DetailView(generic.DetailView): model = Question template_name = 'polls/detail.html'

class ResultView(generic.DetailView): model = Question template_name = 'polls/results.html'

def vote(request, question_id): ... 上述代碼使用了兩個通用view:ListView和DetailView,兩者分別爲展現一個對象列表,一個展現特定類型對象的詳情頁面 每一個通用view必須指定model屬性,而DetailView要求能從url中獲取到pk屬性,這就是爲何上一步在urlconf中將question_id改成pk DetailView默認使用模板<app_name>/<model_name>_detail.html這個模板,可是這裏另作了指定,避免Detail和Results進入了同一頁面 同理,ListView默認使用<app_name>/<model_name>_list.html這個模板,這裏也另行制定了

另外,還需強調的是,在以前的教程中,經過context向前臺傳輸數據,包括latest_question_list和question兩個變量,可是若是使用了DetailView,就不用指定context了,它會自動提供給前端頁面,由於已經制定了model爲Question;可是對於ListView,它默認傳遞到前端頁面的變量是question_list,爲了覆蓋,這裏特地制定了context_object_name

接下來運行服務器,看看如何基於通用view來使用最新的網站

2.7 編寫第一個Django應用(第五部分) 網站已經基本寫好了,如今寫一寫自動測試

2.8

相關文章
相關標籤/搜索