接上一篇Django入门-从0开始编写一个投票网站(二)
开始笔记的part5-6。
part 5
- 第一个自动化测试
我们之前加的Question.was_published_recently()
有一个bug:如果日期是在未来,返回的也是True
。要验证这个,可以通过shell来创建一个quesiton,日期是未来的某一天,然后验证:
噼里啪啦一顿下来返回>>> 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
,显然有问题。
- 创建一个test来暴露bug,test的方法写在
tests.py
里。
我们这里创建了一个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
开头)。这个方法里创建了一个未来日期的Question,我们通过断言来判断它的was_published_recently()
是否是期望的False
。
- 跑测试。在终端运行:
然后看到python manage.py test polls
python manage.py test polls
去polls应用找tests
如果发现了django.test.TestCase
的子类
它会为测试创建一个特殊的数据库
查找test开头的 测试方法
在这里就是找到test_was_published_recently_with_future_question
,创建一个日期在一个月以后的Question
调用断言assertIs()
方法,发现返回Ture,跟希望的False不一致,然后给出上面的提示。
- 发现了bug,就要修复。在
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()
方法安装了一个可以模版渲染的东西。这个方法不会建立测试的数据库,所有的都会运行在当前存在的数据库上并且输出的结果可能跟你已经创建的会有一点点的区别,比如如果你在settings.py里设置的TIME_ZONE不对,那么你的结果就会不大一样,为了避免这个,检查一下TIME_ZONE的设置。
使用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()
,使它在查询的时候可以把日期和当前日期比对一下:
这里的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数据模型日期属性的一个固定写法,用在filter
方法里,属性__lte=日期
表示日期早于或等于指定的那个日期。
- 测试我们的新的view,在tests.py里:
下面来仔细看: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()
是快捷创建question的方法
test_index_view_with_no_question
不创建任何的question对象,而是验证latest_question_list
是否是空的。
执行每一个测试方法的时候测试数据库都会重置。
- 测试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,这些一般都是静态文件,我们可以用django.contrib.staticfiles
来应对这些:对于小的app可以放在指定的地方而不需要用这个模块,对于大的应用用这个统一管理比较好。
首先在polls文件夹下创建一个static文件夹,类似templates。
django的STATICFILES_FINDERS
设置包含了一系列的finder,这些finder定义了查找静态文件的方式。其中有一个叫做AppDirectoriesFinder
的会在每一个INSTALLED_APPS
里查找一个叫做static
的文件夹。
在这个static文件夹里创建一个polls文件夹,在这个polls文件夹里再创建一个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 %}
生成了静态文件的绝对路径。重新加载http://127.0.0.1:8000/polls/你可以看到问题的链接变成原谅色了。
- 添加一张背景图。
在polls/static/polls/
下创建一个用来放置图片的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开始编写一个投票网站(四)(完结)