第一個Django appphp
by:授客 QQ:1033553122css
測試環境:html
Python版本:python-3.4.0.amd64python
下載地址:https://www.python.org/downloads/release/python-340/mysql
Win7 64位web
Django 1.11.4正則表達式
下載地址:https://www.djangoproject.com/download/sql
安裝djangoshell
python setup.py install數據庫
測試是否成功
>>> import django
>>> print(django.get_version())
1.10.6
>>>
或者
python -m django --version
1.10.6
參考鏈接:
https://docs.djangoproject.com/en/1.10/intro/install/
https://docs.djangoproject.com/en/1.10/intro/tutorial01/
第一個 Django app Part1
新建項目,選擇存放項目的目錄(例F:\project\Django\FirstApp),進入該目錄,執行django-admin命令
例:新建mysite項目
C:\Users\laiyu>cd /d F:\project\Django\FirstApp
F:\project\Django\FirstApp>django-admin startproject mysite
注意:項目名稱不能和python內置組件,或Django組件命名項目,特別是django(和Django自身衝突)或test(和python內置模塊衝突)。
運行命令後,生成文件以下:
FirstApp/
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
說明:
最外層mystie: 項目根目錄。
manage.py:提供各類方式同Django項目交互的命令行工具。
內層的mysite:python包,存放項目python文件目錄。
mystie/__init__.py:一個空文件,告訴python,該目錄應該被解析爲python包
mysite/settings.py:包含Django項目的配置/設置。
mysite/urls.py:包含Django項目的url聲明。
mysite/wsgi.py:服務項目的WSGI-compatible。
確認項目是否能正常運行,切換到根目錄,即例中的mysite,運行以下命令:
F:\project\Django\FirstApp\mysite>python manage.py runserver
運行結果:控制檯輸出以下信息
Performing system checks...
System check identified no issues (0 silenced).
You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
March 13, 2017 - 23:14:26
Django version 1.10.6, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
訪問http://127.0.0.1:8000/,ok
運行服務時,可指定端口,以下
python manage.py runserver 8080
也能夠用指定ip來訪問服務,放入以下
1.運行以下命令
python manage.py runserver 0.0.0.0:8000
2.編輯project_dir/settings.py文件,把服務所在ip添加到 ALLOWED_HOSTS列表中,以下
ALLOWED_HOSTS = ['192.168.1.103']
點擊runserver查看更多關於runserver的說明
建立投票app
項目(project) vs 應用(app)
應用:即一個web應用,好比web 博客系統,存放公共記錄的數據庫,或者一個簡單的投票系統。
項目:特定網站應用和配置的集合。一個項目包含多個應用,而一個應用只能在一個項目中。
接下來,在項目根目錄下,建立poll應用,這樣方便做爲頂級模塊導入。
例:在manage.py所在目錄,即項目根目錄下運行命令來建立polls app
F:\project\Django\FirstApp\mysite>python manage.py startapp polls
運行後生成polls文件:
mysite/
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
編寫第一個view
編輯polls/views.py文件,添加python代碼(帶背景色部分)以下
from django.shortcuts import render
# Create your views here.
from django.http import HttpResponse
def index(request):
return HttpResponse("hello, world. You're at the polls index")
爲了調用這個index view,須要把它映射到一個URL,所以須要一個URL 配置。
在polls目錄下,新建一個名爲urls.py的文件,以建立URLConf。如今app目錄看起來以下:
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
urls.py
views.py
編輯urls.py,添加以下代碼
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
配置項目URLConf
編輯mysite/urls.py文件,以下
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^polls/', include('polls.urls')),
url(r'^admin/', admin.site.urls),
]
瀏覽器訪問
說明:
1) 正則表達式:$, xxx$:匹配xxx結尾的字符串)。
2) 當Django遇到include()時,會先把請求中的url同include()函數對應的正則表達式匹配(例中按前後順序分別爲:'^polls/','^admin/',若是匹配到,則把URL中匹配到的字符串以後的剩餘URL扔給include引用的app URLconf進行後續處理。
例子:修改polls/urls.py內容以下
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'test', views.index, name='index'),
]
修改訪問鏈接:
http://127.0.0.1:8000/polls/testview
訪問結果同上。
3)當且僅當須要包含其它應用的URLConf式時使用include()。這裏admin.site.urls是個特例。
url函數
url函數接收4個參數:必選參數regex,view,可選參數 kwargs和name。
參數regex: 字符串類型的正則表達式。Django會從urlpatterns list中第一個正則表達式子開始匹配查找直到找到一個匹配的。
注意:正則表達匹配查找時,不搜索GET和POST參數以及域名。好比請求 https://www.example.com/myapp/, URLconf只查找myapp/,又如https://www.example.com/myapp/?page=3,URLconf只查找myapp/
注:正則表達式在第一次加載URLconf模塊時就進行了編譯,只要不是太複雜的正則表達式,查找速度都很快。
參數view:當Django找到匹配正則表達式的字符串時,會調用view函數,並把一個HttpRequest對象看成第一個函數參數,把經過正則表達式「捕獲」的其它值做爲其它參數。若是使用simple capture,那麼捕獲的值以位置參數傳遞,若是使用named capture則以關鍵詞參數傳遞。
參數kwargs:關鍵詞參數,以字典方式傳遞給目標view的關鍵詞參數。
參數name:命名URL,以便在Django其它地方引用時不產生歧義。
參考鏈接:
https://docs.djangoproject.com/en/1.10/intro/tutorial01/
第一個 Django app Part2
創建數據庫
打開mysite/settings.py。這是個普通的python模塊,擁有表明Django配置的模塊級變量。
默認的,配置使用SQLite。若是你對數據庫不熟悉,或者僅是想使用試用Djano,這是個最容易的選擇。SQLite包含在python中,因此不要安裝其它任何東西來提供數據庫支持。可是開始真正的項目時,可能須要使用其它更有伸縮性的數據庫好比PostgreSQL。
若是想使用其它數據庫,安裝數據庫並改變DATABASE 'default'項中的關鍵詞來匹配數據庫鏈接,以下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
說明:
ENGINE:可選值是'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', 或者'django.db.backends.oracle'。其它後端也能夠,查看詳情
NAME:數據庫名字。
若是使用SQLite,數據庫文件將存放在電腦上,這種狀況下,NAME應該爲絕對路徑,包含數據庫文件的文件名。默認值如上,把數據庫文件存放在項目根目錄下。
若是不使用SQLite,須要設置額外參數如 USER, PASSWORD,和HOST。更多詳情參考DATABASES.
另外,確保提供的USER具有「create database」權限。
編輯mysite/settings.py,設置TIME_ZONE爲你的時區。
注意INSTALLED_APPS設置,該設置包含了Django實例中激活的全部Django應用。應用可在多個項目中使用,能夠打包併發布給其它項目使用。
默認的,INSTALLED_APPS包含如下來自Django應用:
django.contrib.admin - 管理後臺
django.contrib.auth - 受權系統
django.contrib.contenttypes - content type框架
django.contrib.sessions - 會話框架
django.contrib.message - 消息框架
django.contrib.staticfiles - 管理靜態文件的框架
其中,一些應用使用了數據庫表,因此,咱們須要在使用它們以前建立數據庫表。爲了達到這個目的,執行如下命令:
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OK
F:\project\Django\FirstApp\mysite>
migrate查找INSTALLED_APPS設置,而後根據mysite/setting.py中的數據庫設置和應用程序的數據庫遷移
建立必要的數據庫。查看建立的表:數據庫客戶端輸入命令\dt(PostgreSQL),.shema(MySQL), SELECT TABLE_NAME FROM USER_TABLES(Oracle);
提醒:一些默認的應用咱們不須要,能夠在運行migrate以前刪除、註釋掉。
建立模塊
將在poll應用中建立兩個模塊:Question和Choice。每一個Question包含一個問題和發佈日期,每一個Choice有兩個域:選項文字和投票計數器。每一個Choice同一個Question關聯。
編輯polls/models.py文件
from django.db import models
# Create your models here.
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)
每一個模塊都由django.db.models.Model的子類表示,類變量表明model中數據庫Field。
每一個域由一個Field類(好比表明字符串的CharField,表明時間的DateTimeField)實例表示,告訴Django每一個field可容納什麼類型的數據。
Field實例(好比 question_text、pub_date)的名字,即爲域的名字,可在python代碼中使用,同時數據庫也將把它當表字段名使用。
給Field提供的第一個可選的位置參數可用來生成便於人易讀的名字。若是未提供,則使用機器易讀的名字做爲人類易讀的名字。例中,咱們僅爲Question.pub_date提供了人類易讀的名字date published,其它模塊Field實例則使用機器易讀的名字,好比choice_text。
一些Field須要必選參數,好比CharField,須要提供max_length。這不只是用於數據庫模式(schema),還用於合法性驗證(validation)。
Field還有各類可選參數,好比例中把votes的default值設置爲0。
最後,注意這裏使用ForeignKey來確立關係,這告訴Django每一個Choice和單個Question關聯。Django支持全部公共數據庫關係:多對一,多對多,一對一。
激活模塊
上述模塊代碼給Django提供了許多信息,擁有它,Django可:
1)爲該app建立數據庫模式(CREATE TABLE語句)
2)爲訪問Question和Choice對象建立Python 數據庫訪問api
可是,要先告訴項目已安裝polls應用。
爲了在項目中包含該app,須要在INSTALLED_APPS設置中添加引用。PollsConfig類位於polls/apps.py文件中,點分路徑爲jango.apps.PollsConfig,以下:
from django.apps import AppConfig
class PollsConfig(AppConfig):
name = 'polls'
編輯mysite/settings.py,添加點分路徑(帶背景色內容)
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
運行命令:
F:\project\Django\FirstApp\mysite>python manage.py makemigrations polls
Migrations for 'polls':
polls\migrations\0001_initial.py:
- Create model Choice
- Create model Question
- Add field question to choice
經過運行makemigrations,告訴django你對模塊作了些改動,而且但願記錄這些改動(但不當即執行這些改動),這些改動存在在磁盤文件,上例中文件爲polls/migrations/0001_initial.py。可易方式讀取這些改動,查看migration帶來的sql執行。
python manage.py sqlmigrate polls 0001
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "c
hoice_text" varchar(200) NOT NULL, "votes" integer NOT NULL);
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" RENAME TO "polls_choice__old";
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "c
hoice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integ
er NOT NULL REFERENCES "polls_question" ("id"));
INSERT INTO "polls_choice" ("question_id", "choice_text", "votes", "id") SELECT
NULL, "choice_text", "votes", "id" FROM "polls_choice__old";
DROP TABLE "polls_choice__old";
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
COMMIT;
注意:
1)Django會自動添加主鍵 id(可重寫)
2)約定的,Django會添加」_id」到外鍵域(可重寫)
可執行python manage.py check,在不執行遷移或改動數據庫的狀況下,來檢查項目中的問題
接着,執行migrate在數據庫中建立模塊表,即讓上述存儲的改動在應用中生效。
F:\project\Django\FirstApp\mysite>python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0001_initial... OK
閱讀django-admin documentation查看manage.py工具的更多功能。
API交互
調用python shell
python manage.py shell
Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AM
D64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
同直接運行python不同,由於manage.py設置DJANGO_SETTINGS_MODULE環境變量,爲mysite/settings.py文件提供python導入路徑。
注:也能夠不用manage.py,直接設置DJANGO_SETTINGS_MODULE環境變量,而後運行python並設置Django
set DJANGO_SETTINGS_MODULE=mysite.settings
python
Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AM
D64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.setup()
按這種方式,必須在manage.py所在目錄下開啓python。或者確保這個目錄在python的path中,這樣import mystie才起做用。
調用數據庫api
>>> from polls.models import Question, Choice
# 系統中尚未問題
>>> Question.objects.all()
<QuerySet []>
# 建立一個新的Question
>>> from django.utils import timezone
>>> q = Question(question_text="what's up?", pub_date=timezone.now())
# 保存對象到數據庫。
>>> q.save()
# 輸出也多是1L,而不是1,取決於數據庫。
>>> q.id
1
# 經過python屬性訪問模塊field
>>> q.question_text
"what's up?"
>>> q.pub_date
datetime.datetime(2017, 3, 22, 12, 57, 18, 103269, tzinfo=<UTC>)
# 經過修改屬性來修改field
>>> q.question_text = "what's up?"
>>> q.save()
# objects.all()展現數據庫中的全部問題。
>>> Question.objects.all()
<QuerySet [<Question: Question object>]>
爲了更清楚點的顯示對象,可編輯polls/models.py文件,添加一個__str__()方法到Question和Choice。
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible # 若是須要支持python2
Question(models.Model):
# ...
def __str__(self):
return self.question_text
@python_2_unicode_compatible # 若是須要支持python2
# ...
def __str__(self):
return self.choice_text
添加自定義方法
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
再次運行python manage.py shell
>>> from polls.models import Question, Choice
# 確認 __str__() 是否起做用
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
# Django提供了一個徹底由關鍵詞參數驅動的豐富的數據庫API。
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]
>>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
# 獲取今年發佈的問題
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year) #注意 pub_date和year中間有兩下劃線
<Question: What's up?>
# 若是請求的id不存在,將拋出異常.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...DoesNotExist: Question matching query does not exist.
# 按主鍵查詢,如下命令等同於Question.objects.get(id=1)
>>> Question.objects.get(pk=1)
<Question: What's up?>
# 確認自定義方法起做用
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
# 給Question多個Choice。調用create函數構造一個新的Choice對象,執行INSERT 語句,添加choice到#獲取的choice set,而後返回新建的Choice對象。Django建立了一個集合以容納ForeignKey 關係的另外一方#(如 question’s choice)。
>>> q = Question.objects.get(pk=1)
# 展現相關對象集的choice - 目前爲空
>>> q.choice_set.all()
<QuerySet []>
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice 對象有API可訪問與其相關的Question對象。
>>> c.question
<Question: What's up?>
# 反之,Question 對象可訪問Choice對象。
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
# 只要你須要,API自動跟隨關係。關係之間用下劃線關聯。
# 找出同Choice關聯的question,要求qub_date在今年之內
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# 刪除其中一個choice
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
查看更多關於對象關係:Accessing related objects,更多關於使用雙下劃線進行域查找:Field lookups,數據庫API完整信息:Database API reference
介紹Djando Admin
建立管理員用戶
python manage.py createsuperuser
Username (leave blank to use 'laiyu'): admin
Email address: 1033553122@qq.com
Password:
Password (again):
This password is too short. It must contain at least 8 characters.
Password:
Password (again):
Superuser created successfully.
開啓開發服務器
Django管理員站點默認是激活的。
若是服務器未運行,執行以下命令
python manage.py runserver
瀏覽器訪問
進入站點
輸入賬號,密碼登陸
可看到groups和users,這是由django.contrib.auth提供的,django的認證框架。
使得poll應用在站點管理頁中可修改
如上,沒看到poll應用。要展現該頁面,還需告訴admin,Question對象擁有admin接口。爲了達到這個目的,打開polls/admin.py,按以下編輯
from django.contrib import admin
# Register your models here.
from .models import Question
admin.site.register(Question)
點擊Questions
點擊what’s up
第一個 Django app Part3
Django中,web頁面和其它內容都是從views派生的,每一個view由python函數(或方法)表示,Django經過檢查請求的域名後面的那部分URL來選擇view。
編寫更多的視圖(view)
在polls/view.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 the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
而後在polls/urls.py中添加url()調用
polls/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
# ex: /polls/
url(r'^$', views.index, name='index'),
# ex: /polls/3/
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
# ex: /polls/3/results/
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
# ex: /polls/3/vote/
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
瀏覽器訪問
默認的,從站點請求頁面,好比「/polls/3」,Django會先加載mysite.urls python模塊,由於ROOT_URLCONF配置指向它。先查找urlpatterns變量,並按順序解析正則表達式,若是找到匹配‘^polls/’的,把URL中匹配到的字符串polls/去掉,而後把後面剩餘部分「3/」扔給polls.urls URLCONf進行後續處理。接着匹配到r'^(?P<question_id>[0-9]+)/$',調用detail view,以下:
detail(request=<HttpRequest object>, question_id=3)
question_id=3 來自(?P<question_id>[0-9]+)。使用雙括號於正則表達式,可捕獲正則表達式匹配到的文本,而後看成參數發給view函數。?P<question_id>定義了用於匹配正則表達式的名稱,即用來匹配函數關鍵詞參數的pattern,[0-9]+用於匹配數字序列。
編寫執行實際任務的視圖
每一個視圖都負責這兩件事之一:返回一個包含請求頁面內容的HttpResponse()對象,或者是拋出異常,好比Http404
視圖可從數據庫讀取記錄,也可以使用Django的模板系統,或者是第三方的Python模板系統,可生成PDF文件,輸出XML,建立ZIP及其它你想要的。
根據發佈日期,展現最新的5個question,逗號分隔。
在polls/view.py中添加如下內容,其它保持不變
from .models import Question
# Create your views here
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("Hello, world. You’re at the polls index")
這裏有個問題,就是視圖中的頁面設計是寫死的,若是想改變頁面樣式,須要編輯Python代碼。這裏,使用Django的模板系統來建立一個可用視圖。
先在polls目錄下建立一個名爲templates的目錄,Django會在這裏查找目標。
項目的TEMPLATES設置描述了Django將咋樣加載並渲染模板。默認的,配置文件配置了一個DjangoTemplates後端,其APP_DIRS選項被設置爲True。約定的,DjangoTemplates會在每一個INSTALLED_APP中查找templates子目錄。
在剛建立的templates目錄下建立另外一個polls目錄,並在該目錄下新建index.html文件。換句話說,template應該在polls/templates/polls/index.html。因爲app_directories模板加載器按上述描述的方式工做,因此,可簡單的使用polls/index.html引用該模板。
注意:模板命名
咱們可直接在polls/templates目錄下存放咱們的模板,可是這樣很差,Django會選擇它查找到的第一個名字匹配的模板,這樣的話,若是你在另外一個不一樣的應用下有相同名稱的目標,Django沒法區分它們。因此,咱們須要對它們進行命名,也就是把那些目標存放在以應用自身命名的另外一個目錄。
編輯模板
{% if latest_question_list %}
<url>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}" > {{ question.question_text }}</a></li>
{% endfor %}
</url>
{% else %}
<p>No polls are available.</p>
<% endif %>
使用模板來更新polls/views.py裏面的index視圖。
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()
編輯polls/views.py
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做爲其第一個參數,模板名字做爲第二個參數,字典做爲可選的第三個參數。函數返回一個通過給定context渲染的HttpResponse對象。
拋出404錯誤
polls/views.py
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})
新建模板
polls/templates/polls/detail.html
{{ question }}
運行瀏覽器
捷徑:get_object_or_404()
推薦使用get_object_or_404()
使用模板系統
到回來看detail視圖,針對給定變量question,polls/detail.html模板可能以下
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
運行結果以下
模板系統使用點查找(dot-lookup)語法訪問變量屬性。{{ question.question_text }}爲例,先在question對象上執行字典查找,而後在視圖屬性中查找-這種狀況下,找到了。若是屬性查找失敗,則嘗試列表索引查找。
方法調用發生咋{ % for %}循環:question.choice_set.all()被轉爲python代碼 question.choice_set.all(),返回適合{% for %}標籤,由Choice對象組成的可迭代對象。
移除模板中的寫死的URL
polls/index.html中編寫的指向question的鏈接
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
寫死的url,對有許多模板的項目來講,更改url會變成一件很困難的事情。因爲polls.urls模塊的url()函數中定義了命名的參數,可經過{% url %}模板標籤來移除在url配置中,特定url路徑上定義的依賴:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
如下是'detail' polls/urls.py中的定義
...
# 'name'的值被 {% url %} 模板標籤訂義
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...
這樣當須要更改應用的url,好比更改成polls/specifics/12/,能夠不用在目標中更改寫死的url,直接在polls/urls.py中更改。
...
# 添加 'specifics'到url
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...
給URL名字增長名稱空間
在URLConf中添加名稱空間,以便使用{% url %}模板標籤時,django能區分不用應用的url。
在polls/urls.py中添加app_name來設置應用的名稱空間。
from django.conf.urls import url
from . import views
app_name = 'polls'
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
更改polls/index.html模板
更改模板
polls/templates/polls/index.html
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
爲以下:
polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
第一個 Django app Part4
編寫一個簡單的表格
更新detail模板(polls/detail.html)
<h1>{{ question.question_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>
說明:
1)每一個choice都有一個對應的radio按鈕。每一個radio按鈕的值都同關聯問題choice id關聯。每一個radio按鈕的名字爲choice。這也就意味着,當某人選擇其中一個radio按鈕並提交表單時,發送POST數據choice=#,其中#表示所選擇的choice的id
2)設置表單的action爲 {% url 'polls:vote' question.id %},設置method='post'(對立的method='get'),這很重要,由於這會改變服務器端的數據。
3)forloop.counter一個表示當前循環的執行次數的整數計數器。 這個計數器是從1開始的,因此在第一次循環時 forloop.counter 將會被設置爲1
4)由於是POST表單,須要考慮跨站腳本攻擊,因此使用{% csrf_token %}模板標籤。
接着,建立處理提交數據的視圖
還記得polls/urls.py有以下設置:
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
修改polls/views.py中的樣本函數vote
polls/views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
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):
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
說明:
Request.POST相似字典的對象,容許經過key名稱來訪問提交的數據。例中,request.POST['choice']返回字符串表示choice的ID。Request.POST值老是字符串。
相似的,django提供了request.GET來訪問GET data
若是POST數據中無choice,Request.POST['choice']將拋出KeyError。
增長choice計數後,code返回HttpResponseRedirect而非正常的HttpResponse。HttpResponseRedirect攜帶單個參數:將要重定向至的url。
使用reverse()函數避免在view視圖中寫死url。reverse()調用返回一個相似以下的字符串:
'/polls/3/results'
其中,3爲問題id,該重訂向url將會調用'results'視圖來展現最終頁面。
As mentioned in Tutorial 3, request is an HttpRequest object. For more on HttpRequest objects, see the request and response documentation.
投票以後,vote視圖,重定向到問題的結果頁面。重寫vote視圖:
polls/views.py
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})
建立polls/results.html模板
polls/templates/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>
瀏覽器訪問
使用通用視圖
使用通用視圖來轉換poll應用。
1)轉換URLConf
2)刪除舊的,沒必要要的視圖
3)引入基於Django的通用視圖(generic view)
改良的URLConf
polls/urls.py
from django.conf.urls import url
from . import views
app_name = 'polls'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
改良的視圖
polls/views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
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 the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
def vote(request, question_id):
... # same as above, no changes needed.
問題:問題列表這麼調整後變成了空白,怎麼解決?
這裏使用了兩種視圖:ListView和DetailView。這兩種對象分別抽象了list對象的展現和特定讀寫的詳細頁面展現。
每種通用視圖使用model屬性來區分須要做用的模塊。
DetailView視圖指望從ULR捕獲的主鍵值被稱爲pk,因此把question_id改爲了pk
默認的DetailView視圖使用名爲<app name>/<model name>_detail.html的模板。例子中,使用polls/question_detail.html。template_name屬性告訴Django使用指定名稱的模板,而不是使用默認模板名稱。
相似的,ListView使用<app name>/<model name>_list.html模板。
對於ListView,自動生成context變量question_list。爲了重寫這個,提供context_object_name來指定本身的變量latest_question_list。
第一個 Django app Part5
略
第一個 Django app Part6
自定義app樣式和感觀。
在polls目錄下新建static。Django會在這查找靜態文件。相似查找模板。
Django的STATICFILES_FINDERS設置包含了finder list,告訴它怎麼查找靜態文件。其中一個默認的finder AppDirectoriesFinder會在每一個INSTALLED_APPS查找static子目錄。管理站點對靜態文件使用相同的目錄結構
static目錄下新建一個polls目錄,在該目錄下新建名爲style.css的文件。使用polls/style.css引用資源
編輯style.css
polls/static/polls/style.css
li a {
color: green;
}
修改polls/templates/polls/index.html
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />
運行效果:
{% static %}模板標籤生成靜態文件的絕對URL。
添加背景圖片
在polls/static/polls目錄下新建images目錄,並在該目錄下存放一張名爲background.gif的圖片。
修改sytle.cass,新增代碼以下
body {
background: white url("images/background.gif") no-repeat right bottom;
}
刷新頁面,可看到屏幕右上方顯示動態圖片
注意:{% static %}模板標籤不適用非Django生成的靜態文件,好比樣式表單。
第一個 Django app Part7
自定義管理站點 form
polls/admin.py
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question_text']
admin.site.register(Question, QuestionAdmin)
上述代碼使得Question field位於Publication date以後
分隔成多個fieldsets
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
admin.site.register(Question, QuestionAdmin)
添加關聯對象
方式1,註冊模塊
polls/admin.py
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
那個form中,Question field是一個select box,包含數據庫中的每一個問題。Django知道ForeignKey應該在<select> box中出現。
Question的編輯和+號按鈕,可分別打開question編輯(須要先選定問題纔可用)和添加頁面。
方式2:
from django.contrib import admin
from .models import Choice, Question
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
默認3個choice是由extra指定的,點擊Add another Choice連接,自動新增一個Choice。
這裏有個問題,就是佔用空間比較大。爲此,Django提供了一個tabular的方式來展現inline相關對象。
polls/admin.py
class ChoiceInline(admin.TabularInline):
#...
delete?列用於提供刪除操做(經過點擊Add another Choice增長的行。)
自定義admin change list
展現單個field。使用list_display admin選項,供展現的field元組,好比列。
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date')
還可添加was_published_recently()方法。
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
可點擊列標題來排序,was_published_recently列除外,由於不支持按任意方法的輸出排序。另外,was_published_recently列默認爲方法名稱(下劃線替換了空格)。
經過給方法增長屬性來改善。
polls/models.py
class Question(models.Model):
# ...
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
效果:
增長過濾器
修改polls/admin.py,新增list_filer,新增代碼以下。
list_filter = ['pub_date']
效果以下:
filter展現類型取決於你過濾的field。由於pub_date是DateTimeField,Django知道怎麼給恰當的filter選項:Any date,Today等
增長搜索
search_fields = ['question_text']
效果以下:
自定義admin樣式和感觀
自定義項目模板
在項目目錄中(包含manage.py文件)下建立template目錄。template可放在Django可訪問的任何文件系統,可是保持模板在項目裏,是須要好好遵照的約定。
編輯mysite/settings.py,在TEMPLATES設置中添加一個DIRS選項。
mysite/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
DIRS是當加載Django模板時,須要檢查的文件系統目錄列表,是一個搜索路徑。
如今在templates目錄中新建一個名爲admin的目錄,從默認的Django admin模板目錄(django/contrib/admin/templates)中拷貝模板文件admin/base_site.html到該目錄。
編輯文件,替換{{ site_header|default:_('Django administration') }}爲本身的站點名稱。
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
{% endblock %}
該例字告訴咱們使用這種方法來重寫模板。
模板包含不少相似{% block branding %} and {{ title }}的文本,{%和{{標籤是Django的模板語言。
參考鏈接:
https://docs.djangoproject.com/en/1.10/intro/tutorial02/