接上一篇Django入門-從0開始編寫一個投票網(wǎng)站(二)
開始筆記的part5-6疏虫。
part 5
- 第一個自動化測試
我們之前加的Question.was_published_recently()
有一個bug:如果日期是在未來,返回的也是True
。要驗(yàn)證這個导俘,可以通過shell來創(chuàng)建一個quesiton榄审,日期是未來的某一天,然后驗(yàn)證:
噼里啪啦一頓下來返回>>> import datetime >>> from django.utils import timezone >>> from polls.models import Question >>> future_question = Question(pub_date=timezone.now() + date time.timedelta(days=10)) >>> future_question.was_published_recently()
True
贩虾,顯然有問題催烘。
- 創(chuàng)建一個test來暴露bug,test的方法寫在
tests.py
里缎罢。
我們這里創(chuàng)建了一個import datetime from django.utils import timezone from django.test import TestCase from .models import Question class QuestionMethodTests(TestCase): def test_was_published_recently_with_future_question(self): time = timezone.now()+datetime.timedelta(days=30) future_question = Question(pub_date=time) self.assertIs(future_Question.was_published_recently(), False)
django.test.TestCase
的子類颗圣,定義了方法(方法一定要以test
開頭)。這個方法里創(chuàng)建了一個未來日期的Question屁使,我們通過斷言來判斷它的was_published_recently()
是否是期望的False
在岂。
- 跑測試。在終端運(yùn)行:
然后看到python manage.py test polls
python manage.py test polls
去polls應(yīng)用找tests
如果發(fā)現(xiàn)了django.test.TestCase
的子類
它會為測試創(chuàng)建一個特殊的數(shù)據(jù)庫
查找test開頭的 測試方法
在這里就是找到test_was_published_recently_with_future_question
蛮寂,創(chuàng)建一個日期在一個月以后的Question
調(diào)用斷言assertIs()
方法蔽午,發(fā)現(xiàn)返回Ture,跟希望的False不一致酬蹋,然后給出上面的提示及老。
- 發(fā)現(xiàn)了bug抽莱,就要修復(fù)。在
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
更多綜合測試
def test_was_published_recently_with_old_question(self): time = timezone.now() - datetime.timedelta(days=30) old_question = Question(pub_date=time) self.assertIs(old_question.was_published_recently(), False) def test_was_published_recently_with_recent_question(self): time = timezone.now() - datetime.timedelta(hours=1) recent_question = Question(pub_date=time) self.assertIs(recent_question.was_published_recently(), True)
- 測試views食铐。剛才我們在代碼本身找bug,接下來可以在與用戶交互的界面上來找找問題僧鲁。
django提供了一個測試的客戶端模擬器來測試view虐呻,我們可以在tests.py和shell使用。
我們從shell用:>>> from django.test.utils import setup_test_environment >>> setup_test_environment()
setup_test_environment()
方法安裝了一個可以模版渲染的東西寞秃。這個方法不會建立測試的數(shù)據(jù)庫斟叼,所有的都會運(yùn)行在當(dāng)前存在的數(shù)據(jù)庫上并且輸出的結(jié)果可能跟你已經(jīng)創(chuàng)建的會有一點(diǎn)點(diǎn)的區(qū)別,比如如果你在settings.py里設(shè)置的TIME_ZONE不對春寿,那么你的結(jié)果就會不大一樣朗涩,為了避免這個,檢查一下TIME_ZONE的設(shè)置绑改。
使用client類>>> from django.test import Client >>> client = Client() >>> response = client.get("/") # Not Found: / >>> response.status_code # 404 >>> from django.urls import reverse >>> response = client.get(reverse('polls:index')) >>> response.status_code # 200 >>> response.content >>> response.context['latest_question_list']
- 提升我們的view谢床,在
polls/views.py
的IndexView類里,我們修改一下get_queryset()
厘线,使它在查詢的時候可以把日期和當(dāng)前日期比對一下:
這里的from django.utils import timezone class IndexView(generic.ListView): ... def get_queryset(self): return Question.objects.filter(pub_date__lte=timezone.now()).order('-pub_date')[:5]
filter
返回了一個包含日期早于或等于Question的查詢集萤悴,__lte
(注意是2條_
)是django數(shù)據(jù)模型日期屬性的一個固定寫法,用在filter
方法里皆的,屬性__lte=日期
表示日期早于或等于指定的那個日期覆履。
- 測試我們的新的view,在tests.py里:
下面來仔細(xì)看:from django.urls import reverse def create_question(question_text, days): time = timezone.now() + date time.timedelta(days=days) return Question.objects.create(question_test=question_text, pub_date=time) class QuestionViewTests(TestCase): def test_index_view_with_no_question(self): response = self.client.get(reverse('polls:index')) self.assertEqual(response.status_code, 200) self.assertContains(response, "No polls are available") self.assertQuesrysetEqual(response.context['latest_question_list'], []) def test_index_view_with_a_past_question(self): create_question(question_text="Past question", days=-30) response = self.client.get(reverse('polls:index')) self.assertQuerysetEqual(response.context['latest_question_list'], ['<Question: Past question.>']) def test_index_view_with_a_future_question(self): create_question(question_text="Future question", days=30) response = self.client.get('polls:index') self.assertQuerysetEqual(response.context['latest_question_list'], []) def test_index_view_with_future_question_and_past_question(self): create_question(question_text="Past question.", days=-30) create_question(question_text="Future question.", days=30) response = self.client.get(reverse('polls:index')) self.assertQuerysetEqual(response.context['latest_question_list'], ['<Question: question.>']) def test_index_view_with_two_past_questions(self): create_question(question_text="Past question 1.", days=-30) create_question(question_text="Past question 2.", days=-5) response = self.client.get(reverse('polls:index')) self.assertQuerysetEqual(response.context['latest_question_list'],['<Question: Past question 1.>', '<Question: Past question 2.>'])
create_question()
是快捷創(chuàng)建question的方法
test_index_view_with_no_question
不創(chuàng)建任何的question對象费薄,而是驗(yàn)證latest_question_list
是否是空的硝全。
執(zhí)行每一個測試方法的時候測試數(shù)據(jù)庫都會重置。
- 測試DetailView
目前日期是未來的question不會顯示到index.html里楞抡,但是如果用戶猜出來了正確的URL伟众,那還是能獲得這些未來日期的question,所以要在DetailView里加一個類似的約束:
在test.py里class DetailView(generic.DetailView): ... def get_queryset(self): return Question.objects.filter(pub_date__lte=timezone.now())
以上是測試的部分召廷。class QuestionIndexDetailTests(TestCase): def test_detail_view_with_a_future_question(self): future_question = create_question(question_text='Future question.', days=5) url = reverse('polls:detail', args=(future_question.id,)) response=self.client.get(url) self.assertEqual(response.status_code, 404) def test_detail_view_with_a_past_question(self): past_question=create_question(question_text='Past question.', days=-5) url=reverse('polls:detail', args=(past_question.id,)) response=self.client.get(url) self.assertContains(response, past_question.question_text)
part 6
- 定制app的外觀凳厢。
web app除了HTML以外,還需要諸如圖片竞慢,JS文件或者CSS先紫,這些一般都是靜態(tài)文件,我們可以用django.contrib.staticfiles
來應(yīng)對這些:對于小的app可以放在指定的地方而不需要用這個模塊筹煮,對于大的應(yīng)用用這個統(tǒng)一管理比較好遮精。
首先在polls文件夾下創(chuàng)建一個static文件夾,類似templates。
django的STATICFILES_FINDERS
設(shè)置包含了一系列的finder本冲,這些finder定義了查找靜態(tài)文件的方式准脂。其中有一個叫做AppDirectoriesFinder
的會在每一個INSTALLED_APPS
里查找一個叫做static
的文件夾。
在這個static文件夾里創(chuàng)建一個polls文件夾檬洞,在這個polls文件夾里再創(chuàng)建一個style.css文件狸膏,所以這個css文件的地址是polls/static/polls/style.css
。
在這個style.css文件里:
接下來在li a { color: green; }
polls/templates/polls/index.html
里的最頂部添加:{% load static %} <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}"></link>
{% static %}
生成了靜態(tài)文件的絕對路徑添怔。重新加載http://127.0.0.1:8000/polls/你可以看到問題的鏈接變成原諒色了湾戳。
- 添加一張背景圖。
在polls/static/polls/
下創(chuàng)建一個用來放置圖片的images
文件夾澎灸。在文件夾里放一張background.gif
圖片,所以這張圖片的路徑是polls/static/polls/images/background.gif
遮晚。
在style.css里
重新加載就能看到背景圖的效果body { background: white url("images/background.gif") no-repeat right bottom; }
part5-6就到這里性昭。
戳這里查看下面的教程:Django入門-從0開始編寫一個投票網(wǎng)站(四)(完結(jié))