编写你的第一个 Django 应用程序,第4部分

Posted xh2023

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编写你的第一个 Django 应用程序,第4部分相关的知识,希望对你有一定的参考价值。

Django

本教程从教程 3 停止的地方开始。我们是 继续民意调查应用程序,并将专注于表单处理和 减少我们的代码。

一、编写最小表单

让我们更新上一个教程的投票详细信息模板(“polls/detail.html”) ,以便模板包含一个 HTML  <form>元素:

<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>
  • 由于我们正在创建一个 POST 表单(这可能会产生修改的效果 数据),我们需要担心跨站点请求伪造。 值得庆幸的是,你不必太担心,因为 Django 带有一个 有用的系统来防止它。简而言之,所有 POST 表单 定位到内部网址应使用 csrf_token % 模板标记。

现在,让我们创建一个 Django 视图来处理提交的数据并 随之而来的东西。请记住,在教程 3 中,我们 为包含以下行的投票应用程序创建了一个 URLconf:

path("<int:question_id>/vote/", views.vote, name="vote"),

我们之前创建了vote()函数的虚拟实现。让我们 创建一个真实版本。将以下内容添加到:polls/views.py

from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Choice, Question


# ...
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()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))

此代码包含一些我们在本教程中尚未介绍的内容:

  • 请求。POST是一个类似字典的 允许您按键名访问提交数据的对象。在本例中,返回所选选项的 ID,作为 字符串。请求。开机自检值为 始终为字符串。request.POST[\'choice\']

    请注意,Django 也提供了请求。 GET 以相同的方式访问 GET 数据 – 但我们明确使用请求。在我们的代码中 POST,以确保数据仅 通过开机自检呼叫进行更改。

  • request.POST[\'choice\']如果开机自检数据中未提供,将引发密钥错误。上面的代码检查 KeyError 并重新显示错误的问题表单 消息(如果未给出)。choicechoice

  • 递增选择计数后,代码将返回 HttpResponseRedirect,而不是正常的 HttpResponseHttpResponseRedirect采用单个参数: 用户将被重定向到的 URL(请参阅以下点,了解如何 在这种情况下,我们构造 URL)。

    正如上面的 Python 注释所指出的,在成功处理 开机自检数据。这个技巧不是特定于 Django 的;这是很好的网络开发 一般做法。

  • 在这个例子中,我们在 HttpResponseRedirect 构造函数中使用了 reverse() 函数。 此函数有助于避免在视图函数中对 URL 进行硬编码。 它被赋予了我们要向其传递控制权的视图的名称和 指向该视图的 URL 模式的变量部分。在此 案例,使用我们在教程 3 中设置的 URLconf, 这个 reverse() 调用将返回一个字符串,例如 

    "/polls/3/results/"

对问题进行投票后,vote() 视图将重定向到结果 问题的页面。让我们改写下这个视图:

from django.shortcuts import get_object_or_404, render


def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/results.html", "question": question)

这与教程 3 中的 detail() 视图几乎完全相同。唯一的区别是模板名称。我们将解决此问题 以后冗余。

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

<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>

现在,转到/polls/1/ 浏览器中并在问题中投票。您应该会看到一个 每次投票时都会更新的结果页面。如果您提交表格 如果没有选择选项,您应该会看到错误消息。

 

二、使用通用视图:代码越少越好

detail()(来自教程 3)和 results() 视图非常短 - 如上所述,冗余。显示投票列表的 index() 视图与此类似。

这些视图代表了基本 Web 开发的常见情况:从以下位置获取数据 数据库根据 URL 中传递的参数,加载模板和 返回呈现的模板。

因为这太常见了,Django 提供了一个 快捷方式,称为“通用视图”系统。

通用视图将常见模式抽象到您甚至不需要的程度 编写 Python 代码来编写应用程序。

让我们将我们的投票应用程序转换为使用通用视图系统,以便我们可以删除 一堆我们自己的代码。我们必须采取几个步骤才能进行转换。 我们会的:

  1. 转换网址。
  2. 删除一些旧的、不需要的视图。
  3. 基于 Django 的通用视图引入新视图。

请继续阅读以了解详细信息。

三、修改网址

首先,打开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"),
]

请注意,第二个和 第三种模式已从 <question_id>更改为 <pk>。

四、修改视图

接下来,我们将删除旧的 index、detail 和 results 视图,并使用 Django 的通用视图。为此,请打开 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.

我们在这里使用两个通用视图: ListView and DetailView视图。分别是那些 两个视图抽象化了“显示对象列表”和 “显示特定类型对象的详细信息页面。”

  • 每个通用视图都需要知道它将执行什么模型 后。这是使用model属性提供的。
  • DetailView通用视图 期望调用从 URL 称为pk值,因此我们把question_id更改为pk 的通用视图。

在本教程的前面部分中,提供了模板 具有包含 和 上下文变量的上下文。

对于变量提供 自动 – 因为我们使用的是 Django 模型 (), Django 能够为上下文变量确定适当的名称。

但是,对于 ListView,自动生成的上下文变量为 。为了覆盖它,我们提供了属性,指定我们要改用。

作为替代方法,您可以更改模板以匹配 新的默认上下文变量 - 但告诉 Django 要容易得多 使用所需的变量。

 

运行服务器,并使用基于通用视图的新网络投票应用。

有关通用视图的完整详细信息,请参阅通用视图文档

当您熟悉表单和通用视图后,请阅读本文的第 5 部分 教程,了解如何测试我们的民意调查应用程序。

  ---------------------------------------end--------------------------

Django L1 编写你的第一个Django应用,第1部分

http://python.usyiyi.cn/django/index.html

 

1 查看django版本

python -c ‘import django ; print(django.get_version())‘
1.10.5

  

创建一个项目

$ django-admin startproject ltest

$tree
.
├── ltest
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── settings.py
│   ├── settings.pyc
│   ├── urls.py
│   └── wsgi.py
├── manage.py

  

settings.py 

默认情况下,INSTALLED_APPS包含下面的应用,它们都是Django 与生俱来的:

django.contrib.admin —— 管理站点。你将在本教程的第2部分使用到它。
django.contrib.auth —— 认证系统。
django.contrib.contenttypes —— 用于内容类型的框架。
django.contrib.sessions —— 会话框架。
django.contrib.messages —— 消息框架。
django.contrib.staticfiles —— 管理静态文件的框架。
这些应用,默认包含在Django中,以方便通用场合下使用。

然而上面的部分应用至少需要使用一个数据库表,因此我们需要在使用它们之前先在数据库中创建相应的表。要做到这一点,请运行以下命令:

  

开发服务器

$ python manage.py runserver
$ python manage.py runserver 0.0.0.0:8080

  

创建模型

一个项目可以有多个应用,一个应用可以属于多个项目

$ python manage.py startapp polls
$ tree
.
├── ltest
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── settings.py
│   ├── settings.pyc
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── tasks
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
└── templates

  

 

创建模型

cat tasks/models.py 
from __future__ import unicode_literals

from django.db import models

# Create your models here.

class Questions(models.Model):
    question_text = models.CharField(max_length=200)
    pub_data = models.DateTimeField(data published)


class Choice(models.Model):
    question = models.ForeignKey(Questions)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

 

激活模型

  Django 应用是可以“热插拔”的,即可以在多个项目中使用同一个应用,也可以分发这些应用, 因为它们不需要与某个特定的Django安装绑定。

$grep -Ri  INSTALLED_APPS -A 10 ./ltest/settings.py
INSTALLED_APPS = [
    ‘django.contrib.admin‘,
    ‘django.contrib.auth‘,
    ‘django.contrib.contenttypes‘,
    ‘django.contrib.sessions‘,
    ‘django.contrib.messages‘,
    ‘django.contrib.staticfiles‘,

    ‘tasks‘,
]

  

$python manage.py makemigrations tasks
Migrations for ‘tasks‘:
  tasks/migrations/0001_initial.py:
    - Create model Choice
    - Create model Questions
    - Add field question to choice

有一个命令可以运行这些迁移文件并自动管理你的数据库模式 —— 它叫做migrate,我们一会儿会用到它 —— 但是首先,让我们看一下迁移行为将会执行哪些SQL语句。sqlmigrate命令接收迁移文件的名字并返回它们的SQL语句:

t$ python manage.py sqlmigrate tasks 0001
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "tasks_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL);
--
-- Create model Questions
--
CREATE TABLE "tasks_questions" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_data" datetime NOT NULL);
--
-- Add field question to choice
--
ALTER TABLE "tasks_choice" RENAME TO "tasks_choice__old";
CREATE TABLE "tasks_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "tasks_questions" ("id"));
INSERT INTO "tasks_choice" ("choice_text", "votes", "id", "question_id") SELECT "choice_text", "votes", "id", NULL FROM "tasks_choice__old";
DROP TABLE "tasks_choice__old";
CREATE INDEX "tasks_choice_7aa0f6ee" ON "tasks_choice" ("question_id");
COMMIT;

  

  • sqlmigrate命令并不会在你的数据库上真正运行迁移文件 —— 它只是把Django 认为需要的SQL打印在屏幕上以让你能够看到。 这对于检查Django将要进行的数据库操作或者你的数据库管理员需要这些SQL脚本是非常有用的。

 

$ python manage.py check #它会检查你的项目中的模型是否存在问题,而不用执行迁移或者接触数据库。
System check identified no issues (0 silenced).

  再次运行migrate以在你的数据库中创建模型所对应的表:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, tasks
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying sessions.0001_initial... OK
  Applying tasks.0001_initial... OK

  

迁移功能非常强大,可以让你在开发过程中不断修改你的模型而不用删除数据库或者表然后再重新生成一个新的 —— 它专注于升级你的数据库且不丢失数据。 我们将在本教程的后续章节对迁移进行深入地讲解,但是现在,请记住实现模型变更的三个步骤:

 

玩转API 

$ python manage.py shell
Python 2.7.6 (default, Oct 26 2016, 20:30:19)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

 

如果你不想使用manage.py,也没问题。只要设置DJANGO_SETTINGS_MODULE 环境变量为 mysite.settings,启动一个普通的Python shell,然后建立Django:

>>> import django
>>> django.setup()
  

数据库API

 

 

给你的模型添加__str__()方法很重要,不仅会使你自己在使用交互式命令行时看得更加方便,而且会在Django自动生成的管理界面中使用对象的这种表示。

__str__ 还是 __unicode__?

对于Python 3来说,这很简单,只需使用__str__()

对于Python 2来说,你应该定义__unicode__()方法并返回unicode 值。Django 模型具有一个默认的__str__() 方法,它会调用__unicode__()并将结果转换为UTF-8 字节字符串。这意味着unicode(p)将返回一个Unicode 字符串,而str(p)将返回一个字节字符串,其字符以UTF-8编码。Python 的行为则相反:对象__unicode__方法调用 __str__方法并将结果理解为ASCII 字节字符串。这个不同点可能会产生困惑。

 

models.py

from __future__ import unicode_literals

from django.db import models

# Create your models here.

class Questions(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField(‘data published‘)

    def __unicode__(self):      #
        return self.question_text

class Choice(models.Model):
    question = models.ForeignKey(Questions)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __unicode__(self):      #
        return self.choice_text

  

 

 

以上是关于编写你的第一个 Django 应用程序,第4部分的主要内容,如果未能解决你的问题,请参考以下文章

Django L2 编写你的第一个Django应用,第2部分¶

编写你的第一个 Django 应用程序,第8部分

编写你的第一个 Django 应用程序,第6部分

编写你的第一个Django应用程序,第四部分

编写你的第一个 Django 应用半成品

编写你的第一个Django应用程序