Django入门与实践-第17章:保护视图

5-1.jpg

前言

欢迎来到本系列教程的第5部分,在这节课,我们将学习如何保护视图防止未登录的用户访问,以及在视图和表单中访问已经登录的用户,我们还将实现主题列表和回复列表视图,最后,将探索 Django ORM 的一些特性和数据迁移的简单介绍。

保护视图

我们必须保护视图防止那些未认证(登录)的用户访问,下面是发起一个新话题的页面

5-2.png

在上图中,用户还没有登录,尽管他们可以看到页面和表单。Django 有一个内置的 视图装饰器 来避免它被未登录的用户访问:

boards/views.py完整代码

  1. from django.contrib.auth.decorators import login_required
  2. @login_required
  3. def new_topic(request, pk):
  4. # ...

现在如果用户没有登录,将被重定向到登录页面:

5-3.png

注意查询字符串 ?next=/boards/1/new/ ,我们可以改进登录模板以便利用 next 变量来改进我们的用户体验,(译注:实际上这步操作不加也没问题)

配置登录后的重定向地址

templates/login.html (查看完整内容)

  1. <form method="post" novalidate>
  2. {% csrf_token %}
  3. <input type="hidden" name="next" value="{{ next }}">
  4. {% include 'includes/form.html' %}
  5. <button type="submit" class="btn btn-primary btn-block">Log in</button>
  6. </form>

现在尝试登录,登录成功后,应用程序会跳转到原来所在的位置。

5-4.png

next 参数是内置功能的一部分(译注:详情请参考Django官方文档

测试

现在添加一个测试用例确保主题发布视图被 @login_required装饰器保护了,不过,我们还是先来重构一下 boards/tests/test_views.py 文件。

test_views.py拆分成3个文件:

  • test_view_home.py 包含 HomeTests 类 (完整代码
  • test_view_board_topics.py 包含 BoardTopicsTests 类(完整代码
  • test_view_new_topic.py 包含 NewTopicTests 类(完整代码
  1. myproject/
  2. |-- myproject/
  3. | |-- accounts/
  4. | |-- boards/
  5. | | |-- migrations/
  6. | | |-- templatetags/
  7. | | |-- tests/
  8. | | | |-- __init__.py
  9. | | | |-- test_templatetags.py
  10. | | | |-- test_view_home.py <-- here
  11. | | | |-- test_view_board_topics.py <-- here
  12. | | | +-- test_view_new_topic.py <-- and here
  13. | | |-- __init__.py
  14. | | |-- admin.py
  15. | | |-- apps.py
  16. | | |-- models.py
  17. | | +-- views.py
  18. | |-- myproject/
  19. | |-- static/
  20. | |-- templates/
  21. | |-- db.sqlite3
  22. | +-- manage.py
  23. +-- venv/

重新运行测试,确保一切正常。

现在在 test_view_new_topic.py 中添加一个新测试用例,用来检查试图是否被@login_required保护:

boards/tests/test_view_new_topic.py完成代码

  1. from django.test import TestCase
  2. from django.urls import reverse
  3. from ..models import Board
  4. class LoginRequiredNewTopicTests(TestCase):
  5. def setUp(self):
  6. Board.objects.create(name='Django', description='Django board.')
  7. self.url = reverse('new_topic', kwargs={'pk': 1})
  8. self.response = self.client.get(self.url)
  9. def test_redirection(self):
  10. login_url = reverse('login')
  11. self.assertRedirects(self.response, '{login_url}?next={url}'.format(login_url=login_url, url=self.url))

在测试用例中,我们尝试在没有登录的情况下发送请求给 new topic 视图,期待的结果是请求重定向到登录页面。