或者http://docs.30c.org/djangobook2/chapter01/html
django-admin.py startproject mysite命令建立一个目录,包含4个文件:python
mysite/ __init__.py manage.py settings.py urls.py
文件以下:mysql
__init__.py :让 Python 把该目录当成一个开发包 (即一组模块)所需的文件。 这是一个空文件,通常你不须要修改它。web
manage.py :一种命令行工具,容许你以多种方式与该 Django 项目进行交互。 键入python manage.py help,看一下它能作什么。 你应当不须要编辑这个文件;在这个目录下生成它纯是为了方便。正则表达式
settings.py :该 Django 项目的设置或配置。 查看并理解这个文件中可用的设置类型及其默认值。sql
默认状况下, runserver 命令在 8000 端口启动开发服务器,且仅监听本地链接。 要想要更改服务器端口的话,可将端口做为命令行参数传入:shell
python manage.py runserver 8080
经过指定一个 IP 地址,你能够告诉服务器–容许非本地链接访问。 若是你想和其余开发人员共享同一开发站点的话,该功能特别有用。 `` 0.0.0.0`` 这个 IP 地址,告诉服务器去侦放任意的网络接口。数据库
python manage.py runserver 0.0.0.0:8000
Hello world视图很是简单。 这些是完整的函数和导入声明,你须要输入到views.py文件:django
from django.http import HttpResponse def hello(request): return HttpResponse("Hello world")
咱们逐行逐句地分析一遍这段代码:编程
首先,咱们从 django.http 模块导入(import) HttpResponse 类。参阅附录 H 了解更多关于 HttpRequest 和 HttpResponse 的细节。 咱们须要导入这些类,由于咱们会在后面用到。
接下来,咱们定义一个叫作hello 的视图函数。
每一个视图函数至少要有一个参数,一般被叫做request。 这是一个触发这个视图、包含当前Web请求信息的对象,是类django.http.HttpRequest的一个实例。在这个示例中,咱们虽然不用request作任何事情,然而它仍必须是这个视图的第一个参数。
这个函数只有简单的一行代码: 它仅仅返回一个HttpResponse对象,这个对象包含了文本“Hello world”。
默认的URLconf包含了一些被注释起来的Django中经常使用的功能,仅仅只需去掉这些注释就能够开启这些功能. 下面是URLconf中忽略被注释的行后的实际内容
from django.conf.urls.defaults import * urlpatterns = patterns('', )
让咱们逐行解释一下代码:
第一行导入django.conf.urls.defaults下的全部模块,它们是Django URLconf的基本构造。 这包含了一个patterns函数。
若是想在URLconf中加入URL和view,只需增长映射URL模式和view功能的Python tuple便可. 这里演示如何添加view中hello功能.
from django.conf.urls.defaults import *#其实只用到了里面的pattern
from mysite.views import hello#这句报错,我修改为去掉了mysite.
urlpatterns = patterns('', ('^hello/$', hello), )#正则表达式
请留意:为了简洁,咱们移除了注释代码。 若是你喜欢的话,你能够保留那些行。)
咱们作了两处修改。
首先,咱们从模块 (在 Python 的 import 语法中, mysite/views.py 转译为 mysite.views ) 中引入了 hello 视图。 (这假设mysite/views.py在你的Python搜索路径上。关于搜索路径的解释,请参照下文。)
接下来,咱们为urlpatterns加上一行: (‘^hello/$’, hello), 这行被称做URLpattern,它是一个Python的元组。元组中第一个元素是模式匹配字符串(正则表达式);第二个元素是那个模式将使用的视图函数。
正则表达式
正则表达式 (或 regexes ) 是通用的文本模式匹配的方法。 Django URLconfs 容许你 使用任意的正则表达式来作强有力的URL映射,不过一般你实际上可能只须要使用不多的一 部分功能。 这里是一些基本的语法。
符号 | 匹配 |
---|---|
. (dot) | 任意单一字符 |
\d | 任意一位数字 |
[A-Z] | A 到 Z中任意一个字符(大写) |
[a-z] | a 到 z中任意一个字符(小写) |
[A-Za-z] | a 到 z中任意一个字符(不区分大小写) |
+ | 匹配一个或更多 (例如, \d+ 匹配一个或 多个数字字符) |
[^/]+ | 一个或多个不为‘/’的字符 |
? | 零个或一个以前的表达式(例如:\d? 匹配零个或一个数字) |
* | 匹配0个或更多 (例如, \d* 匹配0个 或更多数字字符) |
{1,3} | 介于一个和三个(包含)以前的表达式(例如,\d{1,3}匹配一个或两个或三个数字) |
这些敏感信息若是部署到了因特网上的站点就不该该暴露 这些信息。这个“Page not found”页面只会在 调试模式(debug mode) 下 显示。 咱们将在之后说明怎么关闭调试模式。
全部均开始于setting文件。当你运行python manage.py runserver,脚本将在于manage.py同一个目录下查找名为setting.py的文件。这个文件包含了全部有关这个Django项目的配置信息,均大写: TEMPLATE_DIRS , DATABASE_NAME , 等. 最重要的设置时ROOT_URLCONF,它将做为URLconf告诉Django在这个站点中那些Python的模块将被用到
还记得何时django-admin.py startproject建立文件settings.py和urls.py吗?自动建立的settings.py包含一个ROOT_URLCONF配置用来指向自动产生的urls.py. 打开文件settings.py你将看到以下:
ROOT_URLCONF = 'mysite.urls'
相对应的文件是mysite/urls.py
当访问 URL /hello/ 时,Django 根据 ROOT_URLCONF 的设置装载 URLconf 。 而后按顺序逐个匹配URLconf里的URLpatterns,直到找到一个匹配的。 当找到这个匹配 的URLpatterns就调用相关联的view函数,并把 HttpRequest 对象做为第一个参数。
总结一下:
进来的请求转入/hello/.
Django经过在ROOT_URLCONF配置来决定根URLconf.
Django在URLconf中的全部URL模式中,查找第一个匹配/hello/的条目。
若是找到匹配,将调用相应的视图函数
视图函数返回一个HttpResponse
Django转换HttpResponse为一个适合的HTTP response, 以Web page显示出来
from django.conf.urls.defaults import * from mysite.views import hello, current_datetime urlpatterns = patterns('', ('^hello/$', hello), ('^time/$', current_datetime), )
写好视图而且更新URLconf以后,运行命令python manage.py runserver以启动服务,在浏览器中输入http://127.0.0.1:8000/time/。 你将看到当前的日期和时间。
若是咱们想要输出这个函数到 一些 URL, 咱们只须要修改URL配置而不用 去改动视图的代码。
如何设计程序来处理任意数量的时差? 答案是:使用通配符(wildcard URLpatterns)。正如咱们以前提到过,一个URL模式就是一个正则表达式。所以,这里可使用d+来匹配1个以上的数字。
urlpatterns = patterns('',
# ...
(r'^time/plus/\d+/$', hours_ahead),
# ...
)
这里使用# …来表示省略了其它可能存在的URL模式定义。 (见上)
这个URL模式将匹配相似 /time/plus/2/ , /time/plus/25/ ,甚至 /time/plus/100000000000/ 的任何URL。 更进一步,让咱们把它限制在最大容许99个小时, 这样咱们就只容许一个或两个数字,正则表达式的语法就是 \d{1,2} :
(r'^time/plus/\d{1,2}/$', hours_ahead),
备注
在建造Web应用的时候,尽量多考虑可能的数据输入是很重要的,而后决定哪些咱们能够接受。 在这里咱们就设置了99个小时的时间段限制。
另一个重点,正则表达式字符串的开头字母“r”。 它告诉Python这是个原始字符串,不须要处理里面的反斜杠(转义字符)。 在普通Python字符串中,反斜杠用于特殊字符的转义。好比n转义成一个换行符。 当你用r把它标示为一个原始字符串后,Python再也不视其中的反斜杠为转义字符。也就是说,“n”是两个字符串:“”和“n”。因为反斜杠在Python代码和正则表达式中有冲突,所以建议你在Python定义正则表达式时都使用原始字符串。 从如今开始,本文全部URL模式都用原始字符串。
hours_ahead 和咱们之前写的 current_datetime 很象,关键的区别在于: 它多了一个额外参数,时间差。如下是view代码:
from django.http import Http404, HttpResponse import datetime def hours_ahead(request, offset): try: offset = int(offset)
except ValueError: raise Http404()
dt = datetime.datetime.now() + datetime.timedelta(hours=offset) html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt) return HttpResponse(html)
让咱们逐行分析一下代码:
视图函数, hours_ahead , 有 两个 参数: request 和 offset . (见上)
request 是一个 HttpRequest 对象, 就像在 current_datetime 中同样. 再说一次好了: 每个视图 老是 以一个 HttpRequest 对象做为 它的第一个参数。 (见上)
offset 是从匹配的URL里提取出来的。 例如:若是请求URL是/time/plus/3/,那么offset将会是3;若是请求URL是/time/plus/21/,那么offset将会是21。请注意:捕获值永远都是字符串(string)类型,而不会是整数(integer)类型,即便这个字符串全由数字构成(如:“21”)。
(从技术上来讲,捕获值老是Unicode objects,而不是简单的Python字节串,但目前不须要担忧这些差异。)
在这里咱们命名变量为 offset ,你也能够任意命名它,只要符合Python 的语法。 变量名是可有可无的,重要的是它的位置,它是这个函数的第二个 参数 (在 request 的后面)。 你还可使用关键字来定义它,而不是用 位置。
咱们在这个函数中要作的第一件事情就是在 offset 上调用 int() . 这会把这个字符串值转换为整数。
请留意:若是你在一个不能转换成整数类型的值上调用int(),Python将抛出一个ValueError异常。如:int(‘foo’)。在这个例子中,若是咱们遇到ValueError异常,咱们将转为抛出django.http.Http404异常——正如你想象的那样:最终显示404页面(提示信息:页面不存在)。
<ul> {% for item in item_list %} <li>{{ item }}</li> {% endfor %} </ul> {% if ordered_warranty %} <p>Your warranty information will be included in the packaging.</p> {% else %} <p>You didn't order a warranty, so you're on your own when the products inevitably stop working.</p> {% endif %}
用两个大括号括起来的文字(例如 {{ person_name }} )称为 变量(variable) 。这意味着将按照给定的名字插入变量的值。 如何指定变量的值呢? 稍后就会说明。
被大括号和百分号包围的文本(例如 {% if ordered_warranty %} )是 模板标签(template tag) 。标签(tag)定义比较明确,即: 仅通知模板系统完成某些工做的标签。
这个例子中的模板包含一个for标签( {% for item in item_list %} )和一个if 标签({% if ordered_warranty %} )
for标签相似Python的for语句,可以让你循环访问序列里的每个项目。 if 标签,正如你所料,是用来执行逻辑判断的。 在这里,tag标签检查ordered_warranty值是否为True。若是是,模板系统将显示{% if ordered_warranty %}和{% else %}之间的内容;不然将显示{% else %}和{% endif %}之间的内容。{% else %}是可选的。
最后,这个模板的第二段中有一个关于filter过滤器的例子,它是一种最便捷的转换变量输出格式的方式。 如这个例子中的{{ship_date|date:”F j, Y” }},咱们将变量ship_date传递给date过滤器,同时指定参数”F j,Y”。date过滤器根据参数进行格式输出。 过滤器是用管道符(|)来调用的,具体能够参见Unix管道符。
Django 模板含有不少内置的tags和filters,咱们将陆续进行学习. 附录F列出了不少的tags和filters的列表,
在Python代码中使用Django模板的最基本方式以下:
能够用原始的模板代码字符串建立一个 Template 对象, Django一样支持用指定模板文件路径的方式来建立 Template 对象;
调用模板对象的render方法,而且传入一套变量context。它将返回一个基于模板的展示字符串,模板中的变量和标签会被context值替换。
代码以下:
>>> from django import template >>> t = template.Template('My name is {{ name }}.') >>> c = template.Context({'name': 'Adrian'}) >>> print t.render(c) My name is Adrian. >>> c = template.Context({'name': 'Fred'}) >>> print t.render(c) My name is Fred.
建立一个 Template 对象最简单的方法就是直接实例化它。 Template 类就在 django.template 模块中,构造函数接受一个参数,原始模板代码。 让咱们深刻挖掘一下 Python的解释器看看它是怎么工做的。
转到project目录(在第二章由 django-admin.py startproject 命令建立), 输入命令 python manage.py shell 启动交互界面。
一个特殊的Python提示符
若是你曾经使用过Python,你必定好奇,为何咱们运行python manage.py shell而不是python。这两个命令都会启动交互解释器,可是manage.py shell命令有一个重要的不一样: 在启动解释器以前,它告诉Django使用哪一个设置文件。 Django框架的大部分子系统,包括模板系统,都依赖于配置文件;若是Django不知道使用哪一个配置文件,这些系统将不能工做。
了解一些模板系统的基本知识:
>>> from django.template import Template >>> t = Template('My name is {{ name }}.') >>> print t
若是你跟咱们一块儿作,你将会看到下面的内容:
<django.template.Template object at 0xb7d5f24c>
0xb7d5f24c 每次都会不同,这没什么关系;这只是Python运行时 Template 对象的ID。
当你建立一个 Template 对象,模板系统在内部编译这个模板到内部格式,并作优化,作好 渲染的准备。 若是你的模板语法有错误,那么在调用 Template() 时就会抛出 TemplateSyntaxError 异常:
>>> from django.template import Template >>> t = Template('{% notatag %}') Traceback (most recent call last): File "<stdin>", line 1, in ? ... django.template.TemplateSyntaxError: Invalid block tag: 'notatag'
这里,块标签(block tag)指向的是`` {% notatag %}``,块标签与模板标签是同义的。
字典和Contexts
Python的字典数据类型就是关键字和它们值的一个映射。 Context 和字典很相似, Context 还提供更多的功能,请看第九章。
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
print t.render(Context({'name': name}))
-------------
模板系统可以很是简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。
点语法也能够用来引用对象的* 方法*。
句点也可用于访问列表索引,不容许使用负数列表索引。
句点查找规则可归纳为: 当模板系统在变量名中遇到点时,按照如下顺序尝试进行查找:
字典类型查找 (好比 foo["bar"] )
属性查找 (好比 foo.bar )
方法调用 (好比 foo.bar() )
可使用标准的Python字典语法(syntax)向``上下文(Context)`` 对象添加或者删除条目:
>>> from django.template import Context >>> c = Context({"foo": "bar"}) >>> c['foo'] 'bar' >>> del c['foo'] >>> c['foo'] Traceback (most recent call last): ... KeyError: 'foo' >>> c['newvariable'] = 'hello' >>> c['newvariable'] 'hello'
----------------------
在Python和Django模板系统中,如下这些对象至关于布尔值的False
空列表([] )
空元组(() )
空字典({} )
空字符串('' )
零值(0 )
特殊对象None
对象False(很明显)
给定一个运动员列表 athlete_list 变量
{% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %}
给标签增长一个 reversed 使得该列表被反向迭代:
{% for athlete in athlete_list reversed %} ... {% endfor %}
能够嵌套使用 {% for %} 标签
---------------------forloop.parentloop 是一个指向当前循环的上一级循环的 forloop 对象的引用(在嵌套循环的状况下)。forloop 变量仅仅可以在循环中使用。 在模板解析器碰到 {% endfor %}标签后, forloop就不可访问了。
Django模板系统压根儿就没想过实现一个全功能的编程语言,因此它不容许咱们在模板中执行Python的语句(仍是那句话,要了解更多请参看理念和限制小节)。 可是比较两个变量的值而且显示一些结果实在是个太常见的需求了,因此Django提供了 {% ifequal %} 标签供咱们使用。
{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中全部的值。
下面的例子比较两个模板变量 user 和 currentuser :
{% ifequal user currentuser %} <h1>Welcome!</h1> {% endifequal %}
注意:
只有模板变量,字符串,整数和小数能够做为 {% ifequal %} 标签的参数。
如下几个是最为重要的过滤器的一部分。 附录F包含其他的过滤器。
addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。 这在处理包含JavaScript的文本时是很是有用的。
date : 按指定的格式字符串参数格式化 date 或者 datetime 对象, 范例:
{{ pub_date|date:"F j, Y" }}
--------------------
首先考虑把模板保存在文件系统的某个位置并用 Python 内建的文件操做函数来读取文件内容。 假设文件保存在 /home/djangouser/templates/mytemplate.html 中的话,代码就会像下面这样:
from django.template import Template, Context from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() # Simple way of using templates from the filesystem. # This is BAD because it doesn't account for missing files! fp = open('/home/djangouser/templates/mytemplate.html') t = Template(fp.read()) fp.close() html = t.render(Context({'current_date': now})) return HttpResponse(html)
然而,基于如下几个缘由,该方法还算不上简洁:
它没有对文件丢失的状况作出处理。 若是文件 mytemplate.html 不存在或者不可读, open() 函数调用将会引起 IOError 异常。
这里对模板文件的位置进行了硬编码。 若是你在每一个视图函数都用该技术,就要不断复制这些模板的位置。 更不用说还要带来大量的输入工做!
它包含了大量使人生厌的重复代码。 与其在每次加载模板时都调用 open() 、 fp.read() 和 fp.close() ,还不如作出更佳选择。
要使用此模板加载API,首先你必须将模板的保存位置告诉框架。
打开你的settings.py配置文件,找到TEMPLATE_DIRS这项设置吧。 它的默认设置是一个空元组(tuple),加上一些自动生成的注释。
该设置告诉 Django 的模板加载机制在哪里查找模板。 选择一个目录用于存放模板并将其添加到 TEMPLATE_DIRS 中:
TEMPLATE_DIRS = ( '/home/django/mysite/templates', )
若是你的 TEMPLATE_DIRS只包含一个目录,别忘了在该目录后加上个逗号。
---------------
(晕菜了)
import MySQLdb
def book_list(request):
db = MySQLdb.connect(user='me', db='mydb', passwd='secret', host='localhost')
cursor = db.cursor()
cursor.execute('SELECT name FROM books ORDER BY name')
names = [row[0] for row in cursor.fetchall()]
db.close()
return render_to_response('book_list.html', {'names': names})
问题:
咱们将数据库链接参数硬行编码于代码之中。 理想状况下,这些参数应当保存在 Django 配置中。
咱们不得不重复一样的代码: 建立数据库链接、建立数据库游标、执行某个语句、而后关闭数据库。 理想状况下,咱们所须要应该只是指定所需的结果。
它把咱们栓死在 MySQL 之上。 若是过段时间,咱们要从 MySQL 换到 PostgreSQL,就不得不使用不一样的数据库适配器(例如 psycopg 而不是 MySQLdb ),改变链接参数,根据 SQL 语句的类型可能还要修改SQL 。 理想状况下,应对所使用的数据库服务器进行抽象,这样一来只在一处修改便可变换数据库服务器。 (若是你正在创建一个开源的Django应用程序来尽量让更多人使用的话,这个特性是很是适当的。)
-------------
Django 里更关注的是模型(Model)、模板(Template)和视图(Views),Django 也被称为 MTV 框架 。在 MTV 开发模式中:
M 表明模型(Model),即数据存取层。 该层处理与数据相关的全部事务: 如何存取、如何验证有效性、包含哪些行为以及数据之间的关系等。
T 表明模板(Template),即表现层。 该层处理与表现相关的决定: 如何在页面或其余类型文档中进行显示。
DATABASE_ENGINE = '' DATABASE_NAME = '' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = ''
设置 | 数据库 | 所需适配器 |
---|---|---|
`` postgresql`` | PostgreSQL | psycopg 1.x版, http://www.djangoproject.com/r/python-pgsql/1/。 |
postgresql_psycopg2 | PostgreSQL | psycopg 2.x版, http://www.djangoproject.com/r/python-pgsql/。 |
mysql | MySQL | MySQLdb , http://www.djangoproject.com/r/python-mysql/. |
sqlite3 | SQLite | 若是使用Python 2.5+则不须要适配器。 不然就使用 pysqlite , http://www.djangoproject.com/r/python-sqlite/。 |
oracle | Oracle | cx_Oracle , http://www.djangoproject.com/r/python-oracle/. |
配置示例:
DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_NAME 将数据库名称告知 Django 。 例如:
DATABASE_NAME = 'mydb'
若是使用 SQLite,请对数据库文件指定完整的文件系统路径。 例如:
DATABASE_NAME = '/home/django/mydata.db'
在这个例子中,咱们将SQLite数据库放在/home/django目录下,你能够任意选用最合适你的目录。
DATABASE_USER 告诉 Django 用哪一个用户链接数据库。 例如: 若是用SQLite,空白便可。
DATABASE_PASSWORD 告诉Django链接用户的密码。 SQLite 用空密码便可。
DATABASE_HOST 告诉 Django 链接哪一台主机的数据库服务器。 若是数据库与 Django 安装于同一台计算机(即本机),可将此项保留空白。 若是你使用SQLite,此项留空。
此处的 MySQL 是一个特例。 若是使用的是 MySQL 且该项设置值由斜杠( '/' )开头,MySQL 将经过 Unix socket 来链接指定的套接字,例如:
DATABASE_HOST = '/var/run/mysql'
一旦在输入了那些设置并保存以后应当测试一下你的配置。 咱们能够在`` mysite`` 项目目录下执行上章所提到的`` python manage.py shell`` 来进行测试。 (咱们上一章提到过在,`` manager.py shell`` 命令是以正确Django配置启用Python交互解释器的一种方法。 这个方法在这里是颇有必要的,由于Django须要知道加载哪一个配置文件来获取数据库链接信息。)
输入下面这些命令来测试你的数据库配置:
>>> from django.db import connection >>> cursor = connection.cursor()
错误信息 | 解决方法 |
---|---|
You haven’t set the DATABASE_ENGINE setting yet. | 不要以空字符串配置`` DATABASE_ENGINE`` 的值。 表格 5-1 列出可用的值。 |
Environment variable DJANGO_SETTINGS_MODULE is undefined. | 使用`` python manager.py shell`` 命令启动交互解释器,不要以`` python`` 命令直接启动交互解释器。 |
Error loading _____ module: No module named _____. | 未安装合适的数据库适配器 (例如, psycopg 或 MySQLdb )。Django并不自带适配器,因此你得本身下载安装。 |
_____ isn’t an available database backend. | 把DATABASE_ENGINE 配置成前面提到的合法的数据库引擎。 也许是拼写错误? |
database _____ does not exist | 设置`` DATABASE_NAME`` 指向存在的数据库,或者先在数据库客户端中执行合适的`` CREATE DATABASE`` 语句建立数据库。 |
role _____ does not exist | 设置`` DATABASE_USER`` 指向存在的用户,或者先在数据库客户端中执建立用户。 |
could not connect to server | 查看DATABASE_HOST和DATABASE_PORT是否已正确配置,并确认数据库服务器是否已正常运行。 |
若是你只是建造一个简单的Web站点,那么可能你只须要一个app就能够了; 但若是是一个包含许多不相关的模块的复杂的网站,例如电子商务和社区之类的站点,那么你可能须要把这些模块划分红不一样的app,以便之后复用。
系统对app有一个约定: 若是你使用了Django的数据库层(模型),你 必须建立一个Django app。 模型必须存放在apps中。 所以,为了开始建造 咱们的模型,咱们必须建立一个新的app。
在`` mysite`` 项目文件下输入下面的命令来建立`` books`` app:
python manage.py startapp books
from django.db import models class Publisher(models.Model):…… class Author(models.Model):…… class Book(models.Model):……