Django入门-从0开始编写一个投票网站(三)

接上一篇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
    
    然后看到
    Django入门-从0开始编写一个投票网站(三)_第1张图片
    test_polls_failed.png
    这里发生了这些:
    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
    

    重新跑,可以看到
    Django入门-从0开始编写一个投票网站(三)_第2张图片
    test_polls_success.png

    更多综合测试

    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'], [''])
    
          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'], [''])
    
          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'],['', ''])
    
    下面来仔细看:
    create_question()是快捷创建question的方法
    test_index_view_with_no_question不创建任何的question对象,而是验证latest_question_list是否是空的。
    执行每一个测试方法的时候测试数据库都会重置。

  • 测试DetailView
    目前日期是未来的question不会显示到index.html里,但是如果用户猜出来了正确的URL,那还是能获得这些未来日期的question,所以要在DetailView里加一个类似的约束:
    class DetailView(generic.DetailView):
          ...
          def get_queryset(self):
              return Question.objects.filter(pub_date__lte=timezone.now())
    
    在test.py里
    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 %}
    
    
    {% 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开始编写一个投票网站(四)(完结)

你可能感兴趣的:(Django入门-从0开始编写一个投票网站(三))