测试驱动开发(Django)13

第13章 数据库层验证

13.1 模型层验证

服务端的验证分为:模型层和表单层。

13.1.1 self.assertRaises上下文管理器

lists/tests/test_models.py

   def test_cannot_save_empty_list_items(self):
        list_ = List.objects.create()
        item = Item(list= list_,text='')
        with self.assertRaises(ValidationError):
            item.save()

python manage.py test lists

AssertionError: ValidationError not raised

13.1.2 Django怪异的表现:保存时不验证数据

TextField默认设置是blank=False,文本字段应该拒绝空值。SQLite不支持字段的强制空值约束,所以save()方法直接通过了验证。

Django提供了一个用于运行全部验证的方法:full_clean.

with self.assertRaises(ValidationError):
    item.save()
    item.full_clean()

通过了测试

13.2 在视图中年显示模型验证错误

lists/templates/base.html

<form method="POST" action="{% block form_action %}{% endblock %}">
                    {% csrf_token %}
                    <input name="item_text" id="id_new_item" placeholder="Enter a to-do item"
                           class="form-control input-lg">
                    {% if error %}
                        <div class="form-group has-error">
                            <span class="help-block">{{ error }}</span>
                        </div>
                    {% endif %}
                </form>

lists/tests/test_views.py

 

class NewListTest(TestCase):
     def test_validation_errors_are_sesnt_back_to_home_page_template(self):
          response = self.client.post('/lists/new',data={'item_text':''})
          self.assertEqual(response.status_code,200)
          self.assertTemplateUsed(response,'home.html')
          expected_error = "You can't have an empty list item"
          self.assertContains(response,expected_error)

AssertionError: 302 != 200

lists/views.py

def new_list(request):
    list_ = List.objects.create()
    item = Item.objects.create(text=request.POST['item_text'], list=list_)
    item.full_clean()
    return redirect(f'/lists/{list_.id}/')

django.core.exceptions.ValidationError: {'text': ['This field cannot be blank.']}

 

继续修改视图:

from django.core.exceptions import ValidationError
def new_list(request):
    list_ = List.objects.create()
    item = Item.objects.create(text=request.POST['item_text'], list=list_)
    try:
        item.full_clean()
    except ValidationError:
        pass
    return redirect(f'/lists/{list_.id}/')

AssertionError: 302 != 200

 

except ValidationError:
    return render(request,'home.html')

AssertionError: False is not true : Couldn't find 'You can't have an empty list item' in response

 

传入变量

except ValidationError:
    error = "You can't have an empty list item"
    return render(request, 'home.html', {'error': error})

AssertionError: False is not true : Couldn't find 'You can't have an empty list item' in response

没有效果继续:

lists/tests/test_views.py

expected_error = "You can't have an empty list item"
print(response.content.decode())

 <span class="help-block">You can&#39;t have an empty list item</span>

原来是转义了‘

lists/tests/test_views.py

from django.utils.html import escape
self.assertTemplateUsed(response,'home.html')
expected_error = escape("You can't have an empty list item")
self.assertContains(response,expected_error)

之后通过单元测试

确保无效的输入值不会存入数据库

lists/tests/test_views.py 增加一个测试

def test_invalid_list_items_arent_saved(self):
    self.client.post('/lists/new',data={'item_text':''})
    self.assertEqual(List.objects.count(),0)
    self.assertEqual(Item.objects.count(),0)

AssertionError: 1 != 0

lists/views.py 

try:
    item.full_clean()
    item.save()
except ValidationError:
    list_.delete()
    error = "You can't have an empty list item"
    return render(request, 'home.html', {'error': error})
return redirect(f'/lists/{list_.id}/')

 

通过单元测试

提交:git commit -am 'Adjust new list view to do model validation'

 

13.3 Django模式:在渲染表单的视图中处理POST请求

现在的情况是显示清单用一个视图函数和URL,处理清单中的待办事项用的是另外一个URL。合并为一。

lists/templates/list.html

{%  block form_action %}/lists/{{ list.id }}/{% endblock %}

python manage.py test functional_tests

AssertionError: '2: Use peacock feathers to make a fly' not found in ['1: Buy peacock feathers']

view_list视图还不知道如何处理请求

 

13.3.1 重构:把new_item实现的功能移到view_list中

lists/tests/test_views.py

删掉了NewItemTest类,修改两个方法:


    def test_can_save_a_POST_request_to_an_existing_list(self):
        other_list =List.objects.create()
        correct_list =List.objects.create()

        self.client.post(
            f'/lists/{correct_list.id}/',
            data={'item_text':'A new item for an existing list'}
        )
        self.assertEqual(Item.objects.create(),1)
        new_item = Item.objects.first()
        self.assertEqual(new_item.text,'A new item for an existing list')
        self.assertEqual(new_item.list,correct_list)

    def test_POST_redirects_to_list_view(self):
        other_list = List.objects.create()
        correct_list = List.objects.create()

        response = self.client.post(
            f'/lists/{correct_list.id}/',
            data={'item_text':'A new item for an existing list'}
        )
        self.assertRedirects(response,f'/lists/{correct_list.id}/')

 

 

python manage.py test lists

AssertionError: 200 != 302 : Response didn't redirect as expected: Response code was 200 (expected 302)

 

修改视图,删掉add_item

def view_list(request, list_id):
    list_ = List.objects.get(id=list_id)
    if request.method == 'POST':
        Item.objects.create(text=request.POST['item_text'], list=list_)
        return redirect(f'/lists/{list_.id}/')
    return render(request, 'list.html', {'list': list_})

再测:

AttributeError: module 'lists.views' has no attribute 'add_item'

修改lists/urls.py

from django.conf.urls import url
from lists import views

urlpatterns = [
    url(r'^new$', views.new_list, name='new_list'),
    url(r'^(\d+)/$', views.view_list, name='view_list'),
]

python manage.py test

Ran 16 tests in 42.788s

FAILED (errors=1)

提交:git commit -am 'Refactor list view to handlenew item POSTs'

13.3.2 在view_list视图执行模型验证

lists/tests/test_views.py新增一个测试


    def test_validation_errors_end_up_on_lists_page(self):
        list_ = List.objects.create()
        response = self.client.post(
            f'/lists/{list_.id}/',
            data={'item_text': ''}
        )
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'list.html')
        expected_error = escape("You can't have an empty list item")
        self.assertContains(response, expected_error)

  self.assertEqual(response.status_code, 200)

AssertionError: 302 != 200

视图中执行验证



def view_list(request, list_id):
    list_ = List.objects.get(id=list_id)
    error = None

    if request.method == 'POST':
        try:
            item = Item(text=request.POST['item_text'], list=list_)
            item.full_clean()
            item.save()
            return redirect(f'/lists/{list_.id}/')
        except ValidationError:
            error = "You can't have an empty list item"

    return render(request, 'list.html', {'list': list_, 'error': error})

通过所有测试

git commit -am 'enforce model vlidation in list view'

13.4 重构:去除硬编码的URL

    url(r'^new$', views.new_list, name='new_list'),
    url(r'^(\d+)/$', views.view_list, name='view_list'),

根据url修改home.html
{% extends 'base.html' %}

{% block header_text %}Start a new To-Do list{% endblock %}

{%  block form_action %}{% url 'new_list' %}{% endblock %}

测试:python manage.py test lists

list.html

{%  block form_action %}{% url 'view_list' list.id %}{% endblock %}

测试通过

提交:Git commit -am 'Rafactor jhard-coded URLs out of templates'

13.4.2 重定向时使用get_absolute_url

def new_list(request):
     [...]
    return redirect('view_list',list_.id)

lists/tests/test_models.py增加

    
def test_get_absolute_url(self):
    list_ = List.objects.create()
    self.assertEqual(list_.get_absolute_url(),f'/lists/{list_.id}/')

 

from django.core.urlresolvers import reverse

lists/models.py

class List(models.Model):
    def get_absolute_url(self):
        return reverse('view_list', args=[self.id])

视图:

def new_list(request):
   return redirect(list_)

测试:

Ran 18 tests in 50.939s

OK

 

修改另一个:

def view_list(request, list_id):
       return redirect(list_)

 

Ran 18 tests in 39.634s

OK

提交:Git commit -am 'Use get_absolute_url on List model to DRY urls in views '

   

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值