创建你的第一个Django app-Part 1
- 让我们从简单的例子开始吧。
- 通过这个教程,我们将快速学习投票(poll)应用的创建。
- 环境:windows 8 32 bits + Python 2.7.5 + Django 1.7.3
- 它包含两个部分:
- 一个供人们查看和进行投票的公开的站点。
- 一个管理员站点,可以让你进行添加,修改和查询投票。
- 我们假设你已经装好了Django.你可以通过以下命令查看你安装的版本。
1
$ python -c "import django; print(django.get_version())"
- 如果你成功安装了Django,你就会看到版本号,否则,你就会得到一个错误信息:”No module named django”.
- 我的Django信息如下:
创建项目
- 如果你是第一次使用Django,那么你应该注意一些初始化的设置。也就是说,你需要一些自生成的代码来构建一个Django项目,包括Django实例的设置文件,譬如数据库配置文件,Django指定选项和应用指定的设置。
- 从命令行中切换到你想把项目存储的目录,然后输入以下命令:
1
$ django-admin.py startproject mysite
- 在当前目录下就会生成一个mysite的目录。如果没有,请查看问题反馈
笔记
请注意命名,不能和Python或者Django组件中的关键字重名,特别的,你不能使用django或者test。
- 通过startproject命令创建的目录如下:
和你建的目录不同?
默认项目布局最近有所改变。如果你看到一个’flat’布局(里面没有mysite/目录),你可能使用的Django和本教程的版本不同。请移步。
- 目录路径解释:
- 外层mysite/:你的项目的根目录。它的名字可以随意更改。
- manage.py文件:它是一个和Django进行多样化交互的命令行工具。你可以从以下链接查看详细内容django-admin.py and manage.py
- 内层mysite/:它是项目的真正的python包。名字不可随意更改,因为它是在其他地方可供导入的(例如:mysite.urls)
- mysite/init.py:一个空文件,它的作用是标注自己是一个python包。
- mysite/settings.py:Django项目的设置或配置。详细可查看Django settings
- mysite/urls.py:对Django项目的URL声明,就像是你的Django网站的目录列表。详细可查看URL dispatcher
- mysite/wsgi.py:它是配置WSGI服务器的入口点。详细可见:How to deploy with WSGI
数据库配置
现在,编辑mysite/settings.py文件。它是一个普通的Python模块,用模块级变量表示Django设置。
默认情况下,配置是用SQLite。如果你不熟悉数据库,或者你只想玩玩Django,这是最快捷的选择。SQLite包含在Python中,所以不需要你安装。
如果你想用其他数据库,安装相应数据库绑定,然后修改DATABASES中的默认选项来匹配你的数据库连接设置:
- ENGINE-数据库引擎,SQLite(默认):**’django.db.backends.sqlite3’;postgresql:‘django.db.backends.postgresql_psycopg2’;mysql:‘django.db.backends.mysql’**;Oracle: **’django.db.backends.oracle’**,或其他。
- NAME-数据库名称。如果你用SQLite,那么他是电脑上的一个文件,这样的话,NAME应该是一个绝对路径,包括文件名。默认值是**os.path.join(BASE_DIR, ‘db.sqlite3’)**,它是存储在项目路径下的文件。
如果你不用SQLite,那么你需要添加USER,PASSWORD,HOST。详细可见:DATABASES。
笔记
如果你在使用PostgreSQL或者MySQL,你必须要创建好数据库。通过命令**CREATE DATABASE database_name;**。
如果你在使用SQLite,那么它会自动创建,必须手动创建。
当你在编辑mysite/settings.py时,请把TIME_ZONE设置为自己的时区。
另外,文件开头的INSTALLED_APPS配置,它Django实例中被激活的Django应用的名字。Apps可以被多个项目使用,你可以把Apps打包和发布给其他项目使用。
默认情况下,INSTALLED_APPS配置包含Django中的apps,如下:
- django.contrib.admin-管理员站点,你在Part2中会用到。
- django.contrib.auth-认证系统
- django.contrib.contenttypes-一个内容类型的框架
- django.contrib.sessions-session框架
- django.contrib.messages-messaging框架
- django.contrib.staticfiles-管理静态文件的框架
这些apps默认添加的,你可以根据自己需求修改。
这些apps中可能要用到数据库表,所以,我们应该先要在数据库中创建表,然后再使用。命令如下:
1
$ python manage.py migrate
migrate命令会查看INSTALLED_APPS配置,然后根据mysite/settings.py文件创建一些需要的数据库表,并把数据库迁移到相应的应用中。n你可以查看信息:运行数据库客户端,在命令行中输入\dt (PostgreSQL), SHOW TABLES; (MySQL), or .schema (SQLite)。
发布到服务器
- 验证你的项目是否可运行,在外层mysite目录下,运行一下命令:
1
$ python manage.py runserver
- 你可以看到以下输出(我的版本如下):
1
2
3
4January 16, 2015 - 14:18:47
Django version 1.7.3, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK. - 你已经把项目成功发布到服务器上了,它是一个用纯python实现的轻量级的Web server。它已经被包含到Django当中,方便我们进行快速的开发。
- 它不可以用于商用,因为能力有限。我们是开发框架而不是服务器。
- 现在打开浏览器,输入”127.0.0.1:8000/“,你就可以访问到你的项目页面了。效果如下:
修改端口
默认端口是8000,如果你想修改,可以按如下格式发布项目
1 | $ python mange.py runserver 8080 |
如果你想修改服务器的IP,如下格式:
1 | $ python manage.py runserver 0.0.0.0:8000 |
详细文档参考runserver
服务器自动加载,不用重新发布项目。
创建模型(models)
现在你项目的基本环境已经搭建好,你可以开始做事了。
在Django中创建的app都有相同的目录结构,但是Django已经用过工具自动生成了,而不必你操心,你只要专注于写代码就好了。
Projects VS apps
项目和app之间有什么不同呢?一个app表示网站程序中的一个功能,譬如,一个网路博客系统,一个公开记录或公共投票的数据库应用。而一个项目(project)往往是app的集合,几个app组成一个project,另外,一个app可以被多个项目包含。如果你的python环境已经配置好,那么你的app可以在任何地方创建。这里我们把polls应用创建在manage.py的旁边,所以,它可以被当成自己的顶级模块导入,而不是mysite的子模块。
创建app,请到manage.py所在目录下,执行命令:
1
$ python manage.py startapp polls
创建了一个polls目录,结构如下:
这个目录包含polls应用。
通过Django写数据库网页应用的第一步是定义自己的模块(models)-主要的是,你的数据库布局,加上额外的元数据。
哲学
一个模型是一个独立的,明确的数据资源。它包含主要的字段和你存储的数据的行为。Django遵循DRY原则。目的是从一个地方定义和导出数据。
还包括迁移,不像Ruby On Rails,例如,模型中的数据可以全部迁移,但它只不过是Django中的一个历史记录,可以随时回滚到当前适应的模型。
- 在简单的投票(polls)应用中,我们创建2个模型:Question和Choice。Question有一个问题和发表日期。Choice有两个域:选择文本和投票计数器。每一个Choice关联一个Question。
- 这些概念都能通过简单的Python类实现。编辑polls/models.py文件,如下:
- polls/models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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) - 代码非常简单明了。每个模型都是继承类**django.db.models.Model。每个模型都有一些类变量,其中每个变量都代表数据库中的一个字段**。
- 每个字段都是一个字段(Field)类的实例–譬如,CharField代表字符域,DateTimeField代表数据库实时间。这样Django就知道每个字段是什么类型的。
- 每个域实例的名字(譬如question_text或者pub_date)就是字段名称,它是机器能够识别的形式。字段的值会用python代码表示,数据库就会把字段名称当成一个列名。
- 你还可以为每个字段起一个易于让人读懂的名字。在Django中是支持的,而且也可以记录到文档中。如果你美誉定义这个名字,那么Django就会用机器识别的名字。例如,我们只为Question.pub_date定义一个易于让人都懂的名字。对于模型中的其他字段,机读和人读的名字是一样的。
- 一些字段类中有写必需的参数,例如CharField,我们必须给它指定一个最大长度**(max_length)**。它不仅仅需要数据库模式,还需要验证的,后面我们就会将到。
- 一个字段还可以有多个可选的参数,譬如,我们把vote的默认值设为0.
- 最后,要定义一个关系,用ForeignKey,即是外键。它向Django表明每个Choice关联一个Question。Django支持多种数据库映射关系:多对一,多对多,一对一。
激活模型(Activating models)
上面一小段代码给了Django很多的信息。通过代码,Django可以做到:
- 为这个app创建一个数据库模式(CREATE TABLE 语句)
- 创建一个Python数据库可用API,用于使用Question和Choice对象。
但是我们先要告知项目polls已经安装了。
哲学
Django的app是可插的(像USB一样,适配多台电脑):你的app可以插入到多个项目当中,你也可以发布app,这样它(app)就不用绑定到Django中用于安装了。再一次编辑mysite/settings.py文件,然后修改INSTALLED_APPS的内容,让它包含’polls‘,修改后的文件是:
mysite/settings.py
1
2
3
4
5
6
7
8
9INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'polls',
)现在Django知道应该包含polls这个app了。让我们运行另外一个命令:
1
$ python manage.py makemigrations polls
你应该看到以下的输出:
1
2
3
4
5
6Migrations for 'polls':
0001_initial.py:
- Create model Choice
- Create model Question
- Add field question to choice通过makemigrations口令,你在告诉Django你的模型有了修改(这样,你就新建了一个模型),你希望修改转存的过程当成迁移。
Migrations记录着你对模型做了什么修改(也即是数据库模式)-它们只是硬盘上的文件。你可以读取你新模型的migration如果你喜欢,它保存在polls/migrations/0001_initial.py文件当中。不要担心,你不需要每当Django创建一个的时候就读取一次,但它还是可编辑的,当你想对它进行修改的时候。
有一个口令可以帮你运行migration并且自动管理你的数据库模式,它是migrate,我们就要将到它了,但首先,让我们看一下migration会运行什么样的SQL代码。sqlmigrate口令得到migration的名字并且返回它们的SQL。
1
$ python manage.py sqlmigrate polls 0001
你应该会看到以下相似的代码:(我们已经把代码格式化了,为了让我们更易读懂它)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22BEGIN;
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;注意一下内容:
- 确切的输出会和你用的数据库而有所不同。上面的例子是用PostgreSQL生成的。
- 表名是自动取app的名字加上模型的名字(小写格式),如polls_question和polls_choice。(你可以自己更改)
- 主见(IDs)是自增长的,你也可以自行修改。
- 按照惯例,Django会在外键字段名字后面加上**_id**,(当然这个你也可以修改)。
- 外键关系是通过FOREIGN KEY这个约束来清晰定义的。不要担心不同的部分,它仅仅是告知PostgreSQL在结束事务之前不要强行实施外键。
- 它是根据你用的数据库来适配的,所以数据库指定的域类型,好像,自动适配auto_increment(MySQL),serial(PostgreSQL),或者integer primary key autoincrement(SQLite),给字段名加引号是一样的,加单引号或双引号。
- sqlmigrate口令并不会真正在你的数据库上运行migtaration-只是把它打印到屏幕上,让你知道Django需要什么SQL。知道Django正在做什么或者你拥有可以修改SQL资格的数据库管理员是非常有用的。
如果你有兴趣,你也可以运行python manage.py check口令,它会检测你的项目是否有问题或者创建数据库是否顺利。
现在,再次运行migrate来创建你数据库上的模型:
1
2
3
4Operations to perform:
Apply all migrations: admin, contenttypes, polls, auth, sessions
Running migrations:
Applying polls.0001_initial... OKmigrate口令处理所有那些没被处理的migrations。(Django通过你数据库当中的特殊表django_migrations来跟踪那些被处理了或者那些没有处理)
migrations非常强大,而且当你在开发你的项目时,它可以让你随着时间变化修改你的模型,而不需要删除当前的数据库去创建一个新的。我们稍后再对这部分做深入研究,现在,请记住以下三步来对你的模型进行修改:
- 修改模型(在models.py文件中)
- 运行python manage.py makemigrations来为这些修改创建migrations
- 运行python manage.py migrate把这些修改应用(apply)到数据库
为什么make和apply这两步要分开执行呢?原因是你要首先提交migrations到你的版本控制系统,然后在把它们和你的app一起执行。这样会不仅会令你的开发更容易,还便于其他的开发者使用和再次开发。
阅读django-admin.py documentation得到manage.py工具类的更详细的信息。
玩转API
- 现在,让我们投进可交互的Python shell,玩一下Django提供给我们的免费的API。进入Python Shell,用这个命令:
1
$ python manage.py shell
- 我们用python manage.py而不用python的原因是manage.py设置了DJANGO_SETTINGS_MODULE的环境变量,也就是说,Django和python的路径都加入了你的mysite/settings.py文件中。
跳过manage.py(Bypassing manage.py)
如果你不想使用manage.py,没问题,只需要把DJANGO_SETTINGS_MODULE的环境变量设置到mysite.settings中,新开一个纯净的Python shell,然后设置Django:
1 | import django |
如果这样报了一个AttributeError错误,那么你现在使用的是一个不匹配的Django版本。请更换不同的Django版本。
你必须在manage.py的目录下运行python,或者确保你的目录加入了Python路径,这样import mysite才起作用。
更多详情,请看django-admin.py documentation
当你进入了shell,可以探索[database API](database API:)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
In [1]: from polls.models import Question, Choice # Import the model classes we just wrote
# No questions are in the system yet.
In [2]: Question.objects.all()
Out[2]: []
# Create a new Question.
# Support for time zones is enabled in the default settings file, so Django experts a datatime with tzinfo for
# pub_date.Use timezone.now()
# Instead of datatime.datetime.now() and it will do the right thing.
In [3]: from django.utils import timezone
In [4]: q = Question(question_text="What's new?", pub_date = timezone.now())
# Save the object into the database. You have to call save() explicitly.
In [5]: 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 returns to return integers as Python long integer objects.
In [6]: q.id
Out[6]: 1
# Access model field values via Python attributes.
In [7]: q.question_text
Out[7]: "What's new?"
In [8]: q.pub_date
Out[8]: datetime.datetime(2015, 1, 18, 2, 22, 53, 431000, tzinfo=<UTC>)
# Change values by changing the attributes, then calling save().
In [9]: q.question_text = "What's up?"
In [10]: q.save()
# objects.all() displays all the questions in the database.
In [11]: Question.objects.all()
Out[11]: [<Question: Question object>]等一下,**<Question: Question object>似乎对这个对象来说表现很差。我们可以这样来修正它,修改Question模型(在polls/models.py文件中),为Question和Choice分别添加一个unicode**方法(就像java中重写toString()方法一样)。
polls/models.py
1
2
3
4
5
6
7
8
9
10
11from django.db import models
class Question(models.Model):
# ...
def __unicode__(self): # __str__ on Python 3
return self.question_text
class Choice(models.Model):
# ...
def __unicode__(self): # __str__ on Python 3
return self.choice_text为你的模型添加__unicode__()方法非常重要,不仅仅方便于你和Python进行交互,还方便通过Django自动生成的管理员对对象的展现。
str()方法还是__unicode__()方法
在Python 3,就简单的用**str()方法
在Python 2中,你应该定义unicode()方法返回的是unicode值。Django模型有一个默认的str()方法,它调用了unicode()方法并且把结果转化成了一个UTF-8字节型字符串。也就是说unicode(p)方法返回的是一个Unicode字符串,而str(p)返回的是一个字节型的字符串,编码格式是UTF-8。Python刚好相反:object有一个unicode()方法,它调用了str()**方法,并把结果解释成一个ASCII的字节型的字符串。这些不同很容易混淆。
如果你觉得以上内容很混乱,那请直接用Python 3吧。看完了一般的Python方法,让我们添加一个自定义的方法,说明一下:
polls/models.py
1
2
3
4
5
6
7
8
9
10import 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交互:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75from polls.models import Question, Choice
# Make sure our __str__() addition worked.
all() Question.objects.
[<Question: What's up?>]
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
filter(id=1) Question.objects.
[<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.
id=2) Question.objects.get(
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).
1) Question.objects.get(pk=
<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.
1) q = Question.objects.get(pk=
# Display any choices from the related object set -- none so far.
all() q.choice_set.
[]
# Create three choices.
'Not much', votes=0) q.choice_set.create(choice_text=
<Choice: Not much>
'The sky', votes=0) q.choice_set.create(choice_text=
<Choice: The sky>
'Just hacking again', votes=0) c = q.choice_set.create(choice_text=
# 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).
filter(question__pub_date__year=current_year) Choice.objects.
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
# Let's delete one of the choices. Use delete() for that.
filter(choice_text__startswith='Just hacking') c = q.choice_set.
c.delete()关于更多信息,请看
[Accessing related objects](Accessing related objects),
如果你熟悉了API,那么请读下一篇Part2-Django’s automatic admin working.
- 本文标题:Django-1.7.3-Tutorial-Part-1-模型
- 创建时间:2015-01-16 10:46:52
- 本文链接:2015/01/16/技能-修行-进步-Django/Django-1-7-3-Tutorial-Part-1-模型/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!