1. Django1.9应用特性注意事项

Django1.9抛弃了很多之前版本中的函数,也引入了一部分新的特性。下面主要来谈一下博主在Django1.9.x版本下开发应用过程中遇到的问题,以及一些注意事项。

URL文件管理配置

Django1.9在URLConf的方面,引入url函数替代了之前的 patterns函数的功能,目前多数参考书籍关于urlpatterns值的获取还停留在patterns函数阶段,基本上没有对1.9版本中获取函数变为url的方法进行跟进介绍。
新的url函数在应用polls中URL配置文件如下,分别展示了url层级陪陪获取的方法。在新方法中每一个url匹配都被引入到了列表urlpatterns,替代了之前所用的元组urlpatterns。

Code.1

polls/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
# ex: /polls/
url(r'^$', views.index, name='index'),
# ex: /polls/5/
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
# ex: /polls/5/results/
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
# ex: /polls/5/vote/
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]

旧版本Django获取urlpatterns 方法代码示意:
Code.2

polls/urls.py
urlpatterns = patterns('',    
    url(r'^article$','news_index' ,name="myarticle"),
 )

新方法还提供了面向对象的方法
Code.3

urlpatterns = [
    # ex: /polls/
    url(r'^$',views.IndexView.as_view(), name= 'index'),
    # ex: /polls/5/
    url(r'^(?P<pk>[0-9]+)/$',views.DetailView.as_view(),  name= 'detail'),
    # ex: /polls/5/results/
    url(r'^(?P<pk>[0-9]+)/results/$',views.ResultsView.as_view(), name= 'results'),
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>[0-9]+)/vote/$',views.vote, name= 'vote'),
]
-- 

新方法用IndexView.as_view(),DetailView.as_view(),以及ResultsView.as_view(),通过在views.py中引入了generic (from django.views import generic ) 方法实现用对象配置url的功能替代views.py文件中函数对URL配置管理,新方法体现了Django在面向对象编程的特性。polls/views.py代码如下,其中注释部分代码为执行Code.1所用新代码为之前功能的:
Code.4

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.core.urlresolvers import reverse 
from django.views import generic 

from django.utils import timezone

from polls.models import Question, Choice

# Create your views here.

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 (not including those set to be
        published in the future).
        """
        return Question.objects.filter(
            pub_date__lte=timezone.now()
        ).order_by('-pub_date')[:5]

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())

class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'

#def index(request):
#    latest_question_list = Question.objects.order_by('-pub_date')[:5]
#    context = {
#        'latest_question_list':latest_question_list,
#    } 
#    return render(request, 'polls/index.html', context)
#
#def detail(request, question_id):
#    question =  get_object_or_404(Question, pk= question_id)
#    return render(request, 'polls/detail.html', {'question': question})
#
#
#def results(request, question_id):
#    question =  get_object_or_404(Question, pk=question_id)
#    return render(request, 'polls/results.html', {'question': question})

注意事项:

url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail')

vs

 url(r'^(?P<pk>[0-9]+)/$',views.DetailView.as_view(),  name= 'detail'),

在视图模式中question_id要变为pk,否则不仅涉及改变相关URL解析不会成功,还会影响到整个应用的url配置管理的实现。

**

单元测试

**
阿里的工程师志向在博客写有价值的单元测试中提到的错误恒定定律(程序员的错误产出比是个常数)和规模代价平方定律(定位并修复一个BUG所需的代价正比于目标代码规模的平方)充分说明了单元测试对于软件项目质量保证的重要性。当我们在学习任何一种新的开发语言时候,任何没有UT代码的demo都是不完整的。在UT支持这方面,Django显然走在了前列,通过调用核心模块的TestCase(from django.test import TestCase)功能,我们很容易对开发中的Django应用写Uint Test代码。

Demo应用polls的UT代码如下:
Code.5

import datetime
from django.utils import timezone
from django.test import TestCase
from django.core.urlresolvers import reverse 

from polls.models import Question
# Create your tests here.


class QuestionMethodTests(TestCase):
    def test_was_published_recently_with_future_question(self):
        """
        was_published_recently() should return False for questions whose
        pub_date is in the future. 
        """
        time = timezone.now() + datetime.timedelta(days=30) 
        future_question =Question(pub_date=time)
        self.assertEqual(future_question.was_published_recently(),False )

    def test_was_published_recently_with_old_question(self):
        """
        was_published_recently() should return False for questions whose
        pub_date is older than 1 day. 
        """
        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):
        """
        was_published_recently() should return True for questions whose
        pub_date is within the last day. 
        """
        time = timezone.now() - datetime.timedelta(hours=1) 
        recent_question =Question(pub_date=time)
        self.assertEqual(recent_question.was_published_recently(), True )

def create_question(question_text, days):
    """
    Creates a question with the given `question_text` and published the 
    given number of `days` offset to now (negative for questions published
    in the past, postive for questions tha 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_question(self):
        """
        If no question 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 no 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 question 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.>']
        )

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
        be 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)  

在Django框架下跑单元测试代码,所在路径为项目toger目录,polls应用(app)目录同级,上述代码单元测试运行结果如下:

[hbu@localhost toger]$ python manage.py test polls
Creating test database for alias 'default'...
..........
----------------------------------------------------------------------
Ran 10 tests in 0.043s

OK
Destroying test database for alias 'default'...

表明10个单元测试的case全部通过。
更改单元测试代码Code.5最后的一行,使 status_code=404观察不通过的情况,提示如下:

[hbu@localhost toger]$ python manage.py test polls
Creating test database for alias 'default'...
.F........
======================================================================
FAIL: test_detail_view_with_a_past_question (polls.tests.QuestionIndexDetailTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/hbu/toger/polls/tests.py", line 130, in test_detail_view_with_a_past_question
    status_code=404)
  File "/usr/lib64/python2.7/site-packages/django/test/testcases.py", line 395, in assertContains
    response, text, status_code, msg_prefix, html)
  File "/usr/lib64/python2.7/site-packages/django/test/testcases.py", line 365, in _assert_contains
    " (expected %d)" % (response.status_code, status_code))
AssertionError: Couldn't retrieve content: Response code was 200 (expected 404)

----------------------------------------------------------------------
Ran 10 tests in 0.055s

FAILED (failures=1)
Destroying test database for alias 'default'...

单元测试报错代码会提示涉及Unit Test代码的位置,以及其他的一些有提示指向功能的报错信息。根据相关信息,我们可以更好地改善发现bug,在开发阶段保证得到高质量的代码。

参考资料:
1、Django1.9.x Document(PDF)
2、写有价值的单元测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值