天天看點

Python-Django 第一個Django app

第一個Django app

by:授客 QQ:1033553122

測試環境:

Python版本:python-3.4.0.amd64

下載下傳位址:https://www.python.org/downloads/release/python-340/

Win7 64位

Django  1.11.4

下載下傳位址:https://www.djangoproject.com/download/

安裝django

python setup.py install

測試是否成功

>>> import django

>>> print(django.get_version())

1.10.6

>>>

或者

python -m django --version

參考連接配接:

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-Django 第一個Django app

運作服務時,可指定端口,如下

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/

    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目錄看起來如下:

    admin.py

    apps.py

    migrations/

        __init__.py

    urls.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

    url(r'^polls/', include('polls.urls')),

    url(r'^admin/', admin.site.urls),

]

浏覽器通路

Python-Django 第一個Django app

1) 正規表達式:$, xxx$:比對xxx結尾的字元串)。

2) 當Django遇到include()時,會先把請求中的url同include()函數對應的正規表達式比對(例中按先後順序分别為:'^polls/','^admin/',如果比對到,則把URL中比對到的字元串之後的剩餘URL扔給include引用的app URLconf進行後續處理。

例子:修改polls/urls.py内容如下

    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其它地方引用時不産生歧義。

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

  Apply all migrations: admin, auth, contenttypes, polls, sessions

  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

>>> 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?"

# objects.all()展示資料庫中的所有問題。

<QuerySet [<Question: Question object>]>

為了更清楚點的顯示對象,可編輯polls/models.py檔案,添加一個__str__()方法到Question和Choice。

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

        return self.choice_text

添加自定義方法

import datetime

from django.utils import timezone

    def was_published_recently(self):

        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

再次運作python manage.py shell

# 确認 __str__() 是否起作用

<QuerySet [<Question: What's up?>]>

# Django提供了一個完全由關鍵詞參數驅動的豐富的資料庫API。

>>> Question.objects.filter(id=1)

<QuerySet [<Question: What's up?>]

>>>> Question.objects.filter(question_text__startswith='What')

# 擷取今年釋出的問題

>>> 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)

# 确認自定義方法起作用

>>> 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)。

# 展示相關對象集的choice - 目前為空

>>> q.choice_set.all()

>>> 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 對象可通路Choice對象。

<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)

# 删除其中一個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: [email protected]

Password:

Password (again):

This password is too short. It must contain at least 8 characters.

Superuser created successfully.

開啟開發伺服器

Django管理者站點預設是激活的。

如果伺服器未運作,執行如下指令

python manage.py runserver

Python-Django 第一個Django app

進入站點

輸入帳号,密碼登入

Python-Django 第一個Django app

可看到groups和users,這是由django.contrib.auth提供的,django的認證架構。

使得poll應用在站點管理頁中可修改

如上,沒看到poll應用。要展示該頁面,還需告訴admin,Question對象擁有admin接口。為了達到這個目的,打開polls/admin.py,按如下編輯

# Register your models here.

from .models import Question

admin.site.register(Question)

Python-Django 第一個Django app

點選Questions

Python-Django 第一個Django app

點選what’s up

Python-Django 第一個Django app

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

    # ex: /polls/

    # 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'),

Python-Django 第一個Django app

預設的,從站點請求頁面,比如“/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中添加以下内容,其它保持不變

# Create your views here

     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.template import loader

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對象。

點選連接配接,打開詳情。

Python-Django 第一個Django app

捷徑:render()

編輯polls/views.py

    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

# ...

    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 }}

運作浏覽器

Python-Django 第一個Django app

捷徑: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>

運作結果如下

Python-Django 第一個Django app

模闆系統使用點查找(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來設定應用的名稱空間。

app_name = 'polls'

更改polls/index.html模闆

更改模闆

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)

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

<form action="{% url 'polls:vote' question.id %}" method="post">

{% csrf_token %}

    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />

    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />

<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

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

    question = get_object_or_404(Question, pk=question_id)

        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視圖:

    return render(request, 'polls/results.html', {'question': question})

建立polls/results.html模闆

polls/templates/polls/results.html

<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>

Python-Django 第一個Django app

使用通用視圖

使用通用視圖來轉換poll應用。

1)轉換URLConf

2)删除舊的,不必要的視圖

3)引入基于Django的通用視圖(generic view)

改良的URLConf

    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'),

改良的視圖

from django.http import HttpResponseRedirect

from django.views import generic

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):

    template_name = 'polls/results.html'

    ... # 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' %}" />

運作效果:

Python-Django 第一個Django app

{% 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

class QuestionAdmin(admin.ModelAdmin):

    fields = ['pub_date', 'question_text']

admin.site.register(Question, QuestionAdmin)

上述代碼使得Question field位于Publication date之後

Python-Django 第一個Django app

分隔成多個fieldsets

    fieldsets = [

        (None,               {'fields': ['question_text']}),

        ('Date information', {'fields': ['pub_date']}),

Python-Django 第一個Django app

添加關聯對象

方式1,注冊子產品

admin.site.register(Choice)

Python-Django 第一個Django app

那個form中,Question field是一個select box,包含資料庫中的每個問題。Django知道ForeignKey應該在<select> box中出現。

Question的編輯和+号按鈕,可分别打開question編輯(需要先標明問題才可用)和添加頁面。

方式2:

class ChoiceInline(admin.StackedInline):

    model = Choice

    extra = 3

        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),

    inlines = [ChoiceInline]

Python-Django 第一個Django app

預設3個choice是由extra指定的,點選Add another Choice連結,自動新增一個Choice。

這裡有個問題,就是占用空間比較大。為此,Django提供了一個tabular的方式來展示inline相關對象。

class ChoiceInline(admin.TabularInline):

    #...

Python-Django 第一個Django app

delete?列用于提供删除操作(通過點選Add another Choice增加的行。)

自定義admin change list

展示單個field。使用list_display admin選項,供展示的field元組,比如列。

    list_display = ('question_text', 'pub_date')

還可添加was_published_recently()方法。

    list_display = ('question_text', 'pub_date', 'was_published_recently')

Python-Django 第一個Django app

可點選列标題來排序,was_published_recently列除外,因為不支援按任意方法的輸出排序。另外,was_published_recently列預設為方法名稱(下劃線替換了空格)。

通過給方法增加屬性來改善。

polls/models.py

        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?'

效果:

Python-Django 第一個Django app

增加過濾器

修改polls/admin.py,新增list_filer,新增代碼如下。

list_filter = ['pub_date']

效果如下:

Python-Django 第一個Django app

filter展示類型取決于你過濾的field。因為pub_date是DateTimeField,Django知道怎麼給恰當的filter選項:Any date,Today等

增加搜尋

search_fields = ['question_text']

Python-Django 第一個Django app

自定義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/

作者:授客

QQ:1033553122

全國軟體測試QQ交流群:7156436

Git位址:https://gitee.com/ishouke

友情提示:限于時間倉促,文中可能存在錯誤,歡迎指正、評論!

作者五行缺錢,如果覺得文章對您有幫助,請掃描下邊的二維碼打賞作者,金額随意,您的支援将是我繼續創作的源動力,打賞後如有任何疑問,請聯系我!!!

           微信打賞                       

支付寶打賞                  全國軟體測試交流QQ群  

Python-Django 第一個Django app
Python-Django 第一個Django app
Python-Django 第一個Django app