让我们通过一个例子来学习。
贯穿这个教程,我们将带你了解一个基本的poll应用的创建。
它将由两部分组成:
我们将假设你已经安装了Django 。你可以通过如下的命令来判断Django是否安装,及安装的版本:
$ python -c "import django; print(django.get_version())"
如果Django已经安装,你应该能够看到你安装的版本。如果没有,你将得到一条错误消息“No module named django”。
这个教程是为Django 1.7和Python 3.2或更高的版本而写的。如果Django版本不匹配,你可以通过使用本页右下角的版本切换器,来参考你的Django版本的教程,或者将你的Django更新到最新版。如果你依然在使用Python 2.7,你将需要对示例代码做微小的调整,如注释里面描述的那样。
请参考 如何安装Django部分来了解如何移除老的Django版本并安装一个新的。
Where to get help:
如果在读这份教程时有任何问题,请post一个message给django-users,或顺便拜访#django on irc.freenode.net来与其他可能能够帮到你的Django用户交流。
如果这是你第一次使用Django,你将不得不留意一些初始的设置。即,你将需要自动生成一些代码来建立一个Django 工程——一个Django实体的一系列设置,包括数据库配置,Django特定的选项和特定于应用的设置。
由命令行,cd进入一个你想要在其中保存你的代码的目录,然后执行如下的命令:
$ django-admin.py startproject mysite
这将在你的当前目录下创建一个名为 mysite的目录。如果这个命令没有起作用,则请参考 Problems running django-admin.py。
注意
你需要避免将你的工程命名为Python或Django内建的组件名。特别地,这意味着你应该避免使用django之类的名字(这样的名字会与Django本身起冲突)或者test(这会与一个内建的Python package起冲突)。
你应该把这些代码放在哪儿?
如果你的技术背景主要在普通的老式的PHP(没有使用现代的框架),你可能习惯于把代码放在Web server的document root(在一个类似于/var/www的位置)。在Django里,你不要那样做。把这些Python 代码中的任何部分放在你Web server的document root都不是个好主意,因为这存在一定的风险,即人们有可能会通过web看到你的代码。那样对安全性不好。
把你的代码放在document root之外的一些目录,比如/home/mycode。
让我们看一下startproject创建了些什么东西:
mysite/ manage.py mysite/ __init__.py settings.py urls.py wsgi.py
与你看到的不符?
最近默认的工程布局变了。如果你看到的是一个“flat”布局(没有内部的mysite/目录),你可能正在使用一个与这一版教程不匹配的Django版本。你将需要切换到老的教程或更新的Django版本。
这些文件是:现在,编辑mysite/settings.py。它是一个普通的Python模块,拥有表示Django设置项的模块级变量。
默认情况下,配置使用SQLite。如果你是数据库新手,或者你只对使用Django有兴趣,则这是最简单的选择。Python中包含了SQLite,因而你不需要安装任何其它的东西来支持你的数据库。
如果你想要使用其它的数据库,则需要安装适当的database bindings,并在DATABASES 'default'项中修改如下的key来匹配你的数据库连接设置:
如果你不使用SQLIte作为你的数据库,则必须添加额外的设置,比如USER,PASSWORD,HOST。更多详情,请参考DATABASES文档。
Note注意
如果你使用PostgreSQL或MySQL,则要确保此时你已经创建了一个数据库。可以通过在你的数据库的交互提示符下输入“CREATE DATABASE database_name;”这样的命令来完成。
如果你使用SQLite,则事先不需要创建任何东西 - 数据库文件将在需要的时候自动创建。
当你编辑mysite/settings.py时,请将TIME_ZONE设置为你的时区。
也要注意文件顶部的INSTALLED_APPS设置。它包含了这个Django实例中已经激活的所有Django应用的名字。Apps可被用于多个工程,并且你可以打包并分发给其他的用户以应用于他们的工程。
默认情况下,INSTALLED_APPS包含了如下的apps,它们都来自于Django:
为了大多数情况的方便,这些应用默认都不被包含了进来。
这些应用中的一些至少使用了一个数据库表,因而我们需要在使用它们之前在数据库中创建表。要做到这些,则执行如下的命令:
$ python manage.py migrate
migrate命令查看INSTALLED_APPS设置,并根据你的mysite/settings.py文件的设置及伴随你app的数据库迁移(我们将在后面讨论)创建任何需要的数据库表。对于它应用的每一项迁移你都将看到有一条消息输出。如果感兴趣的话,则可以运行你的数据库的命令行客户端,并键入\dt (PostgreSQL),SHOW TABLES; (MySQL), 或 .schema (SQLite)来显示Django创建的表。
让我们来验证一下你的Django工程是否工作。如果还没有的话则切换目录到mysite的外层,然后执行下面的命令:
$ python manage.py runserver你将在命令行上看到如下的输出:
Performing system checks... 0 errors found January 07, 2015 - 15:50:53 Django version 1.7, using settings 'mysite.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
你已经启动的Django开发版服务器,一个完全用Python写的轻量级Web服务器。我们在Django中包含了它,以便于你可以快速地开发出东西来,而不是不得不处理一个产品级服务器——如Apache——的配置,直到你准备好了要正式上线。
现在是时候要注意一点了:不要在任何类似于一个生产环境的环境中使用这个服务器。它只是在开发时使用的。(我们的主业是创建Web框架,而不是web服务器。)
现在服务器运行起来了,通过你的Web浏览器来访问http://127.0.0.1:8000/。你将看到一个“Welcome to Django”页面,愉快的,淡蓝色的。它工作了!
改变端口
默认情况下,runserver命令在内部IP的8000端口启动开发版服务器。
如果你想要改变服务器的端口,则通过一个命令行参数来传递它。比如,这个命令在8080端口启动服务器:
$ python manage.py runserver 8080
如果你想要改变服务器的IP,则将它与端口号一起通过命令行参数传递。要在所有公共IPs上监听(如果你想要在别的计算机上炫耀你的成果时很有用),使用命令:
$ python manage.py runserver 0.0.0.0:8000
开发版服务器的完整文档可以在runserver参考中找到。
runserver的自动重新加载
如果有需要的话,开发版服务器自动地为每个请求重新加载Python代码。你不需要为了使修改的代码起作用而重新启动服务器。然而,某些行为,比如添加文件不触发一个重启,因而你将不得不在那些情况下重新启动服务器。
现在你的环境——一个“project”——建立起来了,可以开始工作了。
Django中你所写的每个应用由一个Python package及其某一convention组成。Django中带有一个实用工具来自动地产生一个app基本的目录结构,以便于你可以专注于写代码而不是创建目录。
Projects vs. apps
一个project和一个app的不同之处在哪里呢?一个app是一个Web应用,它做的事情是——比如,一个Weblog系统,一个公共记录的数据库或一个简单的poll app。一个project是一个配置及特定的Web站点的apps的集合。一个project可以包含多个apps。一个app可被用于多个projects中。
你的app可以放在你的Python path的任何位置。在这个教程中,我们将在你的manage.py文件所在的目录下创建我们的poll app,以便于它可以以它自己的顶级模块的形式被引入,而不是mysite的子模块。
要创建你的app,请确保你所在的目录与manage.py所在的目录相同,并键入下面的命令:
$ python manage.py startapp polls这将创建一个名为polls的目录,它的目录结构大体如下:
polls/ __init__.py admin.py migrations/ __init__.py models.py tests.py views.py
这个目录结构将放置poll应用。
用Django编写一个数据库Web app的第一个步骤是定义你的models——必不可少的,你的数据库的布局,及一些额外的元数据。
哲学
一个model是关于你的数据单独的,明确的数据源。它包含了必须的字段及你保存数据的行为。Django遵从DRY原则。目标是在一个地方定义你的数据模型,并自动地从它派生东西。
这包含migrations-不像在Ruby On Rails中,比如,migrations完全派生自你的models文件,它所必须的只是一个历史,Django可以跟踪来更新你的数据库schema来匹配你当前的models。
在我们简单的poll app中,我们将创建两个models:Question和Choice。一个Question具有一个问题及一个发布日期。一个Choice具有两个字段: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) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
这个代码很简单。每个model由一个继承自django.db.models.Model的类表示。每个model具有一定数量的类变量,其中的每一个表示model中的一个数据库字段。
每个成员由一个Field类的实例表示 – 比如,CharField表示字符字段,DateTimeField表示时间日期字段。这告诉Django每个字段的数据类型是什么。
每个Field实例的名字(比如question_text或pub_date)是字段的名字,以机器-友好的格式。你将在你的Python代码中使用这个值,并且你的数据库将把它用作列名。
你可以使用可选的第一个位置参数给一个Field来指定一个易读的名字。它被用于Django内部的两个部分,并且它还扮演文档的角色。如果这个字段没有提供,Django将使用机器-可读的名字。在这个例子中,我们只定义了一个易读的名字Question.pub_date。对于这个model中所有其他的字段,机器-可读的名字将是足够易读的名字。
有些Field类需要提供参数。比如CharField,需要你给出一个max_length。它不仅被用于数据库schema,而且用于校验,稍后我们将会看到。
一个Field也可以有各种各样的可选参数;在这个例子中,我们设置了votes的默认值为0。
最后,注意一个关系的定义,使用了ForeignKey。它告诉Django,每个Choice都与一个单独的Question关联。Django支持所有常见的数据库关系:多对一,多对多,和一对一。
那一小段 model的代码,却给了Django大量的信息。通过它,Django能够:
但是首先,我们要告诉我们的project,polls app安装了。
哲学
Django apps是“可插入的”:你可以在多个projects中使用一个app,你也可以分发app,因为它们不是一定要绑定到一个给定的Django安装的。
再次编辑mysite/settings.py文件,并修改INSTALLED_APPS设置,以包含字符串'polls'。它看起来就像这样:
mysite/settings.py
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'polls', )现在Django知道了要包含polls app了。让我们运行另一个命令:
$ python manage.py makemigrations polls你应该看到像下面这样的一些东西:
Migrations for 'polls': 0001_initial.py: - Create model Question - Create model Choice - Add field question to choice
通过运行makemigrations,你告诉Django,你已经对你的models做了一些改变(在这个例子中,你创建了一些新的models),并且你希望这些改变被存储为一个migration。
Migrations是Django如何为你的models存储改动(及你的数据库schema)- 它们仅仅是磁盘上的文件。如果你喜欢的话,你可以为你的新model读取migration;它是文件polls/migrations/0001_initial.py。不用担心,你不会被期望去读Django创建的每一个migration,但它们被设计为是人类可编辑的,以防你想要手动的微调Django要如何做改动。
有一个命令将为你执行migrations,并自动地管理你的数据库schema - 那被称为migrate,我们将稍后再回到这个上面 - 但首先,让我们看一下migrations将执行什么SQL。sqlmigrate命令接收migration名字作为参数,并返回它们的SQL:
$ python manage.py sqlmigrate polls 0001
你应该能够看到一些类似于下面这些的东西(我们已经为了可读性而重新格式化它了):
BEGIN; 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 TABLE polls_choice ( "id" serial NOT NULL PRIMARY KEY, "question_id" integer NOT NULL, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL ); 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;
注意如下的这些:
如果你感兴趣,你也可以运行python manage.py check;这将在不进行migrations或操作数据库的情况下检查你的工程中的任何问题。
现在,来再次执行migrate,以在你的数据库中创建那些model表:
$ python manage.py migrate Operations to perform: Synchronize unmigrated apps: sessions, admin, messages, auth, staticfiles, contenttypes Apply all migrations: polls Synchronizing apps without migrations: Creating tables... Installing custom SQL... Installing indexes... Installed 0 object(s) from 0 fixture(s) Running migrations: Applying polls.0001_initial... OK
migrate命令提取所有的还没有应用的migrations(Django会使用你的数据库中一个称为django_migrations的特别的表来追踪哪些已经应用了)并在你的数据库上执行它们 - 本质上,同步你对你的models所做的改动与你的数据库中的schema。
Migrations非常强大,它是你能够随着时间逐渐开发你的工程而改变你的models,而不需要删除你的数据库或表并创建新的 - 它专门更新你的数据库,而不会丢失数据。在这份教程的后面我们将更深入的描述它们,但现在,记住三步指南来修改models:
使用分开的命令来创建和应用migrations的原因是因为,你将提交migrations到你的版本控制系统,并伴随着你的app一起分发;它们不止使你的开发更简单,他们也可以被其它开发者或产品使用。
参考django-admin.py documentation来了解manage.py实用工具所能做的事情的完整信息。
现在,让我们进入交互式Python shell,并来自由地玩一下Django给你提供的API。要调用Python shell,使用这个命令:
$ python manage.py shell
我们使用这个命令,而不是简单地键入 “python”,因为 manage.py设置了 DJANGO_SETTINGS_MODULE环境变量,其为Django提供指向了你的 mysite/settings.py文件的Python import path。
绕过 manage.py
如果你不愿意使用manage.py,没问题。仅仅设置DJANGO_SETTINGS_MODULE环境变量为mysite.settings,启动一个plain Python shell,然后设置Django:
>>> import django >>> django.setup()
如果这个地方产生了一个AttributeError,那么你可能使用了一个与这份教程不匹配的Django版本。你可能想要切换到更老版本的教程或更新版本的Django。
你必须在manage.py所在的目录运行python,或者确保那个目录在Python path中,以便于导入mysite能够成功。
关于这些的更多信息,请参考django-admin.py 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() [] # 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() [<Question: Question object>]
等一下。<Question: Question object>是,完全的,一个对于这个对象没有任何帮助的描述。让我们通过编辑Question model (在polls/models.py文件中)并给Question和Choice都添加__str__()方法来解决它:
from django.db import models class Question(models.Model): # ... def __str__(self): # __unicode__ on Python 2 return self.question_text class Choice(models.Model): # ... def __str__(self): # __unicode__ on Python 2 return self.choice_text为你的models添加__str__()方法很重要,不仅仅是在处理交互提示时为了你自己而显得明智,而且对象的描述Djang也会被用于Django自动产生的admin。
__str__还是__unicode__?
在Python 3中,它很简单,只是使用__str__()就行。
在Python 2上,你应该定义__unicode__()方法来返回unicode值。Django models有一个默认的 __str__()方法,它调用__unicode__()并将结果转换为一个UTF-8字节串。这意味着unicode(p)将返回一个Unicode字符串,而str(p)将返回一个字节串,其字符都以UTF-8编码了的。Python执行相反的:object有一个__unicode__,它调用__str__,并将结果解释为一个ASCII字节串。这点不同可能会造成一些困扰。
如果这些有点乱,则只使用Python 3。
注意,这些是普通的Python方法。让我们添加一个定制的方法,仅仅为了演示: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和from django.utils import timezone,分别引用了Python的标准datetime模块,及django.utils.timezone中的Django的时区相关实用程序。如果你对Python中的时区处理不熟悉,你可以在time zone support docs中学到更多东西。
保存这些改动,并通过再次运行python manage.py shell启动一个新的Python 交互式 shell:
>>> from polls.models import Question, Choice # Make sure our __str__() addition worked. >>> Question.objects.all() [<Question: What's up?>] # Django provides a rich database lookup API that's entirely driven by # keyword arguments. >>> Question.objects.filter(id=1) [<Question: What's up?>] >>> Question.objects.filter(question_text__startswith='What') [<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() [] # 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() [<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) [<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()
关于model relations的更多信息,请参考Accessing related objects。关于如何通过API使用双下划线执行字段查找,请参考Field lookups。关于数据库API的完整详细信息,请参考我们的Database API reference。
When you’re comfortable with the API, read part 2 of this tutorial to get Django’s automatic admin working.
原文地址:https://docs.djangoproject.com/en/1.7/intro/tutorial01/