建议直接阅读末尾!!!
Writing your first Django app, part 2
本节将设置数据库,创建您的第一个模型(model),并简单介绍Django自动生成的管理页面。
数据库设置
现在,打开mysite/settings.py。这是一个普通的Python模块,代表Django配置的模块级变量。
默认情况下,配置使用SQLite。如果你初涉数据库,或者只想尝试Django,这是最简单的选择。SQLlite包含在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。
还需要注意在该文件顶部的INSTALLED_APPS配置。它包含了在该Django实例中激活的所有Django应用的名称。Apps(应用)可以被多个项目使用,你可以打包并发布给其他项目。
默认情况下,INSTALLED_APPS包含以下应用,这些应用随Django一起提供:
- django.contrib.admin -- 管理站点。你会很快用到它。
- django.contrib.auth -- 认证系统。
- django.contrib.contenttypes -- 内容类型的框架。
- django.contrib.sessions -- 会话框架。
- django.contrib.messages -- 消息框架。
- django.contrib.staticfiles -- 管理静态文件的框架。
这些应用一般情况下被默认包含在内。
其中一些应用要使用至少一张数据表,所以我们在使用它们之前需要创建一张数据表。为此,运行一下命令:
$ python manage.py migrate
migrate命令会查看INSTALLED_APPS配置,并根据mysite/setting.py文件中的数据库配置和应用程序随附的数据迁移来创建所有必需的数据库(稍后介绍)。你将看到每个数据迁移的信息。如果你有兴趣,您可以在数据库的命令行客户端输入\dt(PostgreSQL), SHOW TABLES;(MySQL), .schema(SQLite) 或者 SELECT TABLE_NAME FROM USER_TABLES;(Oracle)来显示Django创建的数据表。
创建模型(model)
现在我们通过使用额外的元数据来来定义自己的模型(本质上就是数据库的布局)。
在我们的poll应用中,我们将创建两个模型:Question和Choice。每个Question有一个问题和一个发布日期。每个Choice有两个字段:选择文本和投票计数器。每个Choice都和一个Question相关联。
这些概念由简单Python类表示,编辑polls/models.py文件:
1 # polls/models.py 2 3 from django.db import models 4 5 6 class Question(models.Model): 7 question_text = models.CharField(max_length=200) 8 pub_date = models.DateTimeField('date published') 9 10 11 class Choice(models.Model): 12 question = models.ForeignKey(Question, on_delete=models.CASCADE) 13 choice_text = models.CharField(max_length=200) 14 votes = models.IntegerField(default=0)
代码很简单。每个模型都由一个继承自django.db.models.Model的子类表示。每个模型中都有一些类的变量,每个模型中的变量代表一个数据库字段。
每个字段由由Field类的实例表示,例如,CharField用于字符字段,DateTimeField用于时期。这告诉Django每个字段代表什么类型的数据。
每个Field实例的名称(例如question_text, pub_date)就是字段的名称(以机器友好的格式书写)。你将在Python代码中使用这些值,你的数据库将使用它作为列名称。
你可以使用可选的第一个位置参数来给Field指定一个方便人理解(human-readable)的名称。这用于Django的几个内省的部分,它可以作为文档。如果该字段没有提供,Django将使用机器刻度(machine-readable)的名称。在本例中,我们只为Question.pub_date字段定义了人类的可读(human-readable)的名称。本例中的其他字段,字段的机器名称足够作为人类可读的名称。(翻译完这段感觉自己已经成仙了/(ㄒoㄒ)/~~)
一些Field类有必要的参数。例如CharField,要求给它一个max_length。这不仅在数据库架构中使用,而且用于验证,我们即将看到。
Field也可能存在一些可选参数,本例中,我们为votes设置了default值为0。
最后,注意一个关联的定义,使用ForeignKey。它告诉Django每个Choice都与一个Question关联。Django支持所有常见的数据库关联:多对一,多对多,一对一。
激活模型
那个小的模型代码给了Django很多信息。有个它,Django可以:
- 为这个应用(app)创建数据库(CREATE TABLE语句);
- 创建用于访问Question和Choice的数据库访问API。
但是,首先我们需要告诉我们的项目,polls应用已安装。
(提示:Django的应用是“可插拔”的:你可以将一个app使用在多个项目中,也可以发布你的apps,因为它们并不予给定的Django安装绑定!)
来将应用包含到我们的项目中,我们需要在INSTALLED_APPS配置中添加对其配置类的引用。本例的配置类即polls/apps.py文件中的PollsConfig类,所以它的引用路径为"polls.apps.PollsConfig"。编辑mysite/settings.py文件,添加该路径到INSTALLED_APPS配置中。
1 # mysite/settings.py 2 3 INSTALLED_APPS = [ 4 'django.contrib.admin', 5 'django.contrib.auth', 6 'django.contrib.contenttypes', 7 'django.contrib.sessions', 8 'django.contrib.messages', 9 'django.contrib.staticfiles', 10 'polls.apps.PollsConfig', 11 ]
(简单写法,可以直接添加应用名即"polls",因为配置类PollsConfig中定义了"name='polls'")
现在Django知道已包含了polls应用。让我们运行另一个命令:
$ python manage.py makemigrations polls
你可以看到类似下面的内容:
Migrations for 'polls': polls/migrations/0001_initial.py: - Create model Choice - Create model Question - Add field question to choice
通过运行makemigrations,你告诉Django,你已经对模型进行了一些修改(在这种情况下,你已经创建了新的模型),并希望将这些修改存储为一个迁移(migration)。
Migration(迁移)是“Django如何修改你的模型(以及数据库结构)” --它们只是磁盘上的文件。如果你喜欢,可以阅读新模型的迁移,即文件polls/migrations/0001_initial.py。不要担心,Django创建该文件时你不需要去一一阅读,但是它们被设计成可编辑的,你可以手动调整Django修改数据的行为。
有一个命令会运行你的migration并自动管理你的数据库结构 -- 调用migrate命令,稍后介绍该命令。首先,让我们来看看migration会运行哪些SQL语句。sqlmagrate命令接受应用名和migration名称,并返回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生成。(这个文档有坑啊,不是说好的用SQLite吗)。
- 表名是结合应用名称和小写的model名称(question和choice)自动生成的。(你可以重写这个行为)
- 主键(ID)将自动添加。如果你在编写model的时候已经添加了主键,就不会自动生成主键ID了。(这个也可以重写)
- 按照管理,Django会追加"_id"给外键字段名。(你猜我要说什么?是的,这个还是可以重写)
- 外键关联由FOREIGN KEY显式声明。不要担心DEFERRABLE部分;它只是为了告诉PostgreSQL不要在事务结束前执行外键。
- 它会适应你使用的数据库,所以数据库的一些特殊字段类型(例如auto_increment--MySQL, serial -- PostgreSQL, integer primary key autoincrement -- SQLite)会自动为你处理。同样适用于对字段名的引用,例如使用双引号或者单引号。
- sqlmigrate命令实际上不会在数据库上运行migration命令,它只是将Django认为需要执行的SQL语句打印到屏幕上,以便你能看到。这对于检查Django将要执行的操作非常有用 or if you have database administrators who require SQL scripts for changes.(这一句不太懂,先上原文)
如果你有兴趣,可以执行python manage.py check;它会检查项目中的所有问题,而不会执行迁移(migration)或创建数据库。
现在,再次执行migrate,在你的数据库中创建这些模型(model)的表:
$ 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命令使用所有未执行的migration(Django会调用django_migrations跟踪哪个申请使用数据库中的特殊表)并根据数据库运行它们 -- 本质上,将你对模型(model)做的修改同步到数据库的结构中。
Migration非常强大,在开发项目的过程中,使你可以随着时间的推移来修改模型,而不需要删除数据库或表再创建一个新的 -- 它专门用来实时更新数据库,而不会丢失数据。我们将在本教程的后续部分中更深入地介绍它们,但是现在,请记住进行模型更改的三步指南:
- 修改你的模型(在models.py文件中)
- 执行python manage.py makemigrations 为这些修改创建迁移
- 执行 python manage.py migerate 将这些修改应用到数据库中。
将创建和应用migration分为两个命令的原因是你将提交migration到版本控制系统并发送到你的应用;它们不仅可以使您的开发更容易,而且还可以被其他开发人员和在生产中使用。
阅读django-admin documentation了解有关manage.py实用程序可执行操作得当详细信息。
使用API
现在,让我们进入交互式的python shell,并使用Django提供的免费API。要调用Python shell,请使用以下命令:
$ python manage.py shell
我们使用这个,而不是简单的输入"python",因为manage.py设置了DJANGO_SETTING_MODULE环境变量,它告诉Django mysite/settings.py文件的python导入路径。
一旦你进入shell,请探索database API:
>>> from polls.models import Question, Choice # 导入我们刚写的模型 # 系统中还没有“问题” >>> Question.objects.all() <QuerySet []> # 创建一个新“问题” # 配置文件中默认启动对时区的支持, 所以Django需要提供一个tzinfo的日期给pub_date.使用timezone.now() # 而不是datetime.datetime.now() >>> from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) # 将对象保存到数据库中。你必须显式调用save()
>>> q.save() # 现在它有一个ID,注意,你可能说是‘1L’而不是‘1’,这取决于你使用的数据库。这意味着你的数据库后端更喜欢返回
# 整数作为Python的长整型对象。 >>> q.id 1 # 通过Python属性访问模型字段值.
>>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>) # 通过更改属性来更改值,然后调用save().
>>> q.question_text = "What's up?" >>> q.save() # oobjects.all()显示数据库中的所有问题.
>>> Question.objects.all() <QuerySet [<Question: Question object>]>
等一下,<Question: Question object>是对该对象完全无毛用的表示。让我们通过编辑Question模型(在polls/models.py文件中),并在Question和Choice中添加__str__()方法:
1 # polls/models.py 2 3 from django.db import models 4 from django.utils.encoding import python_2_unicode_compatible 5 6 @python_2_unicode_compatible # only if you need to support Python 2 7 class Question(models.Model): 8 # ... 9 def __str__(self): 10 return self.question_text 11 12 @python_2_unicode_compatible # only if you need to support Python 2 13 class Choice(models.Model): 14 # ... 15 def __str__(self): 16 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 datettime 和 from django.utils import timezone,它们分别参考Python标准库的datetime模块和Django中django.utils.timezone的时区关联工具。如果您不熟悉Python中的时区处理,您可以在时区支持文档中了解更多信息。
保存这些更改并重新启动一个新的Python交互式shell :python manage.py 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执行字段查找的更多信息,请参阅字段查找。有关数据库API的完整详细信息,请参阅我们的数据库API参考。
本人写这篇翻译是英语实在吃力,阅读原文档的时候很费劲,回看下内容更麻烦,所以想着一边看以便翻译出来,最后发现。。效率好低呀,这点东西竟然写了十几个小时,对本人英语水平默哀三分钟。。。
所以我只写了这么断章的一篇,不过我发现了一个不错的博客,这位大神已经把入门的部分都翻译出来了,所以我就没有然后了