使用Django2快速開(kāi)發(fā)Web項(xiàng)目的詳細(xì)步驟
Django 是一款基于 Python 編寫并且采用 MVC 設(shè)計(jì)模式的開(kāi)源的 Web 應(yīng)用框架,早期是作為勞倫斯出版集團(tuán)新聞網(wǎng)站的 CMS 內(nèi)容管理系統(tǒng)而開(kāi)發(fā),后于 2005 年 7 月在 BSD 許可協(xié)議下開(kāi)源,并于 2017 年 12 月 2 日 發(fā)布 2.0 正式版。
本文基于《Django 官方 Tutorials》以及《Django REST framework 官方 Tutorials》編寫,發(fā)稿時(shí)所使用的 Django 版本為 2.1.4,Python 版本為 3.6.6,文中涉及的代碼都已經(jīng)由筆者驗(yàn)證運(yùn)行通過(guò),最終形成了一個(gè)簡(jiǎn)單項(xiàng)目并推送至筆者Github上的jungle項(xiàng)目當(dāng)中,需要的朋友可以基于此來(lái)逐步步完善成為一個(gè)產(chǎn)品化的項(xiàng)目。
新建 Django 項(xiàng)目
下面的命令行展示了在 Windows 操作系統(tǒng)下,基于 venv 虛擬環(huán)境搭建一個(gè) Django 項(xiàng)目的步驟:
# 建立虛擬環(huán)境 C:\Workspace\django λ python -m venv venv # 激活虛擬環(huán)境 C:\Workspace\django λ .\venv\Scripts\activate.bat (venv) λ # 安裝Django C:\Workspace\django (venv) λ pip install Django Looking in indexes: https://mirrors.aliyun.com/pypi/simple/ Collecting Django Using cached https://mirrors.aliyun.com/pypi/packages/fd/9a/0c028ea0fe4f5803dda1a7afabeed958d0c8b79b0fe762ffbf728db3b90d/Django-2.1.4-py3-none-any.whl Collecting pytz (from Django) Using cached https://mirrors.aliyun.com/pypi/packages/f8/0e/2365ddc010afb3d79147f1dd544e5ee24bf4ece58ab99b16fbb465ce6dc0/pytz-2018.7-py2.py3-none-any.whl Installing collected packages: pytz, Django Successfully installed Django-2.1.4 pytz-2018.7 # 進(jìn)入虛擬環(huán)境目錄,新建一個(gè)Django項(xiàng)目 C:\Workspace\django (venv) λ django-admin startproject mysite C:\Workspace\django (venv) λ ls mysite/ venv/ # 進(jìn)入新建的Django項(xiàng)目,建立一個(gè)應(yīng)用 C:\Workspace\django (venv) λ cd mysite\ C:\Workspace\django\mysite (venv) λ python manage.py startapp demo C:\Workspace\django\mysite (venv) λ ls demo/ manage.py* mysite/ # 同步數(shù)據(jù)庫(kù) C:\Workspace\django\mysite (venv) λ 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 admin.0003_logentry_add_action_flag_choices... 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 auth.0009_alter_user_last_name_max_length... OK Applying sessions.0001_initial... OK # 啟動(dòng)開(kāi)發(fā)服務(wù) (venv) λ python manage.py runserver 8080 Performing system checks... System check identified no issues (0 silenced). January 03, 2019 - 21:31:48 Django version 2.1.4, using settings 'mysite.settings' Starting development server at http://127.0.0.1:8080/ Quit the server with CTRL-BREAK. # 返回uinika虛擬環(huán)境目錄,并將當(dāng)前虛擬環(huán)境的依賴導(dǎo)入至requirements.txt C:\Workspace\django\mysite (venv) λ cd .. C:\Workspace\django (venv) λ pip freeze > requirements.txt C:\Workspace\django (venv) λ ls mysite/ requirements.txt venv/
通過(guò) django-admin startproject
命令創(chuàng)建的外部 mysite/
目錄是 Web 項(xiàng)目的容器,而 manage.py
文件是用于與 Django 項(xiàng)目交互的命令行工具,更多的使用方式可以參閱 django-admin 文檔 。。
mysite/ manage.py mysite/ __init__.py settings.py urls.py wsgi.py
內(nèi)部嵌套的 mysite/
目錄是用于放置項(xiàng)目中具體的 Python 包,它的名稱是您需要用來(lái)導(dǎo)入其中任何內(nèi)容的 Python 包名稱,例如 mysite.urls
。
mysite/__init__.py
: 空文件,用于提示系統(tǒng)將當(dāng)前目錄識(shí)別為一個(gè) Python 包。mysite/settings.py
: Django 項(xiàng)目的配置文件,更多配置請(qǐng)查閱 Django settings 。mysite/urls.py
: 當(dāng)前 Django 項(xiàng)目的 URL 聲明,更多內(nèi)容請(qǐng)參閱 URL dispatcher 。mysite/wsgi.py
: 兼容 WSGI 規(guī)范的當(dāng)前項(xiàng)目入口點(diǎn),更多細(xì)節(jié)可以閱讀 如果使用 WSGI 進(jìn)行部署 。
建立 mysite
項(xiàng)目之后,上面的命令行還通過(guò)了 py manage.py startapp
建立了一個(gè) demo/
應(yīng)用目錄,Django 當(dāng)中一個(gè)項(xiàng)目( mysite
)可以擁有多個(gè)應(yīng)用( demo
), demo/
目錄下的文件結(jié)構(gòu)如下:
demo/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py views.py
請(qǐng)求與響應(yīng)
首先進(jìn)入 Python 虛擬環(huán)境并進(jìn)入 mysite
目錄后,執(zhí)行如下命令:
C:\Workspace\django\mysite (master -> origin) (venv) λ python manage.py startapp polls
新建一個(gè) polls
應(yīng)用之后,打開(kāi)該目錄下的 polls/views.py
源碼文件,輸入以下代碼:
from django.http import HttpResponse def index(request): return HttpResponse("你好,這是一個(gè)投票應(yīng)用!")
接下來(lái),我們需要將上面修改的視圖文件 views.py
映射到一個(gè) URL,先在 polls/
目錄下新建一個(gè) urls.py
文件,然后鍵入下面這段代碼:
from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ]
最后,將上面定義的應(yīng)用的 URL 聲明文件 polls/urls.py
包含至項(xiàng)目的 mysite/urls.py
當(dāng)中,
from django.contrib import admin from django.urls import include, path urlpatterns = [ path('polls/', include('polls.urls')), path('admin/', admin.site.urls), ]
上面代碼中出現(xiàn)的 include()
函數(shù)主要用于引入其它 URL 配置文件,這樣我們就可以通過(guò) http://localhost:8080/polls/
路徑訪問(wèn)到如下信息了:
模型和管理頁(yè)面
mysite/settings.py
文件包含了項(xiàng)目的基本配置,該文件通過(guò)如下聲明默認(rèn)使用 Django 內(nèi)置的 SQLite 作為項(xiàng)目數(shù)據(jù)庫(kù)。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
如果使用其它數(shù)據(jù)庫(kù),則可以將配置書寫為下面的格式:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 數(shù)據(jù)庫(kù)引擎名稱 'NAME': 'db', # 數(shù)據(jù)庫(kù)連接名稱 'USER': 'uinika', # 數(shù)據(jù)庫(kù)連接用戶名 'PASSWORD': 'test', # 數(shù)據(jù)庫(kù)連接密碼 'HOST': 'localhost', # 數(shù)據(jù)庫(kù)主機(jī)地址 'PORT': '3306', # 數(shù)據(jù)庫(kù)端口 } }
其中 ENGINE
屬性可以根據(jù)項(xiàng)目所使用數(shù)據(jù)庫(kù)的不同而選擇如下值:
- SQLite:django.db.backends.sqlite3
- MySQL:django.db.backends.mysql
- PostgreSQL:django.db.backends.postgresql
- Oracle:django.db.backends.oracle
接下來(lái)繼續(xù)修改 mysite/settings.py
,設(shè)置 TIME_ZONE
屬性為項(xiàng)目使用國(guó)家的時(shí)區(qū)。
LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Chongqing'
mysite/settings.py
文件頭部的 INSTALLED_APPS
屬性定義了當(dāng)前項(xiàng)目使用的應(yīng)用程序。
INSTALLED_APPS = [ 'django.contrib.admin', # 管理員站點(diǎn) 'django.contrib.auth', # 認(rèn)證授權(quán)系統(tǒng) 'django.contrib.contenttypes', # 內(nèi)容類型框架 'django.contrib.sessions', # 會(huì)話框架 'django.contrib.messages', # 消息框架 'django.contrib.staticfiles', # 靜態(tài)文件管理 ]
在前面命令行中執(zhí)行的 python manage.py migrate
命令會(huì)檢查 INSTALLED_APPS
屬性的設(shè)置,并為其中的每個(gè)應(yīng)用創(chuàng)建所需的數(shù)據(jù)表,實(shí)際上 migrate
命令只會(huì)為對(duì) INSTALLED_APPS
里聲明了的應(yīng)用進(jìn)行數(shù)據(jù)庫(kù)遷移 。
了解項(xiàng)目配置文件的一些設(shè)置之后,現(xiàn)在來(lái)編輯 polls/models.py
文件新建 Question(問(wèn)題)
和 Choice(選項(xiàng))
兩個(gè)數(shù)據(jù)模型:
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)
每個(gè)自定義模型都是 django.db.models.Model
的子類,模型里的類變量都表示一個(gè)數(shù)據(jù)庫(kù)字段,每個(gè)字段實(shí)質(zhì)都是 Field
類的實(shí)例。注意在 Choice
使用了 ForeignKey
屬性定義了一個(gè)與 Question
的外鍵關(guān)聯(lián)關(guān)系,Django 支持所有常用的多對(duì)一、多對(duì)多和一對(duì)一數(shù)據(jù)庫(kù)關(guān)系。
數(shù)據(jù)庫(kù)模型建立完成之后,由于 PollsConfig
類位于 polls/apps.py
文件當(dāng)中,所以其對(duì)應(yīng)的點(diǎn)式路徑為 polls.apps.PollsConfig
,現(xiàn)在我們需要將該路徑添加至 mysite/settings.py
文件的 INSTALLED_APPS
屬性:
INSTALLED_APPS = [ 'polls.apps.PollsConfig', # 添加PollsConfig 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
通過(guò) manage.py
提供的 makemigrations
命令,將模型的修改遷移到 SQLite 數(shù)據(jù)庫(kù)當(dāng)中。
C:\Workspace\django\mysite (master -> origin) (venv) λ python manage.py makemigrations polls Migrations for 'polls': polls\migrations\0001_initial.py - Create model Choice - Create model Question - Add field question to choice
我們還可以通過(guò) manage.py
提供的 sqlmigrate
命令,查看數(shù)據(jù)遷移過(guò)程中執(zhí)行了哪些 SQL 語(yǔ)句,該命令并不會(huì)實(shí)質(zhì)性執(zhí)行 Django 模型到數(shù)據(jù)庫(kù)的遷移任務(wù)。
C:\Workspace\django\mysite (master -> origin) (venv) λ python manage.py sqlmigrate polls 0001 BEGIN; -- -- Create model Choice -- CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "v otes" 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, "choice_text" varchar(200) NOT NULL, "v otes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERR ED); INSERT INTO "polls_choice" ("id", "choice_text", "votes", "question_id") SELECT "id", "choice_text", "votes", NULL FR OM "polls_choice__old"; DROP TABLE "polls_choice__old"; CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id"); COMMIT;
Django 模型的數(shù)據(jù)庫(kù)主鍵 ID 會(huì)被自動(dòng)創(chuàng)建, 并會(huì)在外鍵字段名稱后追加 _id
字符串作為后綴。
接下來(lái)運(yùn)行 manage.py
提供的 migrate
命令,在根據(jù)新定義的模型創(chuàng)建相應(yīng)的數(shù)據(jù)庫(kù)表。
C:\Workspace\django\mysite (master -> origin) (venv) λ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Applying polls.0001_initial... OK
為了便于在版本管理系統(tǒng)提交遷移數(shù)據(jù),Django 將模型的修改分別獨(dú)立為 生成 和 應(yīng)用 兩個(gè)命令,因此修改 Django 模型會(huì)涉及如下 3 個(gè)步驟:
- 編輯models.py文件修改模型。
- 運(yùn)行python manage.py makemigrations為模型的改變生成遷移文件。
- 運(yùn)行python manage.py migrate來(lái)應(yīng)用數(shù)據(jù)庫(kù)遷移。
完成上述 Django 模型與數(shù)據(jù)庫(kù)的同步之后,接下來(lái)可以通過(guò) manage.py
提供的 shell
命令,在命令行工具內(nèi)運(yùn)行 Django 提供的交互式 API。
C:\Workspace\django\mysite (master -> origin) (venv) λ python manage.py shell Python 3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 03:37:03) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from polls.models import Choice, Question >>> Question.objects.all() <QuerySet []> >>> from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) >>> q.save() >>> q.id 1 >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2019, 1, 4, 9, 10, 1, 955820, tzinfo=<UTC>) >>> q.question_text = "What's up?" >>> q.save() >>> Question.objects.all() <QuerySet [<Question: Question object (1)>]>
上面命令行執(zhí)行結(jié)果中的 <Question: Question object (1)>
對(duì)于實(shí)際開(kāi)發(fā)沒(méi)有意義,因此可以考慮為上面建立的 Django 模型增加 __str__()
方法直接打印模型對(duì)象的屬性數(shù)據(jù)。為了便于進(jìn)一步測(cè)試,這里還為 Question
類添加一個(gè)自定義的 was_published_recently()
方法:
import datetime from django.db import models from django.utils import timezone class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') # 自定義was_published_recently()方法 def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1) # 添加__str__()方法 def __str__(self): return self.question_text class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) # 添加__str__()方法 def __str__(self): return self.choice_text
完成修改工作之后,再一次運(yùn)行 python manage.py shell
命令:
C:\Workspace\django\mysite (master -> origin) (venv) λ python manage.py shell Python 3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 03:37:03) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from polls.models import Choice, Question >>> Question.objects.all() <QuerySet [<Question: What's up?>]> >>> 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) <Question: What's up?> >>> Question.objects.get(id=2) Traceback (most recent call last): File "<console>", line 1, in <module> File "C:\Workspace\django\venv\lib\site-packages\django\db\models\manager.py", line 82, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "C:\Workspace\django\venv\lib\site-packages\django\db\models\query.py", line 399, in get self.model._meta.object_name polls.models.Question.DoesNotExist: Question matching query does not exist. >>> Question.objects.get(pk=1) <Question: What's up?> >>> q = Question.objects.get(pk=1) >>> q.was_published_recently() True >>> q = Question.objects.get(pk=1) >>> 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) >>> c.question <Question: What's up?> >>> q.choice_set.all() <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> q.choice_set.count() 3 >>> Choice.objects.filter(question__pub_date__year=current_year) <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete() (1, {'polls.Choice': 1})
管理站點(diǎn)
Django 能夠根據(jù)模型自動(dòng)創(chuàng)建后臺(tái)管理界面, 這里我們執(zhí)行 manage.py
提供的 createsuperuser
命令創(chuàng)建一個(gè)管理用戶:
C:\Workspace\django\mysite (master -> origin) (venv) λ python manage.py createsuperuser Username (leave blank to use 'zhenghang'): hank Email address: uinika@outlook.com Password: ******** Password (again): ******** Superuser created successfully.
啟動(dòng) Django 服務(wù)之后,就可以通過(guò) URL 地址 http://localhost:8080/admin/login
并使用上面新建的用戶名和密碼進(jìn)行登陸管理操作:
登陸后默認(rèn)只能對(duì)權(quán)限相關(guān)的 User
和 Group
進(jìn)行管理,如果我們需要將 Question
數(shù)據(jù)模型納入管理,那么必須要在 polls/admin.py
文件對(duì)其進(jìn)行注冊(cè)。
from django.contrib import admin from .models import Question admin.site.register(Question)
完成注冊(cè)之后,刷新管理站點(diǎn)頁(yè)面即可查看到 Question
管理選項(xiàng):
視圖與模板
Django 使用 URLconfs
配置將 URL 與視圖關(guān)聯(lián),即將 URL 映射至視圖,下面我們將向 polls/views.py
文件添加一些能夠接收參數(shù)的視圖:
from django.http import HttpResponse def index(request): return HttpResponse("你好,這是一個(gè)投票應(yīng)用!") def detail(request, question_id): return HttpResponse("你正在查看問(wèn)題 %s 。" % question_id) def results(request, question_id): response = "你看到的是問(wèn)題 %s 的結(jié)果。" return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("你正在對(duì)問(wèn)題 %s 進(jìn)行投票。" % question_id)
然后將這些新的視圖添加至 polls.urls
模塊:
from django.urls import path from . import views urlpatterns = [ # 訪問(wèn) http://localhost:8080/polls/ path('', views.index, name='index'), # 訪問(wèn) http://localhost:8080/polls/5/ path('<int:question_id>/', views.detail, name='detail'), # 訪問(wèn) http://localhost:8080/polls/5/results/ path('<int:question_id>/results/', views.results, name='results'), # 訪問(wèn) http://localhost:8080/polls/5/vote/ path('<int:question_id>/vote/', views.vote, name='vote'), ]
Django 的每個(gè)視圖只會(huì)完成兩個(gè)任務(wù):第 1 是返回一個(gè)包含被請(qǐng)求頁(yè)面內(nèi)容的 HttpResponse
對(duì)象,或者拋出一個(gè) Http404
這樣的異常。這里為了展示數(shù)據(jù)庫(kù)里按照發(fā)布日期排序的最近五個(gè)投票問(wèn)題,我們?cè)傧?polls/views.py
代碼文件的 index()
函數(shù)添加如下內(nèi)容:
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)
這樣直接將數(shù)據(jù)庫(kù)查詢結(jié)果輸出到頁(yè)面的方式并不優(yōu)雅,實(shí)際開(kāi)發(fā)環(huán)境當(dāng)中我們通常會(huì)使用模板頁(yè)面來(lái)展示數(shù)據(jù),首先在 polls
應(yīng)用目錄下創(chuàng)建一個(gè)用來(lái)存放模板文件的 templates
目錄。由于站點(diǎn)配置文件 mysite/settings.py
里 TEMPLATES
屬性的默認(rèn)設(shè)置,能夠讓 Django 在每個(gè) INSTALLED_APPS
文件夾中自動(dòng)尋找 templates
子目錄,從而正確定位出模板的位置。
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], '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', ], }, }, ]
接下來(lái)繼續(xù)在 templates
下面新建一個(gè) polls
目錄,然后在里邊放置一個(gè) index.html
文件,此時(shí)通過(guò) URL 地址 http://localhost:8080/polls/
就可以訪問(wèn)到這個(gè)模板文件,模板文件會(huì)將按照發(fā)布日期排序了的 Question
列表 latest_question_list
放置到 HttpResponse
上下文,并在 polls/index.html
模板當(dāng)中完成數(shù)據(jù)綁定。
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))
事實(shí)上,通過(guò)使用 render()
方法,Django 能夠以更加簡(jiǎn)化的方式完成載入模板、填充上下文、返回 HttpResponse 對(duì)象這一系列步驟:
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)
接下來(lái)處理投票詳情頁(yè)面,這里會(huì)有一個(gè)新原則,即如果指定 ID
所對(duì)應(yīng)的 Question
不存在,那么視圖就會(huì)拋出一個(gè) Http404
異常。在 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("問(wèn)題不存在!") return render(request, 'polls/detail.html', {'question': qu
然后暫時(shí)向 polls/templates/polls/detail.html
添加一行簡(jiǎn)單的 代碼便于測(cè)試上面的代碼。
Django 提供了諸如 get_object_or_404()
、 get_list_or_404()
這樣的快捷函數(shù)語(yǔ)法糖來(lái)解決 Http404
判斷的問(wèn)題,因而上一步的代碼依然可以進(jìn)一步簡(jiǎn)化為下面這樣:
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})
讓我們進(jìn)一步完善polls/templates/polls/detail.html,填充完整的視圖代碼:
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul>
通過(guò)在模板代碼中使用.符號(hào)來(lái)訪問(wèn)變量屬性,例如對(duì)于上面代碼中的, Django 首先會(huì)嘗試對(duì)question對(duì)象使用字典查找(既obj.get(str)),如果失敗再嘗試屬性查找(既obj.str),如果依然失敗就會(huì)嘗試列表查找(即obj[int])。另外循環(huán)for中的question.choice_set.all語(yǔ)句會(huì)被解析為question.choice_set.all()的 Python 的函數(shù)調(diào)用,完成后將返回一個(gè)可迭代的Choice對(duì)象,該對(duì)象僅限于for循環(huán)標(biāo)簽內(nèi)部使用。
在polls/templates/polls/index.html編寫的投票鏈接里使用了諸如<a href="/polls//" rel="external nofollow" ></a>這樣的硬編碼,但是這樣容易造成視圖與后端業(yè)務(wù)的耦合,因此 Django 提供了url標(biāo)簽來(lái)解決這個(gè)問(wèn)題。
<a href="{% url 'detail' question.id %}" rel="external nofollow" >{{ question.question_text }}</a>
實(shí)際上在mysite/polls/urls.py內(nèi)的函數(shù)調(diào)用path('<int:question_id>/', views.detail, name='detail')當(dāng)中,path()的name屬性就是作用于url標(biāo)簽中的這個(gè)特性的。
為了避免項(xiàng)目當(dāng)中各種應(yīng)用的 URL 重名,避免url標(biāo)簽被使用時(shí)產(chǎn)生歧義,需要在polls/urls.py上添加應(yīng)用的命名空間作為區(qū)分。
from django.urls import path from . import views app_name = 'polls' 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'), ]
然后編輯polls/templates/polls/index.html文件,為每個(gè)url標(biāo)簽添加上面聲明的polls:命名空間。
<li><a href="{% url 'polls:detail' question.id %}" rel="external nofollow" >{{ question.question_text }}</a></li>
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
pandas 數(shù)據(jù)索引與選取的實(shí)現(xiàn)方法
這篇文章主要介紹了pandas 數(shù)據(jù)索引與選取的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06python使用pandas實(shí)現(xiàn)數(shù)據(jù)分割實(shí)例代碼
這篇文章主要介紹了python使用pandas實(shí)現(xiàn)數(shù)據(jù)分割實(shí)例代碼,介紹了使用pandas實(shí)現(xiàn)對(duì)dataframe格式的數(shù)據(jù)分割成時(shí)間跨度相等的數(shù)據(jù)塊,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01pycharm中使用pyplot時(shí)報(bào)錯(cuò)MatplotlibDeprecationWarning
最近在使用Pycharm中matplotlib作圖處理時(shí)報(bào)錯(cuò),所以這篇文章主要給大家介紹了關(guān)于pycharm中使用pyplot時(shí)報(bào)錯(cuò)MatplotlibDeprecationWarning的相關(guān)資料,需要的朋友可以參考下2023-12-12Python使用pyautogui模塊實(shí)現(xiàn)自動(dòng)化鼠標(biāo)和鍵盤操作示例
這篇文章主要介紹了Python使用pyautogui模塊實(shí)現(xiàn)自動(dòng)化鼠標(biāo)和鍵盤操作,簡(jiǎn)單描述了pyautogui模塊的功能,并結(jié)合實(shí)例形式較為詳細(xì)的分析了Python使用pyautogui模塊實(shí)現(xiàn)鼠標(biāo)與鍵盤自動(dòng)化操作相關(guān)技巧,需要的朋友可以參考下2018-09-09Python私有屬性私有方法應(yīng)用實(shí)例解析
這篇文章主要介紹了Python私有屬性私有方法應(yīng)用場(chǎng)景解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09在Python中使用itertools模塊中的組合函數(shù)的教程
這篇文章主要介紹了在Python中使用itertools模塊中的組合函數(shù)的教程,來(lái)自IBM官方技術(shù)文檔,需要的朋友可以參考下2015-04-04Python的進(jìn)程,線程和協(xié)程實(shí)例詳解
這篇文章主要為大家詳細(xì)介紹了Python進(jìn)程,線程和協(xié)程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03