Django-MVT
(1)查看python版本號:css
python -m django --version
(2) 建立Django項目html
django-admin startproject mysite
(3)測試開發服務器是否成功python
Desktop\bncDjango\mysite>python manage.py runserver
<font style="color:red">Django 自帶一個用純 Python 寫的輕量級的 Web 服務器。爲了讓你能快速的開發出想要的東西,由於你不須要進行配置生產級別的服務器(好比 Apache)方面的工做,除非你已經準備好投入生產環境了。千萬不要 將這個服務器用於和生產環境相關的任何地方。這個服務器只是爲了開發而設計的。(咱們在 Web 框架方面是專家,在 Web 服務器方面並非。)</font>mysql
(4)建立應用模塊算法
python manage.py startapp polls
<code>sql
Application definition
INSTALLED_APPS = [ 'django.contrib.admin', # 管理員站點 'django.contrib.auth', # 認證受權系統 'django.contrib.contenttypes', # 內容類型框架 'django.contrib.sessions', # 會話框架 'django.contrib.messages', # 消息框架 'django.contrib.staticfiles', #管理靜態文件的框架 'polls', # 投票模塊 ] </code>數據庫
(5)polls模型下編輯視圖viewdjango
<code> from django.shortcuts import render瀏覽器
Create your views here.
from django.http import HttpResponse服務器
def index(request): return HttpResponse("Hello,this is my frist polls index.") </code>
(6)polls模塊下映射url
<code> from django.urls import path from . import views
urlpatterns = [ path('', views.index,name='index'), ]
</code>
(7)mysite主模塊下配置url
<code> from django.contrib import admin from django.urls import path,include # 注意導入include模塊
urlpatterns = [ path('polls/', include('polls.urls')), # 配置polls子模板url,支持正則 path('admin/', admin.site.urls), ]
</code>
(8)網頁查詢http://localhost:8000/polls/
(9)數據庫配置與遷移
<code>
DATABASES = { 'default': { # 'django.db.backends.sqlite3', # 'django.db.backends.postgresql', # 'django.db.backends.mysql', # 'django.db.backends.oracle' 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, # MySQL數據庫配置 # 'mysql': { # 'ENGINE': 'django.db.backends.mysql', # 'NAME': 'all_news', # 數據庫名 # 'USER': 'root', # 'PASSWORD': 'root', # 'HOST': '127.0.0.1', # 'PORT': '3306', # } }
</code>
python manage.py migrate
(10)編寫模型M
<code> from django.db import models
Create your models here.
class Question(models.Model): question_text = models.CharField(max_length=200) pub_data = models.DateField('date published')
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)
def __str__(self): return self.choice_text
</code>
(11)激活模型
<font style="color:red">爲模型的改變生成遷移文件</font>
python manage.py makemigrations polls
<font style="color:blue"><em>另外一種查看,選擇執行</em>接收一個遷移的名稱,而後返回對應的 SQL</font>
python manage.py sqlmigrate polls 0001
<font style="color:red">應用數據庫遷移</font>
python manage.py migrate
(12)全自動後臺管理頁面
12.1 建立一個能登陸管理頁面的用戶,均爲admin
python manage.py createsuperuser
12.2 啓動開發服務器:
python manage.py runserver
http://127.0.0.1:8000/admin/login/?next=/admin/
12.3 進入站點
12.4 管理頁面中加入配置應用
<code> from django.contrib import admin
Register your models here.
from .models import Question,Choice
admin.site.register(Question) admin.site.register(Choice) </code>
(13)編寫更多視圖
13.1 polls下的views <code> from django.shortcuts import render from django.http import HttpResponse
問題索引頁
def index(request): return HttpResponse("Hello,this is my frist polls index.")
問題詳情頁
def detail(request,question_id): return HttpResponse("You're looking at question %s." % question_id)
問題結果頁
def results(request,question_id): return HttpResponse("You're looking at the results of question %s." % question_id)
投票處理器
def vote(request,question_id): return HttpResponse("You're voting on question %s." % question_id)
</code>
13.2 polls下的urls<font style="color:red">記得添加命名空間</font>
<code> from django.urls import path from . import views app_name = 'polls' #添加命名空間 urlpatterns = [ # ex: /polls/ path('', views.index,name='index'), # ex: /polls/5/ path('<int:question_id>/', views.detail, name='detail'), # ex: /polls/5/results/ path('<int:question_id>/results/', views.results, name='results'), # ex: /polls/5/vote/ path('<int:question_id>/vote/', views.vote, name='vote'), ] </code>
13.3 查詢數據庫信息並頁面顯示
<code> # 問題索引頁 def index(request): latest_question_list = Question.objects.order_by('pub_data')[:3] output = '<br/>'.join([q.question_text for q in latest_question_list]) HttpResponse(template.render(context,request)) return HttpResponse(output) </code>
(14)編寫模板T
14.1 在mysite下建立templates,並建立polls文件夾下建立index.html
<code> <!DOCTYPE html> <html> <head> <title>投票頁面</title> </head> <body> {% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %} </body> </html> </code>
<font style="color:red">能夠修改成(經常使用)</font>.想改爲 polls/specifics/12/
,你不用在模板裏修改任何東西(包括其它模板),只要在 polls/urls.py
裏稍微修改一下就行: <code> {% for question in latest_question_list %}
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li> {% endfor %} </code>
14.2 在mysite的settings修改DIRS
<code> TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'template/')], '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', ], }, }, ] </code>
14.3 polls/views.py 修改
<code> from django.shortcuts import render from django.http import HttpResponse from django.template import loader
from polls.models import Question
問題索引頁
def index(request): latest_question_list = Question.objects.order_by('pub_data')[:5] # output = '<br/>'.join([q.question_text for q in latest_question_list]) template = loader.get_template('polls/index.html') context = { 'latest_question_list': latest_question_list, } return HttpResponse(template.render(context,request))
</code>
<font style="color:red">進一步能夠修改成(經常使用):</font>
<code> # 問題索引頁 def index(request): latest_question_list = Question.objects.order_by('pub_data')[:5] context = {'latest_question_list': latest_question_list} return render(request,'polls/index.html',context) </code>
14.4 在瀏覽器訪問 "/polls/" 查看:
(15)查看詳細頁面
15.1 polls下views.py
<code> from django.http import Http404
問題詳情頁
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,'question_id':question_id}) </code>
<font style='color:red'>優化後的算法(經常使用)</font>
<code> from django.shortcuts import render,get_object_or_404
問題詳情頁
def detail(request,question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question,'question_id':question_id}) </code>
15.2 template下detail.html
<code> <!DOCTYPE html> <html> <head> <title>詳細問題頁面</title> </head> <body> <h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul>
</body> </html> </code>
15.3 運行結果
(16)polls/detail.html詳細頁面添加一個表單form
<code> <body> <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> </body> </code>
-
每一個單選按鈕的
value
屬性是對應的各個 Choice 的 ID。每一個單選按鈕的name
是"choice"
。這意味着,當有人選擇一個單選按鈕並提交表單提交時,它將發送一個 POST 數據choice=#
,其中# 爲選擇的 Choice 的 ID。這是 HTML 表單的基本概念。 -
咱們設置表單的
action
爲{% url 'polls:vote' question.id %}
,並設置method="post"
。使用 method="post"是很是重要的,由於這個提交表單的行爲會改變服務器端的數據。當你須要建立一個改變服務器端數據的表單時,請使用``method="post"` 。這不是 Django 的特定技巧;這是優秀的網站開發技巧。 -
forloop.counter
指示for
標籤已經循環多少次。 -
因爲咱們建立一個 POST 表單(它具備修改數據的做用),因此咱們須要當心跨站點請求僞造。 謝天謝地,你沒必要太過擔憂,由於 Django 已經擁有一個用來防護它的很是容易使用的系統。 簡而言之,全部針對內部 URL 的 POST 表單都應該使用
{% csrf_token %}
模板標籤。
(17) polls/views.py
視圖編輯
<code> # 投票處理器 def vote(request,question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.",}) else: selected_choice.votes +=1 selected_choice.save() return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) </code>
request.POST
是一個類字典對象,讓你能夠經過關鍵字的名字獲取提交的數據。 這個例子中,request.POST['choice']
以字符串形式返回選擇的 Choice 的 ID。request.POST
的值永遠是字符串。- 若是在
request.POST['choice']
數據中沒有提供choice
, POST 將引起一個KeyError
。上面的代碼檢查KeyError
,若是沒有給出choice
將從新顯示 Question 表單和一個錯誤信息。 - 在增長 Choice 的得票數以後,代碼返回一個
HttpResponseRedirect
而不是經常使用的HttpResponse
、HttpResponseRedirect
只接收一個參數:用戶將要被<font style="color:red">重定向</font>的 URL。構造函數中使用reverse()
函數。這個函數避免了咱們在視圖函數中硬編碼 URL。重定向的 URL 將調用'results'
視圖來顯示最終的頁面。
(18) 重定向results.html
<code> 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}) </code>
(19)通用視圖,代碼重構
19.1 detail()
視圖幾乎如出一轍。惟一的不一樣是模板的名字。
<code> # 問題索引頁 def index(request): latest_question_list = Question.objects.order_by('pub_data')[:5] return render(request,'polls/index.html',{'latest_question_list': latest_question_list})
問題詳情頁
def detail(request,question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question,'question_id':question_id})
問題結果頁
def results(request,question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question})
</code>
19.2 建立一個 polls/results.html
模板
<code> <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> </code>
19.3 通用視圖系統
通用視圖將常見的模式抽象化,可使你在編寫應用時甚至不須要編寫Python代碼。將咱們的投票應用轉換成使用通用視圖系統,這樣咱們能夠刪除許多咱們的代碼。咱們僅僅須要作如下幾步來完成轉換,
- 轉換 URLconf。
- 刪除一些舊的、再也不須要的視圖。
- 基於 Django 的通用視圖引入新的視圖
1 改良URLconf
打開 polls/urls.py
這個 URLconf 並將它修改爲:路徑字符串中匹配模式的名稱已經由 <question_id>
改成 <pk>
。
<code> from django.urls import path from . import views
app_name = 'polls' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('int:pk/', views.DetailView.as_view(), name='detail'), path('int:pk/results/', views.ResultsView.as_view(), name='results'), path('int:question_id/vote/', views.vote, name='vote'), ] </code>
2 改良視圖
刪除舊的 index
, detail
, 和 results
視圖,並用 Django 的通用視圖代替。打開 polls/views.py
文件,並將它修改爲:
<code> from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic
from .models import Choice, Question
class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_question_list'
def get_queryset(self): return Question.objects.order_by('pub_date')[:5]
class DetailView(generic.DetailView): model = Question template_name = 'polls/detail.html'
class ResultsView(generic.DetailView): model = Question template_name = 'polls/results.html'
def vote(request,question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) else: selected_choice.votes +=1 selected_choice.save() return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) </code>
- 每一個通用視圖須要知道它將做用於哪一個模型。 這由
model
屬性提供。 DetailView
指望從 URL 中捕獲名爲"pk"
的主鍵值,因此咱們爲通用視圖把question_id
改爲pk
。
19.4 運行程序
主頁面
子頁面
詳情頁面
(20)自動化測試
20.1 測試的策略
- 測試驅動:寫代碼以前先寫測試。「測試驅動」的開發方法只是將問題的描述抽象爲了 Python 的測試樣例。
- 更廣泛的狀況是,一個剛接觸自動化測試的新手更傾向於先寫代碼,而後再寫測試。
- 若是你才寫了幾千行 Python 代碼,選擇從哪裏開始寫測試確實不怎麼簡單。若是是這種狀況,那麼在你下次修改代碼(好比加新功能,或者修復 Bug)以前寫個測試是比較合理且有效的。
20.2 第一個測試
需求:咱們的要求是若是 Question 是在一天以內發佈,was_published_recently()方法將會返回
True,然而如今這個方法在
Question的
pub_date` 字段比當前時間還晚時也會返回 True
編寫測試代碼:
<code> from django.test import TestCase
Create your tests here.
from django.utils import timezone from .models import Question
class QuestionModelTests(TestCase): def test_was_published_recently_with_future_question(self): """ was_published_recently() returns False for questions whose pub_date is in the future. """ time = timezone.now() + datetime.timedelta(days=30) future_question = Question(pub_date=time) self.assertIs(future_question.was_published_recently(), False) </code>
運行代碼:$ python manage.py test polls
測試結果:
python manage.py test polls
將會尋找polls
應用裏的測試代碼- 它找到了
django.test.TestCase
的一個子類 - 它建立一個特殊的數據庫供測試使用
- 它在類中尋找測試方法——以
test
開頭的方法。 - 在
test_was_published_recently_with_future_question
方法中,它建立了一個pub_date
值爲 30 天后的Question
實例。 - 接着使用
assertls()
方法,發現was_published_recently()
返回了True
,而咱們指望它返回False
。
<font style="color:red">測試系統通知咱們哪些測試樣例失敗了,和形成測試失敗的代碼所在的行號。</font>
(21)靜態文件(圖片/腳本/樣式)
對於小項目來講,靜態文件隨便放在哪,只要服務程序可以找到它們就行。然而在大項目中,處理不一樣應用所須要的靜態文件的工做就顯得有點麻煩了。這就是 django.contrib.staticfiles
存在的意義
建立的 static
文件夾中建立 polls
的文件夾,再在 polls
文件夾中建立一個名爲 style.css
的文件。樣式表路徑應是 polls/static/polls/style.css
。由於 AppDirectoriesFinder
的存在,你能夠在 Django 中簡單地使用以 polls/style.css
的形式引用此文件,相似你引用模板路徑的方式。
li a { color: green; }
polls的index.html引用
<code> <head> <title>投票頁面</title> {% load static %} <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}"> </head> </code>
添加圖片
咱們會建立一個用於存在圖像的目錄。在 polls/static/polls
目錄下建立一個名爲 images
的子目錄。在這個目錄中,放一張名爲 background.gif
的圖片。換言之,在目錄 polls/static/polls/images/background.jpg
中放一張圖片。
<code> body { background: white url("images/background.gif") no-repeat; } </code>
更多關於設置和框架的資料,參考 靜態文件解惑 和 靜態文件指南。部署靜態文件 介紹瞭如何在真實服務器上使用靜態文件。
(22) 編寫第一個django應用
22.1 polls/admin定義後臺表單,列表爲字段顯示順序
<code> from django.contrib import admin
Register your models here.
from .models import Question,Choice
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
# ('問題內容', {'fields': ['question_text']}), # ('發佈時間', {'fields': ['pub_data']}), # ] # fields = ['pub_data', 'question_text'] list_display = ('question_text', 'pub_data')
admin.site.register(Question, QuestionAdmin)
class ChoiceAdmin(admin.ModelAdmin): # fields = ['question','choice_text', 'votes'] list_display = ('question','choice_text', 'votes')
admin.site.register(Choice, ChoiceAdmin)
</code>
22.2 字段過濾器
<code> class QuestionAdmin(admin.ModelAdmin): list_display = ('question_text', 'pub_data') list_filter = ['pub_data'] # 過濾器 admin.site.register(Question, QuestionAdmin) </code>
22.3 自定義後臺界面與風格
-
在
templates
目錄內建立名爲admin
的目錄,隨後,將存放 Django 默認模板的目錄(django/contrib/admin/templates
)內的模板文件admin/base_site.html
複製到這個目錄內。Django 的源文件在哪裏?$ python -c "import django; print(django.path)" -
接着,用你站點的名字替換文件內的 [
](https://docs.djangoproject.com/zh-hans/2.2/intro/tutorial07/#id1){{ site_header|default:_('Django administration') }}
(包含大括號)。完成後,你應該看到以下代碼:
<code> {% block branding %} <h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1> {% endblock %} </code>