第二站练习答案
python
# tickets/tests.py
from django.test import SimpleTestCase
from django.urls import reverse
class TicketPageTests(SimpleTestCase):
def test_detail(self):
response = self.client.get(reverse("tickets:detail", args=[1]))
self.assertContains(response, "Printer offline")
def test_missing_detail(self):
response = self.client.get(reverse("tickets:detail", args=[999]))
self.assertEqual(response.status_code, 404)html
<!-- tickets/templates/tickets/base.html -->
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>{% block title %}Ticket desk{% endblock %}</title></head>
<body><nav><a href="{% url 'tickets:list' %}">Tickets</a></nav>{% block content %}{% endblock %}</body>
</html>运行:
bash
python manage.py test tickets.tests.TicketPageTests专题二核对
- 在
get_queryset()中只接受{"open", "claimed", "closed"},非法状态返回 400 或明确忽略,并用测试锁定选择。 /static/可由 CDN 或 Web 服务器公开;私有附件先做对象授权,再由受控响应或内部重定向交付。- 分别断言
?page=2的对象集合与过界页行为;若采用Paginator.get_page(),过界会落到最后一页,不是 404。
深水边界
SimpleTestCase 不允许数据库查询,适合当前内存数据阶段。第三站引入模型后应切换 TestCase;测试类型本身就是依赖边界。
开放练习(不提供答案)
为 /tickets/abc/、/tickets/0/ 和不存在的整数分别记录 URL resolver 与 view 产生的响应差异。
加码题解:URL、模板与类视图边界
python
from django.shortcuts import get_object_or_404
from django.views.generic import DetailView
from .models import Ticket
class TicketDetailView(DetailView):
model = Ticket
slug_url_kwarg = 'ticket_id'
def get_queryset(self):
return Ticket.objects.select_related('owner').filter(owner=self.request.user)关键推理
把权限过滤放进 queryset,模板只接收已经确定可见的对象。
可替代方案
需要返回统一 API 错误时,可使用显式 view/service,并保留相同的 queryset 合同。
典型错误
只在模板隐藏按钮,或让不存在对象抛 500;URL converter 也不能代替对象权限。
复盘时请把状态码/事务/权限/资源清理中的至少一项写成自动化测试,而不是只截图成功页面。