Django app,第2部分

本教程从教程1的中断处开始,我们将设置数据库,创建你的第一个模型,并对Django的自动生成管理网站的内容给一个快速介绍。

 

 

 

数据库设置

现在,打开 mysite/settings.py 文件。这是一个包含一些模块级别的Django设置变量的普通模块。

默认情况下,配置使用SQLite数据库。如果你不熟悉数据库,或者只是对使用Django感兴趣,使用默认的SQLite数据库是最简单的选择。SQLite数据库是包含在Python中的,因此你不需要为了支持数据库而安装任何东西。当开始你的第一个实际项目时,你可能会为了避免来回切换数据库的麻烦,希望使用可扩展性更高的数据库,比如PostgreSQL。

如果你希望使用另外的数据库,安装合适的数据库绑定,并为了匹配你的数据库连接设置,需要在字典元素DATABASES 'default'中,修改下面的这些键的值:

· ENGINE - 后面这些中的任意一个,'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', 或者 'django.db.backends.oracle'。其他的数据库也可以使用

· NAME - 是你数据库的名称。如果你使用的是SQLite,那么数据库将会是你电脑上的一个文件,在这种情况下, NAME应该是路径应该包含文件文件名的完整路径。默认值是 os.path.join(BASE_DIR, 'db.sqlite3'), 将在你的项目目录中存储该文件。

 

如果你不使用SQLite作为你的数据库,那么必须添加其他的设置,比如 USER, PASSWORD, 和 HOST。关于这个更多的细节,请参见参考文档DATABASES

 

 

关于SQLite以外的数据库

如果你正在使用SQLite数据库以外的数据库,请确保你在这个时候已经创建了数据库。在数据库的交互窗口执行“CREATE DATABASE database_name;”数据库语句。

还要确保在mysite/settings.py中提供数据库用户有创建数据库的权限。这个数据库语句可以自动创建一个 测试数据库,同时这个数据库在后面的教程中也会被使用。

如果你使用的是SQLite数据库,你事先便不需要创建任何内容,这个数据库文件会在需要创建的时候,自动创建。

在编辑mysite/settings.py文件时,将 TIME_ZONE变量设置为你的时区。另外,请注意INSTALLED_APPS设置在该文件的顶部。TIME_ZONE变量包含这个项目实例中**的所有Django应用程序的名字。这些应用程序能在多个项目中使用,你可以打包和发布这些程序,给其他人在他们的项目中使用。

默认情况下, INSTALLED_APPS变量中包含下面的这些应用程序,它们都是由Django提供的。

 

 

 

 

 

 

这些应用程序为了方便大多数的情况,默认包含在INSTALLED_APPS变量中。

这些应用程序中的某些应用会置少使用一个数据表,因此,在使用它们之前,我们需要在数据库中创建这些数据表。通过下面的命令来完成:

$ python manage.py migrate

这个migrate命令将查看 INSTALLED_APPS 变量中的设置内容,并根据你的mysite/settings.py 文件中的数据库,和伴随项目中特定的应用程序的数据库(会在后面进行介绍),一起设置创建任何有必要的数据表。你将看到每个迁移请求的信息。如果你感兴趣,运行你的数据库命令行客户端,并输入\dt(PostgreSQL),SHOW TABLES; (MariaDB, MySQL), .schema (SQLite), 或者 SELECT TABLE_NAME FROM USER_TABLES; (Oracle) 来显示Django创建的表.

 

 

对于极简主义者

就像上面我们所说的,为了大多数常见,默认应用程序的表都会被包含在迁移命令创建的表中,但是不是每个人都需要它们。如果你不需要它们中的任何一个或者全部,在运行 migrate命令之前,可以随时从INSTALLED_APPS变量中,注释绘制删除掉适当的行。migrate命令仅仅针对在INSTALLED_APPS变量中的应用程序,运行迁移操作。

 

 

创建模型

现在我们将定义你的模型 -- 本质上是你的数据库布局以及其他的元数据

 

 

哲学

一个模型是你的数据的唯一,确定的事实来源。它包含你存储的数据的基本字段和行为。Django遵循DRY原则。模型的目的是在一个位置定义你的数据模型,并自动从模型中自动派生数据。

这个原则包括迁移操作,与Ruby On Rails框架不同,举个例子,迁移操作是完全源自你的模型文件,并且本质上是个历史记录,Django可以通过滚动这个历史记录来更新你的数据库用来匹配你的当前模型。

在民意调查应用中,我们将创建两个模型:QuestionChoice。一个Question表有一个问题和一个出版日期。一个Choice表有两个字段:选择的文本和投票提示。每一个Choice实例对象(表)都与一个Question实例对象关联。

这些概念都由Python类来表示,按照下方来编辑polls/models.py文件:

polls/models.py

from django.db import models

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.db.models.Model类的子类来表示。每个模型都有许多类变量,每个类变量都表示一个模型中的数据库字段。

每个字段都是由一个Field 类的实例表示– e.g. CharField 对应字符字段, DateTimeField 对应日期字段。这些 Field类的实例告诉Django每个字段保存什么类型的数据。

每个 Field类实例(e.g. question_text or pub_date) 的名字是数据表中字段的名字,这个名字采用的是机器友好的格式。你将在你的Python代码中使用到这个值(名字),同时你的数据库将使用它作为表的列名。

你可以使用 Field类实例的可选的第一个参数,来给这个Field类实例指定一个人类可读性好的名称。在Django的一些自省部分种使用了该功能,同时它也在文档中被使用。如果这个字段(人类可读性好的名字)没有被提供,Django会使用机器可读性好的名字。在这个例子中,我们仅为Question.pub_date字段提供了人类可读性好的名字。对于这个模型的其他字段,这个机器可读性好的名字将足以作为人类可读性好的名字。

一些 Field 类有必要参数。举个例子,CharField类要求你给一个max_length参数值,我们很快就会看到,这些必要的参数不仅用于数据库的架构上,还用于数据的验证。

一个 Field类实例,也可以具有各种可选参数,在这个例子中,我们将votes字段的default 参数的值设置为了0。

最后,注意使用ForeignKey方法定义了一个关系。这个关系告诉Django,每个Choice实例 与一个 Question实例相关。Django支持所有常见的数据库关系:多对一,多对多和一对一。

 

 

**模型

少量的模型代码给了Django许多的信息。有了它,Django能够:

  • 为此应用程序创建一个数据库架构(CREATE TABLE语句)。
  • 创建一个用于访问QuestionChoice对象的Python数据库访问API 。

但是首先我们需要告诉我们的项目,该polls应用程序已经安装。

 

 

哲学

Django应用程序是“可插拔的”:您可以在多个项目中使用一个应用程序,并且可以分发这些应用程序,因为它们不必绑定到给定的Django安装中。

 

要将应用程序放进我们的项目中,我么需要在它的 INSTALLED_APPS变量中添加它的配置类的引用。 PollsConfig类是在polls/apps.py文件中,所以它的点路径(相对路径)是 'polls.apps.PollsConfig'。编辑mysite/settings.py 文件,并将那个点路径添加到INSTALLED_APPS变量中。此时它看起来像这样:

mysite/settings.py

INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

现在Django知道包括该polls应用程序,让我们来运行另一个命令:

$ python manage.py makemigrations polls

你应该看到类似于以下内容:

Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Question
    - Create model Choice

通过运行makemigrations命令,你将告诉Django,你对你的模型做了一些更改,并且您希望将更改存储为迁移

迁移是Django对模型(以及数据库架构)所做更改的保存方式,它们是磁盘上的文件。如果你愿意,你可以阅读你的新模型的迁移文件,它是polls/migrations/0001_initial.py文件。不用担心,你不用每次迁移的时候你都去阅读它们,但是为了适应你想手动调整Django的更改方式,它们被设计为人类可编辑的。

这里有一个命令将为你运行迁移并自动管理你的数据库架构,这个命令叫做migrate,稍后我们将会介绍它,但是首先,让我们看看迁移会运行哪些SQL语句。sqlmigrate命令接收迁移文件名称(0001)并返回它们的数据库语句:

$ python manage.py sqlmigrate polls 0001

你应该看到类似下方的内容(为了便于阅读,我们对其进行了重新格式化)

BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" serial NOT NULL PRIMARY KEY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL,
    "question_id" integer NOT NULL
);
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");

COMMIT;

请注意以下几点:

  • 确切的输出将根据你所使用的数据库不同而有所不同,上面的示例是PostSQL生成的。

 

  • 表格名称是通过将应用程序的名称(polls)与模型的小写名称question 、choice组合而成的。(你可以覆盖此行为)

 

  • 自动添加主键(ID)。(你也可以覆盖它。)

 

  • 按照约定,Django会在外键字段名称后添加"_id"。(是的,您也可以覆盖它。)

 

  • 通过 FOREIGN KEY 约束使外键关系明确. 不用担心 DEFERRABLE 部分; 它告诉PostgreSQL在事务结束之前不要执行外键.

 

  • 它是针对你正在使用的数据库量身定制的, 因此将自动为你处理特定于数据库的字段类型,例如auto_increment(MySQL),serial(PostgreSQL)或(SQLite)。 字段名称的引用也是如此–e.g.使用双引号或单引号。

 

  • sqlmigrate命令实际上不会在你的数据库中运行迁移 - 而是将它打印输出到屏幕上,便于你查看Django认为需要的SQL语句.这对于检查Django将要执行的操作或作为数据库管理员,是否需要SQL脚本进行更改非常有用。

如果你有兴趣,你也可以运行 python manage.py check命令。这个命令可以在无需进行迁移或者接触数据库的情况下,检测你的项目中是否存在任何问题。

现在,再次运行 migrate命令,来在你的数据库中创建这些模型表:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Rendering model states... DONE
  Applying polls.0001_initial... OK

migrate命令默认接收所有尚未应用的迁移名称(Django使用在数据库中称为django_migrations的特殊表跟踪哪些迁移文件被迁移了),并针对您的数据库运行所有尚未应用的迁移名称。

迁移的功能非常强大,使你在开发的时候不需删除你的数据库或表并创建新的模型,就可以随时更改模型。他专门用于实时更新数据库而不会丢失数据。我们将在本教程后面的部分更加深入的介绍它们,但是现在,请记住模型修改的三步指南:

· 修改模型(在models.py中)

· 运行python manage.py makemigrations命令行,为这些修改生成迁移文件,告诉Django,对模型进行了修改。

· 运行 python manage.py migrate命令行,在数据库中执行这些修改

之所以使用单独的命令来生成迁移文件和运行迁移命令,是因为你会将迁移文件提交到版本控制系统,并随应用程序一起交付,这样它们不仅使你 开发更加简单,还能够被其他开发人员和生成环境使用。

阅读django-admin文档以获取有关该manage.py实用程序可以做什么的完整信息。

 

使用API

现在,让我们跳入交互式Python外壳并尝试使用Django提供的免费API来进行交互。要调用Python Shell,请使用以下命令:

$ python manage.py shell

我们使用它而不是简单地输入“ python”,因为manage.py 文件设置了DJANGO_SETTINGS_MODULE环境变量,该变量为Django提供了Python导入

mysite/settings.py文件的路径。

进入Shell后,探索数据库API

>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>

# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> q.save()

# Now it has an ID.
>>> q.id
1

# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

等一下。<Question: Question object (1)>不是该对象的有用表示。让我们通过编辑 Question模型(在polls/models.py文件中),在 QuestionChoice类中添加 __str__()方法,来修复这个问题:

polls/models.py

from django.db import models

class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text

 

在模型中添加 __str__()方法使非常重要的,这不仅在你使用交互式提示窗口进行交互提供了方便,同时因为在Django的项目的生命周期中都使用了对象的表示形式(because objects’ representations are used throughout Django’s automatically-generated admin.)。

我们还向该模型添加一个自定义方法:

polls/models.py

import datetime

from django.db import models
from django.utils import timezone


class Question(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

注意import datetimefrom django.utils import timezone这两个语句的添加,这两个语句的做事分别是:引用Python的标准库模块 datetime模块和在 django.utils.timezone模块中的Django的时区相关的模块。如果您不熟悉Python中的时区处理的相关细节,则可以在时区支持文档中了解更多信息。

保存这些更改,然后通过再次运行python manage.py shell命令,来启动新的Python交互式窗口:

>>> from polls.models import Choice, Question 
# Make sure our __str__() addition worked. 
>>> Question.objects.all() 
<QuerySet [<Question: What's up?>]> 

# Django provides a rich database lookup API that's entirely driven by 
# keyword arguments. 
>>> Question.objects.filter(id=1) 
<QuerySet [<Question: What's up?>]> 
>>> Question.objects.filter(question_text__startswith='What') <QuerySet [<Question: What's up?>]> 

# Get the question that was published this year. 
>>> from django.utils import timezone 
>>> current_year = timezone.now().year 
>>> Question.objects.get(pub_date__year=current_year) 
<Question: What's up?> 

# Request an ID that doesn't exist, this will raise an exception. 
>>> Question.objects.get(id=2) 
Traceback (most recent call last): 
    ...   
DoesNotExist: Question matching query does not exist. 

# Lookup by a primary key is the most common case, so Django provides a 
# shortcut for primary-key exact lookups. 
# The following is identical to Question.objects.get(id=1). 
>>> Question.objects.get(pk=1) 
<Question: What's up?> 

# Make sure our custom method worked. 
>>> q = Question.objects.get(pk=1) 
>>> q.was_published_recently() 
True 

# Give the Question a couple of Choices. The create call constructs a new 
# Choice object, does the INSERT statement, adds the choice to the set # of available choices and returns the new Choice object. Django creates 
# a set to hold the "other side" of a ForeignKey relation 
# (e.g. a question's choice) which can be accessed via the API. 
>>> q = Question.objects.get(pk=1) 

# Display any choices from the related object set -- none so far. 
>>> q.choice_set.all() 
<QuerySet []> 

# Create three choices. 
>>> 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)

# Choice objects have API access to their related Question objects. 
>>> c.question 
<Question: What's up?> 

# And vice versa: Question objects get access to Choice objects. 
>>> q.choice_set.all() 
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> 
>>> q.choice_set.count() 
3 

# The API automatically follows relationships as far as you need. 
# Use double underscores to separate relationships. 
# This works as many levels deep as you want; there's no limit. 
# Find all Choices for any question whose pub_date is in this year 
# (reusing the 'current_year' variable we created above). 
>>> 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()

有关模型关系的更多信息,请参见访问相关对象。有关通过API执行字段,如何使用双下划线

查找的更多信息,请参见字段查找。有关数据库API的完整详细信息,请参见我们的数据库API参考

 

Django管理网站的介绍

 

 

哲学

 

为您的员工或客户添加,更改和删除内容而生成管理网站是繁琐的工作,这个生成管理网站

的过程不需要太多的创造力。因此,Django能完全自动化创建模型管理界面。

Django是在新闻编辑室环境中编写的,它在“内容发布者”和“公共”网站之间有非常清晰的分隔。网站管理员使用该系统(内容发布者的网站)添加新闻报道,事件,体育比分等,并且该内容显示在公共网站上。Django为了网站管理员编辑内容,解决了创建统一的接口的问题。

管理员网站不适合网站访问者使用。它是针对网站管理员的。

 

 

创建一个管理员用户

首先,我们需要创建一个可以登录到管理网站的用户。运行以下命令:

 

$ python manage.py createsuperuser

输入所需的用户名,然后按Enter。

Username: admin

然后将提示您输入所需的电子邮件地址:

Email address: [email protected]

最后一步是输入您的密码。系统将要求您输入两次密码,第二次输入密码是对第一次输入的验证。

Password: **********
Password (again): *********
Superuser created successfully.

 

启动开发服务器

默认情况下Django管理网站处于**状态。让我们启动开发服务器并进行探索。

如果服务器未运行,请像这样启动它:

$ python manage.py runserver

现在,打开Web浏览器,然后转到本地域上的“ / admin /”地址中,例如 http://127.0.0.1:8000/admin/。您应该看到管理员的登录页面:

 

Django app,第2部分

 

由于默认情况下翻译功能是打开的,因此登录界面可能会以您自己的语言显示,具体取决于您的浏览器设置以及Django是否具有翻译该语言的功能。·

 

输入管理员网站

现在,尝试使用在上一步中创建的超级用户帐户登录。您应该看到Django 管理网站的索引页面:

 

Django app,第2部分

 

您应该看到几种类型的可编辑内容:组和用户。它们由Django 提供的身份验证框架django.contrib.auth模块提供。

 

 

在管理员中修改投票应用程序

但是我们的民意调查应用程序在哪里?它没有显示在管理网站的索引页面上。

只需要做一件事:我们需要告诉管理网站,Question对象具有管理界面。为此,请打开polls/admin.py文件,然后将其编辑为如下所示:

polls/admin.py

from django.contrib import admin

from .models import Question

admin.site.register(Question)

 

探索免费的管理功能

现在我们已经注册了Question,Django知道应该在admin索引页面上显示它:

 

Django app,第2部分

 

点击“问题”。现在,您在“更改列表”页面上提问。此页面显示数据库中的所有问题,并允许您选择一个来更改它。这里有一个我们之前创建的问题"What's up":

Django app,第2部分

点击 “What’s up?” 这个问题来编辑它。

Django app,第2部分

 

这里要注意的事情:

  • 该表格是根据Question模型自动生成的。
  • 不同的模型字段类型(DateTimeField, CharField)对应于适当的HTML输入窗口小部件。每种类型的字段都知道如何在Django管理员中显示自己。
  • 每个DateTimeField都有免费的JavaScript快捷方式。日期具有“今天”快捷方式和日历弹出窗口,时间具有“现在”快捷方式和列出了常用输入时间的便捷弹出窗口。

页面底部为您提供了一些选项:

  • 保存–保存更改并返回此类型对象的更改后的列表页面。
  • 保存并继续编辑–保存更改并重新加载该对象的管理页面。
  • 保存并添加另一个–保存更改并为此类型的对象加载新的空白表格。
  • 删除–显示删除确认页面。

如果“发布日期”的值与在教程1中创建问题的时间不匹配,则可能意味着您忘记为该TIME_ZONE设置设置正确的值。更改它,重新加载页面并检查是否显示正确的值。

通过单击“今天”和“现在”快捷方式来更改“发布日期”。然后点击“保存并继续编辑”。然后,单击右上角的“历史记录”。您将看到一个页面,该页面列出通过Django管理员对该对象所做的所有更改,以及时间戳和进行更改的人的用户名:

Django app,第2部分

 

 

当你熟悉Models API并熟悉管理站点后,请阅读本教程的第3部分,以了解如何向我们的民意调查应用程序添加更多视图。