django tutorial 都说了什么。
新建项目及应用
安装虚拟环境和django
pip install virtualenv
新建虚拟环境并安装django
# 安装依赖python3 的虚拟环境
virtualenv djangovenv -p python3
workon djangovenv
# 安装django
pip install django
新建项目和应用
# 新建项目
django-admin startproject mysite
cd mysite
# 新建应用
python manage.py polls
路由映射
路由映射到基于函数的视图---FBV
- mysite/urls.py
from django.urls import path, include
urlpatterns = [
path('polls/', include('polls.apps.PollsConfig'), name='polls')
]
- polls/urls.py
from django.urls import path
from . import views
app_name='polls'
urlpatterns =[
# 基于函数的视图函数映射
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
视图映射到基于类的视图---CBV
from django.urls import path
from .import views
app_name='polls'
urlpatterns=[
path('', views.IndexView.as_view(), name='index'),
# 通过视图类渲染的, id直接命名pk
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
# vote没有视图模板, 所以不需要视图类
path('<int:question_id>/vote/', views.vote, name='vote'),
视图函数
基于函数的视图
- polls/views.py 三种方法渲染视图模板
from django.shortcuts import render, get_object_or_404
from django.url import request, HttpResponse
from django.template import loader
def index(request):
latest_question_list = Question.objects.order_by('-pub_time')[:5]
# 1. 直接文字显示结果
# output = ','.join([q.question_text for q in latest_question])
# return HttpResponse(output)
context = {
'latest_question_list': latest_question_list
}
## 2. 通过loader.template
template = loader.template('polls/index.html')
return HttpResponse(template.render(context, request))
## 3. 通过render函数渲染模板
return render(request, 'polls/index.html, context)
基于类的视图
Django封装了不同类型的视图类, 只需要填写对应的属性或改写响应的方法就能自动渲染响应模板
- views.py
from django.views import generic
def IndexView(generic.ListView):
model = Question
context_object_name='latest_question_list'
template_name='polls/index.html'
def get_queryset(self):
return Question.objects.filter(pub_date__lte=timezone.now()).order_by('-pub_date')[:5]
def DetailView(generic.DetailView):
model = Question
template_name='polls/detail.py'
def get_queryset(self):
# 过滤未来发布问题, 再测试中纠正的错误
return Question.objects.filter(pub_date__lte=timezone.now()
def ResultsView(generic.DetailView):
model = Question
template_name='polls/results.py'
路由映射到类函数
数据库模型
新建数据库类
- models.py
from django.db import models
from django.utils import timezone
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DatetimeField(default=timezone.now())
def __str__(self):
return self.question_text
def was_published_recently(self):
# 一天内发布的问题为近期问题, 注意未来的时间点不是近期,需要做分割
return self.pub_date-datetime.delta(days=1) <= self.pub_date <= timezone.now()
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
vote = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
- setting.py 中添加应用配置
INSTALLED_APP=[
'polls.apps.PollsConfig',
...
]
-
命令行生成数据库
# 生成迁移文件
python manage.py makemigrations polls
# 执行迁移
python manage.py migrate
# 执行指定迁移
python manage.py migrate polls 0001
交互窗口操作数据库
python manage.py shell
>>> from .models import Question, Choice
>>> from django.utils import timezone
>>> Question.objects.create(question_text='What's up?', pub_date=timezone.now())
>>> q = question.objects.get(pk=1)
>>> q.question_text
>>> q.pub_date
>>> q.id
>>> q1 = Question(question_text='What's your favorate color?', pub_date=timezone.now())
>>> q1.save()
>>> Question.objects.all()
>>> q.choice_set.all()
>>> >>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
>>> c.question
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
视图函数与模板
编写测试
- TestCase类
对数据库模型类的实例方法的测试
通过执行结果与期待的结果相同,则测试通过
import datetime
from django.test import TestCase
from django.utils import timezone
from .model import Question, Choice
class QuestionTestCase(TestCase):
"""Question 模型单元测试"""
def test_was_published_recently_with_future_question(self):
# 测试未来的任务, 是否是最近的任务, 期待结果是False
future_question = Question(pub_date=timezone.now()+datetime.timedelta(days=30))
self.assertIs(future_question.was_published_recently, False)
def test_was_published_recently_with_order_question(self):
# 测试过去的任务(一天以上), 是否是最近的, 期待结果是False
order_question = Question(pub_date=timezone.now()-datetime.timedelta(days=30))
self.assertIs(order_question.was_published_recently, False)
def test_was_published_recently_with_recently_quesiton(self):
# 测试一天内的任务是否为最近任务, 期待结果为True
recently_question = Question(pub_date=timezone.now()-datetime.timedelta(hours=23,minutes=59,seconds=59)
self.assertIs(recently_question.was_published_recently, True)
## 通过以上的三个测试通过,对应选项, 可以判定哪个区间出了问题, 来修补漏洞
对视图函数测试响应结果数据
def create_question(question_text, days):
# 与之前的创建实例不同, 这个需要写入测试临时数据库中
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, pub_date=pub_date)
class QuestionIndexViewTestCase(TestCase):
def test_no_question(self):
# 测试没有发布问题 期待 访问成功, 响应包含为 "No Polls.." queryset为[]
response = self.client.get(reverse('polls:index'))
self.assertEqual(response.status_code, 200) # 测试访问状态码, 连接成功
self.assertContains(response, 'No polls')
self.assertQuerysetEqual(response.context['latest_question_list'], [])
def test_past_question(self):
# 测试过去发布问题, 期待queryset中包含[question]
question = create_quesiton("Past question", days=-30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(response.context['latest_question_list'], [question])
def test_future_question(self):
# 测试未来时间发布问题, 期待queryset为[]
question = create_quesiton("Future question", days=30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(response.context['latest_question_list'], [])
def test_past_question_and_future_question(self):
# 测试过去发布问题, 期待queryset中包含[question]
question = create_question("Past question", days=-30)
create_question("Future question", days=30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(response.context['latest_question_list'], [question])
def test_two_past_questions(self):
# 测试过去发布问题, 期待queryset中包含[question1, question2]
question1 = create_quesiton("Past question 1.", days=-30)
question2 = create_quesiton("Past question 2.", days=-5)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(response.context['latest_question_list'], [question2, question1])
class QuestionDetailViewTestCase(TestCase):
# 测试过去发布问题, 期待结果中包含对应问题
def test_past_question(self):
question = create_question(question_text="Past question", days=-5)
response = self.client.get(reverse('polls:detail', args=(question.id,)))
self.assertContains(response, question.question_text)
# 测试未来发布问题, 期待结果访问结果未找到 404
def test_future_question(self):
question = create_question(question_text="Future question", days=5)
response = self.client.get(reverse('polls:detail', args=(question.id,)))
self.assertEqual(response.status_code, 404)