基于Django的python小应用——投票

一、首先需要创建应用的环境

将在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("/", views.detail, name="detail"),
        # ex: /polls/5/results/
        path("/results/", views.results, name="results"),
        # ex: /polls/5/vote/
        path("/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

      Title

    {{ question.question_text }}

      {% for choice in question.choice_set.all %}
    • {{ choice.choice_text }}
    • {% endfor %}

    创建一个 polls/results.html 模板:

    polls/templates/polls/results.html

       My test page   

    {{ question.question_text }}

      {% for choice in question.choice_set.all %}
    • {{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
    • {% endfor %}
    Vote again?

    去除index中的硬编码

      Title{% if latest_question_list %} 
      {% for question in latest_question_list %}
    • {{ question.question_text }}
    • {% endfor %}
    {% else %}

    No polls are available

    {% endif %}

    为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("/", views.detail, name="detail"),
        # ex: /polls/5/results/
        path("/results/", views.results, name="results"),
        # ex: /polls/5/vote/
        path("/vote/", views.vote, name="vote"),
    ]
    

    再次调整index.html中编码,以命名空间来调用

    五、使用表单

    更新一下投票详细页面的模板

    polls/detail.html

      Title
    {% csrf_token %}

    {{ question.question_text }}

    {% if error_message %}

    {{ error_message }}

    {% endif %} {% for choice in question.choice_set.all %}
    {% endfor %}

    将下面的代码添加到

    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('/', views.DetailView.as_view(), name='detail'),
        path('/results/', views.ResultsView.as_view(), name='results'),
        path('/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.
    

    运行效果