Python-Django 第一個Django app

第一個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/

相關文章
相關標籤/搜索