开始你的第一个Django应用(part 2)

Django1.11版本的官方教程翻译。

在这篇教程中,我们将设置你的数据库,创建你的第一个模型,并快速介绍Django的自动生成的管理站点。

数据库配置

现在打开mysite/settings.py这是一个普通的python模块,模块变量表示Django的设定。

默认情况下,Django配置使用SQLite数据库。如果你是数据库初学者,或者只是感兴趣想随便试试Django,这是最简单的选择。SQLite包含在Python内,因此你不需要安装任何其他东西来支持你的数据库。所以当你开始你的第一个真实项目时,你会想要使用一个更具扩展性的数据库,比如PostgreSQL,以避免数据库切换引发的麻烦。

如果你希望使用其他数据库,安装合适的数据库绑定并且在DATABASES'default'项目中修改keys以匹配数据库连接设置:

  • ENGINE
    比如 'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', or 'django.db.backends.oracle'。还有很多其他后端也有提供。详情请见

  • NAME
    你数据库的名字。如果你使用SQLite,数据库将是你计算机上的一个文件,这种情况下,NAME应该是这个文件的完整绝对路径,包括文件名。默认值os.path.join(BASE_DIR, 'db.sqlite3')将文件春处在项目目录中。

如果你不使用SQLite作为你的数据库,请务必添加USER, PASSWORD, 以及HOST。更多详情

写给其他用户的使用者
如果你在使用SQLite之外的数据库,请确保你使用前已经创建了数据库,请在你的数据库里使用CREATE DATABASE database_name;

同时确保mysite/settings.py拥有创建数据库的权限。这样就可以自动创建一个在后面的教程中需要的测试数据库。

如果你正在使用SQLite,你不需要提前创建任何东西,数据库文件会在必要的时候自动创建。

当你开始编辑你的mysite/settings.py,设置你的TIME_ZONE为你的时区。

另外,注意INSTALLED_APPS放在文件最上面,它承载着这个Django实例中所有活动的Django应用的名字,应用可以被使用于多个项目中,你可以打包分发他们让别人在别的项目中使用。

默认情况下,INSTALLED_APPS随Django包含着几个应用。

  • django.contrib.admin管理站点,你能立即使用上。
    -django.contrib.auth认证系统。
    -django.contrib.contenttypes内容类型的框架
    -django.contrib.sessions session框架
    -django.contrib.messages messaging框架
    -django.contrib.staticfiles 静态文件管理

这些应用默认包括在内以方便开发者使用。

其中一些应用程序,需要至少一个数据库表才能使用,因此我们需要在数据库中创建表,然后才能使用。请使用以下的命令:

python manage.py migrate

migrate命令查看INSTALLED_APPS的设置,并根据mysite/settings.py文件中的数据库设置创建需要的数据库表。如果你感兴趣的话,打开数据库命令行,输入以下命令来看看Django帮你创建了什么。

  • \dt(PostgreSQL),
  • SHOW TABLES;(MySQL),
  • .schema(SQLite),
  • SELECT TABLE_NAME FROM USER_TABLES;(Oracle)

写给极简主义者
正如我们上面所说,我们包含了默认应用为了涵盖常规使用,但并不是每个人都需要它们。如果你不需要,请在运行migrate之前,从INSTALLED_APPS中随意把注释掉或者删掉对应的代码。migrate命令只会运行INSTALLED_APPS中的应用。


创建模型

现在,我们使用额外的元数据来定义你的模型(本质上就是你的数据库布局)。

模型的哲学
一个模型是关于您的数据的唯一的确定的来源。它包含着您正在存储的数据的基本字段和行为。Django遵从DRY原则。我们的目标是在一个地方定义您的数据模型然后自动地从中获取数据。这就包括了迁移在里面。举个例子,不像Ruby on Rails,Django的迁移完全源自您的数据模型,并且本质上只是Django可以通过更新数据库模式来匹配当前模型的历史。

在我们这个简单的投票应用中,我们会创建两个模型:问题(question)选择(choice)。一个问题有一个问题内容和一个发布时间。一个选择有选择的文本和投票计数器。每个选择都会和一个问题关联。

这些概念通过简单的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的类来表示。每个模型都有一些类变量,每个变量都表示模型中的数据库字段。

每个字段通过实例中的一个字段类表示,比如CharField是字符字段,DateTimeField是时间字段。这会告诉Django每个字段保存着什么类型的数据。

每个字段实例的名字(比如question_textpub_date)是字段的对机器友好的格式的名字,你以后可以在你的Python代码中使用这个值,你的数据库也会用它作为列名称。

你可以使用(可选的)第一个位置参数作为指定的可读的名称。他可以用于Django的几个introspective(内省)部分,同时作为文档。如果没有提供,则Django将使用机器可读的名称作为名字。在这个例子中,我们只对Question.pub_date定义了一个名字。对于其他所有的字段都会默认使用自己的机器名作为名字。

有些字段类有一些必须的参数要填。举例,CharField要求你给出最大长度max_length。这不仅会用在数据库中,也会在验证中使用,后面我们会讲到。

一个字段也可以拥有很多可选的参数,比如我们可以看到,我们班votes的默认值设成了0。

最后一点,注意关系是由外键定义的。这告诉Django,每一个Choice都和只和一个问题相关。Django支持所有常见的数据库关系:多对一,多对多,一对一。


激活模型(activating models)

这个简单的模型告诉了Django很多信息。通过它,Django可以做到:

  • 为这个APP创建一个数据库对象集合
  • 创建一个Python数据库访问API来访问问题和选项的对象。

但是首先,我们应该告诉我们的项目,polls应用已经被安装了。

Django哲学小课堂
Django的应用是可插拔的。您可以在多个项目中使用某个应用程序,你可以分发应用程序,因为他们并不是和给定的Django安装绑定的。

为了把对应的应用包括进我们的项目中,我们需要在INSTALLED_APPS设置中添加对其配置类的引用。PollsConfig类在polls/app.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: -Creat model Choice -Create model Question -Add field question to choice

通过运行makemigrations,你可以告诉Django你已经对你的model做出了一些改变,并且你希望这些改变可以作为migration存储起来。(在现在的例子里是创建了一个新的model)

Django通过Migration来存储你的模型(也即是数据库对象)的变化。他们只是硬盘上的一些文件。如果你想,你可以为你的新模型读取一个Migration(迁移);也就是polls/migrations/0001_initial.py文件。不用担心,你并不需要每次Django作出改变都要读取它们,但是如果你想手动调整Django如何改变内容,那么它们是被设计为可以人为编辑的。

有一个命令可以帮你运行migration并且自动管理你的数据库对象,也就是我们稍后会介绍的migrate(迁移)。但是首先让我们看看migration会运行什么样的SQL。sqlmigrate命令接受迁移名称,并且返回其SQL:

python manage.py sqlmigrate polls 0001

你应该会看到类似下面的结果

BEGIN;
--
-- *Create model Choice*
--
CREATE TABLE "polls_choice" ( "id" serial NOT NULL PRIMARY KEY, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL );
--
-- *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 );
--
--*Add field question to choice*
--
ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL;
ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT;
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
ALTER TABLE "polls_choice" ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id" FOREIGN KEY ("question_id") REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED;
COMMIT;

注意:

  • 具体的输出会根据你如何使用的数据库而变化,上面的例子是根据PostgreSQL写的。
  • 表的名字会根据应用的名字以及模型的小写名自动生成(你可以覆盖这个行为)
  • 主键会自动生成(你可以覆盖这个)
  • 按照惯例,django将"_id"附加到外键字段名称。
  • 它根据你使用的数据库量身打造,因此数据库特定的字段类型比如auto_increment(MySQL), serial(PostgreSQL)或是integer primary key autoincrement (SQLite) 都会自动帮你处理。字段名的引用也同样如此。
  • 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 来创建这些更改的迁移
  • 运行 python manage.py migrate 将这些更改应用于数据库。

Django有单独的命令来制作和应用迁移的原因是因为您可以提交迁移到版本控制系统并将其发送到您的应用程序; 它们不仅可以使您的开发更容易,而且还可以被其他开发人员和生产中使用。

请阅读 django-admin文档,了解有关 manage.py 可以执行的操作的完整信息。


玩转API

现在,我们一起进入交互式的Python shell,并使用Django提供的免费API。要调用Python shell,请使用一下命令:

python manage.py shell

我们没有简单的输入python而是使用这个命令,因为manage.py设置了DJANGO_SETTINGS_MODULE环境变量,这给了Django提供了你的mysite/settings.py文件的Python导入路径。

绕过manage.py
如果你不想用manage.py也没问题, 只要将DJANGO_SETTINGS_MODULE
环境变量设置给mysite.settings,打开一个普通的python shell,然后设置Django:
>>>import django
>>>django.setup()

如果这引发了一个AttributeError,你也许用的Django版本不是1.11,请更换不同版本的教程或者升级你的Django。

你必须在manage.py所在的目录下运行Python,或者保证目录在Python路径上,import mysite才能正常工作。

更多相关信息,请参考django-admin documentation

进入了shell后,我们来看看database API:

>>> from polls.models import Question, Choice # 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. Note that this might say "1L" instead of "1", depending
# on which database you're using. That's no biggie; it just means your
# database backend prefers to return integers as Python long integer
# objects.
>>> 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>]>

等一下!<>完全是一个没有任何帮助的表达,让我们编辑问题模型(在polls/models.py文件中)来解决这个问题。然后添加__str__()方法到Question和Choice。

polls/models.py
from django.db import models
from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible # only if you need to support Python 2
class Question(models.Model): def __str__(self): return self.question_text

@python_2_unicode_compatible # only if you need to support Python 2
class Choice(models.Model): def __str__(self): return self.choice_text

__str__()方法的添加是很重要的,不光是为了你自己处理交互提示的方便,也是因为对象的展示是通过Django的自动生成admin的。

注意这些都是普通Python方法,现在我们添加一个自定义方法,为了演示使用。

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 datetime和django.utils import timezone可以分别在django.utils.timezone中引用Python的标准datetime模块和Django的时区相关实用程序。如果您不熟悉Python中的时区处理,您可以在时区文档.中了解更多信息。

保存这些更改并通过再次运行python manage.py shell启动一个新的Python shell:

>>> from polls.models import Question, Choice

# 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执行字段查找的更多信息,请参阅 Field lookups. 有关数据库API的完整详细信息,请参阅我们的 Database API reference.


Django Admin介绍

"Django的哲学"小课堂
Django管理站点是为你的员工或者客户的简单的添加,更改,删除这些繁琐工作而设计的。Django管理站点会完全自动生成模型的管理站。

Django当年创建于新闻编辑室,为内容管理而生的,“内容发布者”和“公共”网站之间的界限非常明确。网站管理人员使用该系统添加新闻故事,事件,体育比分等,该内容显示在公共站点上。 Django解决了为站点管理员创建统一界面来编辑内容的问题。

这个管理站点不打算由网站访问者使用。这是网站经理。

创建一个管理员用户

首先,我们需要创建一个用户来登录管理站点。使用下面的命令:
python manage.py createsuperuser
输入你想要的用户名
Username: admin
输入邮箱地址
Email address: admin@example.com
最后输入你的密码两次。
Password: **********
Password (again): *********
Superuser created successfully.

开启你的开发服务器

django管理站点会自动启动,我们只要启动开发服务器就可以来一起探索它了。
使用python manage.py runserver开启服务器。
现在打开一个浏览器,并且去/admin/-例如http://127.0.0.1:8000/admin/你应该会看到Django administration的登录界面。

进入admin管理站点。

推荐阅读更多精彩内容