Django学习笔记:入门开发(三)

2018-02-24 13:12:58来源:https://www.biaodianfu.com/django-3.html作者:标点符人点击

分享
编写自动化测试
创建测试代码

Question在最近一天发布,Question.was_published_recently() 方法返回True(这是对的),但是如果Question的pub_date 字段是在未来,它还返回True(这肯定是不对的)。创建第一个自动化测试的代码,打开polls/tests.py文件:


import datetime
from django.utils import timezone
from django.test import TestCase
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)
运行测试代码
python manage.py test polls

执行后返回如下信息:


D:/mysite>python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_question
    self.assertIs(future_question.was_published_recently(), False)
AssertionError: True is not False
 
----------------------------------------------------------------------
Ran 1 test in 0.002s
 
FAILED (failures=1)
Destroying test database for alias 'default'...

代码执行后中间经过了如下几个过程:


程序自动寻找了test.TestCase的子类
程序自动运行了以test的方法
运行前创建了临时的数据库,运行后将临时库进行了销毁

修复错误后重新执行


def was_published_recently(self):
    now = timezone.now()
    return now - datetime.timedelta(days=1) <= self.pub_date <= now

重新运行,后获得如下结果:


D:/mysite>python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
 
OK
Destroying test database for alias 'default'...
在测试类中添加各种场景

为保证测试的完整性,需要测试不同场景下程序是否能够正确处理。


def test_was_published_recently_with_old_question(self):
    """
    发布时间大于1天的场景
    """
    time = timezone.now() - datetime.timedelta(days=30)
    old_question = Question(pub_date=time)
    self.assertEqual(old_question.was_published_recently(), False)
 
def test_was_published_recently_with_recent_question(self):
    """
    发布时间小于1天的场景
    """
    time = timezone.now() - datetime.timedelta(hours=1)
    recent_question = Question(pub_date=time)
    self.assertEqual(recent_question.was_published_recently(), True)
测试视图


Django测试客户端


Django提供了一个测试客户端来模拟用户和代码的交互。我们可以在tests.py 甚至在shell 中使用它。我们以shell为例,首先进入shell环境:


python manage.py shell

先设置测试环境:


>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()


setup_test_environment()
会安装一个模板渲染器,可以使我们来检查响应的一些额外属性比如,比如相应上下文response.context。注意,这种方法不会建立一个测试数据库,所以以下命令将运行在现有的数据库上。



下一步我们需要导入测试客户端类(在之后的tests.py 中,我们将使用django.test.TestCase
类,它具有自己的客户端,将不需要导入这个类):


>>> from django.test import Client
>>> client = Client()

这些都做完之后,我们可以让这个客户端来为我们做一些事:


>>> response = client.get('/')
Not Found: /
>>> response.status_code
404
>>> # 我们想要获取的首页是 '/polls/',可以将请求修改为client.get('/polls/')
>>> # 或者使用reverse()方法来动态生成url
>>> from django.urls import reverse
>>> response = client.get(reverse('polls:index'))
>>> response.status_code
200
>>> response.content
b'/n    <ul>/n    /n        <li><a href="/polls/1/">What's up?</a></li>/n    /n    </ul>/n'
>>> response.context['latest_question_list']
[<Question: Who is your favorite Beatle?>]
改进我们的视图

默认情况下投票的列表显示了还没有发布的投票(即pub_date的时间在未来的投票)。需要进行修复。


修改polls/views.py中的IndexView下的get_queryset方法并让它将日期与timezone.now()进行比较。


from django.utils import timezone
 
class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'
 
    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.filter(pub_date__lte=timezone.now()).order_by('-pub_date')[:5]
测试新的视图

打开polls/tests.py导入reverse:


from django.urls import reverse

创建一个创建问题的函数,同时新建一个测试类


def create_question(question_text, days):
    """
    Creates a question with the given `question_text` published the given
    number of `days` offset to now (negative for questions published
    in the past, positive for questions that have yet to be published).
    """
    time = timezone.now() + datetime.timedelta(days=days)
    return Question.objects.create(question_text=question_text,
                                   pub_date=time)
 
class QuestionViewTests(TestCase):
    def test_index_view_with_no_questions(self):
        """
        If no questions exist, an appropriate message should be displayed.
        """
        response = self.client.get(reverse('polls:index'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "No polls are available.")
        self.assertQuerysetEqual(response.context['latest_question_list'], [])
 
    def test_index_view_with_a_past_question(self):
        """
        Questions with a pub_date in the past should be displayed on the
        index page.
        """
        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):
        """
        Questions with a pub_date in the future should not be displayed on
        the index page.
        """
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse('polls:index'))
        self.assertContains(response, "No polls are available.",
                            status_code=200)
        self.assertQuerysetEqual(response.context['latest_question_list'], [])
 
    def test_index_view_with_future_question_and_past_question(self):
        """
        Even if both past and future questions exist, only past questions
        should be displayed.
        """
        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: Past question.>']
        )
 
    def test_index_view_with_two_past_questions(self):
        """
        The questions index page may display multiple questions.
        """
        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 2.>', '<Question: Past question 1.>']
        )
测试 DetailView

即使未来发布的Question不会出现在index中,如果用户知道或者猜出正确的URL依然可以访问它们。所以我们需要给DetailView添加一个这样的约束:


class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'
 
    def get_queryset(self):
        """Excludes any questions that aren't published yet."""
        return Question.objects.filter(pub_date__lte=timezone.now())

当然,我们将增加一些测试来检验pub_date 在过去的Question 可以显示出来,而pub_date在未来的不可以:


class QuestionIndexDetailTests(TestCase):
    def test_detail_view_with_a_future_question(self):
        """
        The detail view of a question with a pub_date in the future should
        return a 404 not found.
        """
        future_question = create_question(question_text='Future question.',
                                          days=5)
        response = self.client.get(reverse('polls:detail',
                                   args=(future_question.id,)))
        self.assertEqual(response.status_code, 404)
 
    def test_detail_view_with_a_past_question(self):
        """
        The detail view of a question with a pub_date in the past should
        display the question's text.
        """
        past_question = create_question(question_text='Past Question.',
                                        days=-5)
        response = self.client.get(reverse('polls:detail',
                                   args=(past_question.id,)))
        self.assertContains(response, past_question.question_text,
                            status_code=200)
自定义应用的外观

首先在你的polls中创建一个static目录。Django将在那里查找静态文件,这与Django在polls/templates/中寻找对应的模板文件的方式是一致的。~~~~



Django 的STATICFILES_FINDERS
设置包含一个查找器列表,它们知道如何从各种源找到静态文件。 其中默认的一个是AppDirectoriesFinder,像在刚刚polls中创建的那个项目查找方式一样,它会在每个INSTALLED_APPS
下查找“static”子目录,。管理站点也为它的静态文件使用相同的目录结构。


在你刚刚创建的static目录中,创建另外一个目录polls并在它下面创建一个文件style.css。换句话讲,你的样式表应该位于polls/static/polls/style.css。因为AppDirectoriesFinder 静态文件查找器的工作方式,你可以通过polls/style.css在Django中访问这个静态文件,与你如何访问模板的路径类似。


静态文件的命名空间


与模板类似,我们可以将静态文件直接放在polls/static(而不是创建另外一个polls 子目录),但实际上这是一个坏主意。Django将使用它所找到的第一个符合要求的静态文件的文件名,如果在你的不同应用中存在两个同名的静态文件,Django将无法区分它们。我们需要告诉Django该使用其中的哪一个,最简单的方法就是为它们添加命名空间。 也就是说,将这些静态文件放进以它们所在的应用的名字命名的另外一个目录下。


将下面的代码放入样式表中 (polls/static/polls/style.css):


li a {
    color: green;
}

下一步,在polls/templates/polls/index.html的顶端添加如下内容 :


{% load staticfiles %}
<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />

{% load staticfiles %} 从staticfiles模板库加载{% static %} 模板标签。{% static %}模板标签会生成静态文件的绝对URL。


这就是你在开发过程中所需要对静态文件做的所有处理。 重新加载 http://localhost:8000/polls/,你应该会看到Question的超链接变成了绿色,这意味着你的样式表被成功导入。


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台