一、首先需要创建应用的环境
将在manage.py 同级目录下创建投票应用。
这样它就可以作为顶级模块导入,而不是 mysite 的子模块。
首先确定自己现在处于 manage.py 所在的目录下
确定路径:
初始化应用:
python manage.py startapp polls
二、编写第一个视图
polls/views.py
from django.shortcuts import render from django.http import HttpResponse # Create your views here. def index(request): return HttpResponse("Hello, world. You're at the polls index.")
想要查看上面的视图,就需要将一个 URL 映射到它,下面将一个url映射到应用内
polls/urls.py
from django.urls import path from . import views urlpatterns = [ path("", views.index, name="index"), ]
想要查看上面的视图,就需要将一个 URL 映射到它,下面将应用增加到项目的url配置中
web/urls.py
from django.contrib import admin from django.urls import include,path from web import * urlpatterns = [ path("polls/", include('polls.urls')), path('admin/', admin.site.urls), ]
运行应用,进入网址,在网址后面输入我们要进入的页面/polls,然后回车
函数 include() 允许引用其它 URLconfs。每当 Django 遇到 include() 时,它会截断与此项匹配的 URL 的部分,并将剩余的字符串发送到 URLconf 以供进一步处理。
include() 的理念是使其可以即插即用。因为投票应用有它自己的 URLconf( polls/urls.py ),他们能够被放在 "/polls/" , "/fun_polls/" ,"/content/polls/",或者其他任何路径下,这个应用都能够正常工作。
三、设计一个应用的数据库
创建数据表
执行下面的操作
python manage.py migrate
创建模型
polls/models.py
from django.db import models # Create your models here. class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
激活模型
上面的一小段用于创建模型的代码给了 Django 很多信息,通过这些信息,Django 可以:
为这个应用创建数据库 schema(生成 CREATE TABLE 语句)。
创建可以与 Question 和 Choice 对象进行交互的 Python 数据库 API。
但是首先得把 polls 应用安装到项目里。
为了在我们的工程中包含这个应用,我们需要在配置类 INSTALLED_APPS 中添加设置。
因为 PollsConfig 类写在文件 polls/apps.py 中,所以它的点式路径是 'polls.apps.PollsConfig'。
在文件 web/settings.py 中 INSTALLED_APPS 子项添加点式路径。
"polls.apps.PollsConfig",
运行迁移命令,生成数据迁移文件
python manage.py makemigrations polls
创建新定义的模型的数据表
真正让数据变化生效的命令是migate。
运行 migrate 命令,在数据库里创建新定义的模型的数据表:
python manage.py migrate
改变模型需要这三步:
编辑 models.py 文件,改变模型。
运行 python manage.py makemigrations 为模型的改变生成迁移文件。
运行 python manage.py migrate 来应用数据库迁移。
查看数据库的数据
python manage.py shell
修改一下模型,来让调试具有更高的可读性
import datetime from django.db import models # Create your models here. class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") #新增 def __str__(self): return self.question_text def was_published_recently(self): now = timezone.now() return now - datetime.timedelta(days=1) <= self.pub_date <= now class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) #新增 def __str__(self): return self.choice_text
连接管理站
创建管理员账号
python manage.py createsuperuser
启动开发服务器
python manage.py runserver
在管理页面中添加投票应用,修改文件polls/admin.py
from django.contrib import admin from .models import Question admin.site.register(Question)
在网址输入/admin进入管理站,输入你的用户名和密码
现在可以直接从管理站添加新的投票和选项了
四、创建更多的视图
在我们的投票应用中,我们需要下列几个视图:
- 问题索引页——展示最近的几个投票问题。
- 问题详情页——展示某个投票的问题和不带结果的选项列表。
- 问题结果页——展示某个投票的结果。
- 投票处理器——用于响应用户为某个问题的特定选项投票的操作。
向 polls/views.py 里添加更多视图
from django.shortcuts import render from django.http import HttpResponse # Create your views here. def index(request): return HttpResponse("Hello, world. You're at the polls index.") # 新增 def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
把这些新视图添加进 polls/urls.py 模块里
from django.urls import path from . import views urlpatterns = [ # ex: /polls/ path("", views.index, name="index"), # ex: /polls/5/ path("<int:question_id>/", views.detail, name="detail"), # ex: /polls/5/results/ path("<int:question_id>/results/", views.results, name="results"), # ex: /polls/5/vote/ path("<int:question_id>/vote/", views.vote, name="vote"), ]
每个视图必须要做的只有两件事:返回一个被请求页面内容的HttpResponse对象,或者抛出一个异常。
修改polls/views.py
from django.shortcuts import render from django.http import HttpResponse from .models import Question # Create your views here. def index(request): latest_question_list = Question.objects.order_by("-pub_date")[:5] output = ", ".join([q.question_text for q in latest_question_list]) return HttpResponse(output) # 新增 def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
页面的设计写死在视图函数的代码里的。
所以让我们使用 Django 的模板系统,只要创建一个视图,就可以将页面的设计从代码中分离出来。
在polls 目录里创建一个 templates 目录
Django 将会在这个目录里查找模板文件。
在创建的 templates 目录里,再创建一个目录 polls,然后在其中新建一个文件 index.html 。模板文件的路径应该是 polls/templates/polls/index.html 。
更新一下 polls/views.py 里的 index 视图来使用模板:
from django.shortcuts import render from django.http import HttpResponse from django.template import loader from .models import Question # Create your views here. def index(request): latest_question_list = Question.objects.order_by("-pub_date")[:5] template = loader.get_template("polls\index.html") context = { "latest_question_list": latest_question_list, } return HttpResponse(template.render(context, request)) # 新增 def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
尝试用 get() 函数获取一个对象,如果不存在就抛出 Http404 错误也是一个普遍的流程。
from django.shortcuts import render from django.http import HttpResponse from django.http import Http404 from django.shortcuts import get_object_or_404, render from django.template import loader from .models import Question # Create your views here. def index(request): latest_question_list = Question.objects.order_by("-pub_date")[:5] template = loader.get_template("polls\index.html") context = { "latest_question_list": latest_question_list, } return HttpResponse(template.render(context, request)) # 新增 def detail(request, question_id): 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): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
设计detail视图对应的模板
polls/templates/polls/detail.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul> </body> </html>
创建一个 polls/results.html
模板:
polls/templates/polls/results.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>My test page</title> </head> <body> <h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' question.id %}">Vote again?</a> </body> </html>
去除index中的硬编码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a> </li> {% endfor %} </ul> {% else %} <p>No polls are available</p> {% endif %} </body> </html>
为url添加命名空间,以面对错综复杂的应用路径。
polls/urls.py
from django.urls import path from . import views app_name = "polls" urlpatterns = [ # ex: /polls/ path("", views.index, name="index"), # ex: /polls/5/ path("<int:question_id>/", views.detail, name="detail"), # ex: /polls/5/results/ path("<int:question_id>/results/", views.results, name="results"), # ex: /polls/5/vote/ path("<int:question_id>/vote/", views.vote, name="vote"), ]
再次调整index.html中编码,以命名空间来调用
五、使用表单
更新一下投票详细页面的模板
polls/detail.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} <fieldset> <legend><h1>{{ question.question_text }}</h1></legend> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} {% for choice in question.choice_set.all %} <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}"> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br> {% endfor %} </fieldset> <input type="submit" value="Vote"> </form> </body> </html>
将下面的代码添加到
polls/views.py
from django.http import HttpResponse, HttpResponseRedirect from django.urls import reverse def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST["choice"]) except (KeyError, Choice.DoesNotExist): # Redisplay the question voting form. return render( request, "polls/detail.html", { "question": question, "error_message": "You didn't select a choice.", }, ) else: selected_choice.votes += 1 selected_choice.save() return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
将投票应用转换成使用通用视图系统,这样可以删除许多代码。
仅仅需要做以下几步来完成转换:
转换 URLconf。
删除一些旧的、不再需要的视图。
基于 Django 的通用视图引入新的视图
改良URLconf
打开 polls/urls.py
这个 URLconf 并将它修改成:
from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('<int:pk>/', views.DetailView.as_view(), name='detail'), path('<int:pk>/results/', views.ResultsView.as_view(), name='results'), path('<int:question_id>/vote/', views.vote, name='vote'), ]
再改polls/views.py
from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from .models import Choice, Question 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.""" return Question.objects.order_by('-pub_date')[:5] class DetailView(generic.DetailView): model = Question template_name = 'polls/detail.html' class ResultsView(generic.DetailView): model = Question template_name = 'polls/results.html' def vote(request, question_id): ... # same as above, no changes needed.