第二十章: 安全

对标题的评论会显示在这里

Internet并不安全。

对这一段的评论会显示在这里

现如今,每天都会出现新的安全问题。 我们目睹过病毒飞速地蔓延,大量被控制的肉鸡作为武器来攻击其他人,与垃圾邮件的永无止境的军备竞赛,以及许许多多站点被黑的报告。

kevin 05-28 06:44

大量被控制的肉鸡作为武器来攻击其他人。。肉鸡本来就是被控制的。改成’大量肉鸡被作为武器来攻击其他人‘ 是不是好一些?

fandyst 05-11 11:50

我觉得这样翻译没问题。修饰语的重复性只有在被修饰词的意义中带有explicit修饰意义时才被认定为语义重复。

xavier 07-23 02:22

这一章看起来真是吓人!骇客们太厉害了!

匿名读者 07-14 07:54

lou

对这一段的评论会显示在这里

作为Web开发人员,我们有责任来对抗这些黑暗的力量。 每一个Web开发者都应该把安全看成是Web编程中的基础部分。 不幸的是,要实现安全是困难的。

对这一段的评论会显示在这里

Django试图减轻这种难度。 它被设计为自动帮你避免一些web开发新手(甚至是老手)经常会犯的错误。 尽管如此,需要弄清楚,Django如何保护我们,以及我们可以采取哪些重要的方法来使得我们的代码更加安全。

对这一段的评论会显示在这里

首先,一个重要的前提: 我们并不打算给出web安全的一个详尽的说明,因此我们也不会详细地解释每一个薄弱环节。 在这里,我们会给出Django所面临的安全问题的一个大概。

对这一段的评论会显示在这里

Web安全现状

对这一段的评论会显示在这里

如果你从这章中只学到了一件事情,那么它会是:

对这一段的评论会显示在这里

在任何条件下都不要相信浏览器端提交的数据。

对这一段的评论会显示在这里

你从不会知道HTTP连接的另一端会是谁。 可能是一个正常的用户,但是同样可能是一个寻找漏洞的邪恶的骇客。

对这一段的评论会显示在这里

从浏览器传过来的任何性质的数据,都需要近乎狂热地接受检查。 这包括用户数据(比如Web表单提交的内容)和带外数据(比如,HTTP头、cookies以及其他信息)。 要修改那些浏览器自动添加的元数据,是一件很容易的事。

对这一段的评论会显示在这里

在这一章所提到的所有的安全隐患都直接源自对传入数据的信任,并且在使用前不加处理。 你需要不断地问自己,这些数据从何而来。

对这一段的评论会显示在这里

SQL注入

对这一段的评论会显示在这里

SQL注入 是一个很常见的形式,在SQL注入中,攻击者改变web网页的参数(例如 GET /POST 数据或者URL地址),加入一些其他的SQL片段。 未加处理的网站会将这些信息在后台数据库直接运行。

对这一段的评论会显示在这里

这种危险通常在由用户输入构造SQL语句时产生。 例如,假设我们要写一个函数,用来从通信录搜索页面收集一系列的联系信息。 为防止垃圾邮件发送器阅读系统中的email,我们将在提供email地址以前,首先强制用户输入用户名。

对这一段的评论会显示在这里
def user_contacts(request):
    user = request.GET['username']
    sql = "SELECT * FROM user_contacts WHERE username = '%s';" % username
    # execute the SQL here...
路人 10-11 02:22

应该是 % user

对这一段的评论会显示在这里

备注

对这一段的评论会显示在这里

在这个例子中,以及在以下所有的“不要这样做”的例子里,我们都去除了大量的代码,避免这些函数可以正常工作。 我们可不想这些例子被拿出去使用。

对这一段的评论会显示在这里

尽管,一眼看上去,这一点都不危险,实际上却不尽然。

对这一段的评论会显示在这里

首先,我们对于保护email列表所采取的措施,遇到精心构造的查询语句就会失效。 想象一下,如果攻击者在查询框中输入 "' OR 'a'='a" 。 此时,查询的字符串会构造如下:

对这一段的评论会显示在这里
SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a';
对这一段的评论会显示在这里

由于我们允许不安全的SQL语句出现在字符串中,攻击者加入 OR 子句,使得每一行数据都被返回。

对这一段的评论会显示在这里

事实上,这是最温和的攻击方式。 如果攻击者提交了 "'; DELETE FROM user_contacts WHERE 'a' = 'a'" ,我们最终将得到这样的查询:

对这一段的评论会显示在这里
SELECT * FROM user_contacts WHERE username = ''; DELETE FROM user_contacts WHERE 'a' = 'a';
对这一段的评论会显示在这里

哦!我们整个通信录名单去哪儿了? 我们整个通讯录会被立即删除

对这一段的评论会显示在这里

解决方案

对这一段的评论会显示在这里

尽管这个问题很阴险,并且有时很难发现,解决方法却很简单: 绝不信任用户提交的数据,并且在传递给SQL语句时,总是转义它。

对这一段的评论会显示在这里

Django的数据库API帮你做了。 它会根据你所使用的数据库服务器(例如PostSQL或者MySQL)的转换规则,自动转义特殊的SQL参数。

对这一段的评论会显示在这里

举个例子,在下面这个API调用中:

对这一段的评论会显示在这里
foo.get_list(bar__exact="' OR 1=1")
对这一段的评论会显示在这里

Django会自动进行转义,得到如下表达:

对这一段的评论会显示在这里
SELECT * FROM foos WHERE bar = '\' OR 1=1'
对这一段的评论会显示在这里

完全无害。

对这一段的评论会显示在这里

这被运用到了整个Django的数据库API中,只有一些例外:

对这一段的评论会显示在这里
  • 传给 extra() 方法的 where 参数。 (参考 附录 C。) 这个参数故意设计成可以接受原始的SQL。
  • 使用底层数据库API的查询。 (详见第十章)
对这一段的评论会显示在这里

以上列举的每一个示例都能够很容易的让您的应用得到保护。 在每一个示例中,为了避免字符串被篡改而使用 绑定参数 来代替。这样,本节开始的例子应该写成这样:

对这一段的评论会显示在这里
from django.db import connection

def user_contacts(request):
    user = request.GET['username']
    sql = "SELECT * FROM user_contacts WHERE username = %s"
    cursor = connection.cursor()
    cursor.execute(sql, [user])
    # ... do something with the results
对这一段的评论会显示在这里

底层 execute 方法采用了一个SQL字符串作为其第二个参数,这个SQL字符串包含若干’%s’占位符,execute方法能够自动对传入列表中的参数进行转义和插入。 你应该用 always 这种方式构造自定义的SQL。

Wally Yu 09-07 09:04

错了吗?应该是“你应该*总是*用这种方式” 一个没翻译,一个顺序错了,读不通...

对这一段的评论会显示在这里

不幸的是,您并不是在SQL中能够处处都使用绑定参数,绑定参数不能够作为标识符(如表或列名等)。 因此,如果您需要这样做—我是说—动态构建 POST 变量中的数据库表的列表的话,您需要在您的代码中来对这些数据库表的名字进行转义。 Django提供了一个函数, django.db.backend.quote_name ,这个函数能够根据当前数据库引用结构对这些标识符进行转义。

对这一段的评论会显示在这里

跨站点脚本 (XSS)

对这一段的评论会显示在这里

在Web应用中, 跨站点脚本 (XSS)有时在被渲染成HTML之前,不能恰当地对用户提交的内容进行转义。 这使得攻击者能够向你的网站页面插入通常以 <script> 标签形式的任意HTML代码。

对这一段的评论会显示在这里

攻击者通常利用XSS攻击来窃取cookie和会话信息,或者诱骗用户将其私密信息透漏给被人(又称 钓鱼 )。

2号店 07-17 13:57

被人-->别人

传说中的XSS? 07-23 02:11

<script type="text/javascript" charset="utf-8"> alert("Hi!"); </script>

匿名读者 12-25 18:22

<script type="text/javascript" charset="utf-8"> alert("Hi!"); </script>

对这一段的评论会显示在这里

这种类型的攻击能够采用多种不同的方式,并且拥有几乎无限的变体,因此我们还是只关注某个典型的例子吧。 让我们来想想这样一个极度简单的Hello World视图:

对这一段的评论会显示在这里
from django.http import HttpResponse

def say_hello(request):
    name = request.GET.get('name', 'world')
    return HttpResponse('<h1>Hello, %s!</h1>' % name)
对这一段的评论会显示在这里

这个视图只是简单的从GET参数中读取姓名然后将姓名传递给hello.html模板。 因此,如果我们访问 http://example.com/hello/?name=Jacob ,被呈现的页面将会包含一以下这些:

对这一段的评论会显示在这里
<h1>Hello, Jacob!</h1>
对这一段的评论会显示在这里

但是,等等,如果我们访问 http://example.com/hello/?name=<i>Jacob</i> 时又会发生什么呢?

对这一段的评论会显示在这里
<h1>Hello, <i>Jacob</i>!</h1>
对这一段的评论会显示在这里

当然,一个攻击者不会使用标签开始的类似代码,他可能会用任意内容去包含一个完整的HTML集来劫持您的页面。 这种类型的攻击已经运用于虚假银行站点以诱骗用户输入个人信息,事实上这就是一种劫持XSS的形式,用以使用户向攻击者提供他们的银行帐户信息。

对这一段的评论会显示在这里

如果您将这些数据保存在数据库中,然后将其显示在您的站点上,那么问题就变得更严重了。 例如,一旦MySpace被发现这样的特点而能够轻易的被XSS攻击,后果不堪设想。 某个用户向他的简介中插入JavaScript,使得您在访问他的简介页面时自动将其加为您的好友,这样在几天之内,这个人就能拥有上百万的好友。 在几天的时间里,他拥有了数以百万的朋友。

forwil 09-07 16:12

在几天的时间里,他拥有了数以百万的朋友。 这个怎么重复了

对这一段的评论会显示在这里

现在,这种后果听起来还不那么恶劣,但是您要清楚——这个攻击者正设法将 的代码而不是MySpace的代码运行在 的计算机上。 这显然违背了假定信任——所有运行在MySpace上的代码应该都是MySpace编写的,而事实上却不如此。

对这一段的评论会显示在这里

MySpace是极度幸运的,因为这些恶意代码并没有自动删除访问者的帐户,没有修改他们的密码,也并没有使整个站点一团糟,或者出现其他因为这个弱点而导致的其他噩梦。

对这一段的评论会显示在这里

解决方案

对这一段的评论会显示在这里

解决方案是简单的: 总是转义可能来自某个用户的任何内容。

对这一段的评论会显示在这里

为了防止这种情况,Django的模板系统自动转义所有的变量值。 让我们来看看如果我们使用模板系统重写我们的例子会发生什么

对这一段的评论会显示在这里
# views.py

from django.shortcuts import render_to_response

def say_hello(request):
    name = request.GET.get('name', 'world')
    return render_to_response('hello.html', {'name': name})

# hello.html

<h1>Hello, {{ name }}!</h1>
对这一段的评论会显示在这里

这样,一个到[http://example.com/hello](http://example.com/hello)/name=Jacob 的请求将导致下面的页面:

roger 12-02 07:01

这里已经被转义了。。原文是:http://example.com/hello/name=<i>Jacob</i>

对这一段的评论会显示在这里
<h1>Hello, &lt;i&gt;Jacob&lt;/i&gt;!</h1>
对这一段的评论会显示在这里

我们在第四章涵盖了Django的自动转义,一起想办法将其关闭。 甚至,如果Django真的新增了这些特性,您也应该习惯性的问自己,一直以来,这些数据都来自于哪里呢? 没有哪个自动解决方案能够永远保护您的站点百分之百的不会受到XSS攻击。

roger 12-02 07:02

along with应反应为并且

对这一段的评论会显示在这里

伪造跨站点请求

对这一段的评论会显示在这里

伪造跨站点请求(CSRF)发生在当某个恶意Web站点诱骗用户不知不觉的从一个信任站点下载某个URL之时,这个信任站点已经被通过信任验证,因此恶意站点就利用了这个被信任状态。

对这一段的评论会显示在这里

Django拥有内建工具来防止这种攻击。 包括攻击本身及其使用的工具都在有详细介绍。16章

对这一段的评论会显示在这里

会话伪造/劫持

对这一段的评论会显示在这里

这不是某个特定的攻击,而是对用户会话数据的通用类攻击。 这种攻击可以采取多种形式:

对这一段的评论会显示在这里

中间人 攻击:检索所在有线(无线)网络,监听会话数据。

对这一段的评论会显示在这里

伪造会话 :攻击者利用会话ID(可能是通过中间人攻击来获得)将自己伪装成另一个用户。

对这一段的评论会显示在这里

这两种攻击的一个例子可以是在一间咖啡店里的某个攻击者利用店内的无线网络来捕获某个会话cookie,然后她就可以利用那个cookie来假冒原始用户。 她便可以使该cookie来模拟原始用户。

对这一段的评论会显示在这里

伪造cookie :就是指某个攻击者覆盖了在某个cookie中本应该是只读的数据。 第十四章 <../chapter14/>__ 详细介绍了cookies如何工作,以及要点之一的是,它在你不知道的情况下无视浏览器和恶意用户私自改变cookies。

对这一段的评论会显示在这里

Web站点以 IsLoggedIn=1 或者 LoggedInAsUser=jacob 这样的方式来保存cookie由来已久,使用这样的cookie是再简单不过的了。

对这一段的评论会显示在这里

一个更微妙的层面上,然而,相信在cookies中存储的任意信息绝对不是一个好主意。 你永远不知道谁一直在作怪。

对这一段的评论会显示在这里

会话滞留 :攻击者诱骗用户设置或者重设置该用户的会话ID。

对这一段的评论会显示在这里

例如,PHP允许在URL(如 http://example.com/?PHPSESSID=fa90197ca25f6ab40bb1374c510d7a32 等)中传递会话标识符。攻击者欺骗用户点击一个硬编码会话ID的链接,这回导致用户转到那个会话。

对这一段的评论会显示在这里

会话滞留已经运用在钓鱼攻击中,以诱骗用户在攻击者拥有的账号里输入其个人信息。 他可以稍后登陆账户并且检索数据。

对这一段的评论会显示在这里

会话中毒 :攻击者通过用户提交设置会话数据的Web表单向该用户会话中注入潜在危险数据。

对这一段的评论会显示在这里

一个经典的例子就是一个站点在某个cookie中存储了简单的用户偏好(比如一个页面背景颜色)。 攻击者可以诱骗用户点击一个链接来提交背景颜色,实际上包含了一个XSS攻击。 如果颜色没有转义,那么就可以再把恶意代码注入到用户环境中。

对这一段的评论会显示在这里

解决方案

对这一段的评论会显示在这里

有许多基本准则能够保护您不受到这些攻击:

对这一段的评论会显示在这里

不要在URL中包含任何session信息。

对这一段的评论会显示在这里

Django的session框架(参见第十四章 <../chapter14/>__ )根本不会容许session包含在URL中。

对这一段的评论会显示在这里

不要直接在cookie中保存数据。 相反,存储一个在后台映射到session数据存储的session ID。

对这一段的评论会显示在这里

如果使用Django内置的session框架(即 request.session ),它会自动进行处理。 这个session框架仅在cookie中存储一个session ID,所有的session数据将会被存储在数据库中。

对这一段的评论会显示在这里

如果需要在模板中显示session数据,要记得对其进行转义。 可参考之前的XSS部分,对所有用户提交的数据和浏览器提交的数据进行转义。 对于session信息,应该像用户提交的数据一样对其进行处理。

对这一段的评论会显示在这里

任何可能的地方都要防止攻击者进行session欺骗。

对这一段的评论会显示在这里

尽管去探测究竟是谁劫持了会话ID是几乎不可能的事儿,Django还是内置了保护措施来抵御暴力会话攻击。 会话ID被存在哈希表里(取代了序列数字),这样就阻止了暴力攻击,并且如果一个用户去尝试一个不存在的会话那么她总是会得到一个新的会话ID,这样就阻止了会话滞留。

对这一段的评论会显示在这里

请注意,以上没有一种准则和工具能够阻止中间人攻击。 这些类型的攻击是几乎不可能被探测的。 如果你的站点允许登陆用户去查看任意敏感数据的话,你应该 总是 通过HTTPS来提供网站服务。 此外,如果你的站点使用SSL,你应该将 SESSION_COOKIE_SECURE 设置为 True ,这样就能够使Django只通过HTTPS发送会话cookie。

对这一段的评论会显示在这里

邮件头部注入

对这一段的评论会显示在这里

邮件头部注入 :SQL注入的兄弟,是一种通过劫持发送邮件的Web表单的攻击方式。 攻击者能够利用这种技术来通过你的邮件服务器发送垃圾邮件。 在这种攻击面前,任何方式的来自Web表单数据的邮件头部构筑都是非常脆弱的。

对这一段的评论会显示在这里

让我们看看在我们许多网站中发现的这种攻击的形式。 通常这种攻击会向硬编码邮件地址发送一个消息,因此,第一眼看上去并不显得像面对垃圾邮件那么脆弱。

对这一段的评论会显示在这里

但是,大多数表单都允许用户输入自己的邮件主题(同时还有from地址,邮件体,有时还有部分其他字段)。 这个主题字段被用来构建邮件消息的主题头部。

对这一段的评论会显示在这里

如果那个邮件头部在构建邮件信息时没有被转义,那么攻击者可以提交类似 "hello\ncc:spamvictim@example.com" (这里的 "\n" 是换行符)的东西。 这有可能使得所构建的邮件头部变成:

对这一段的评论会显示在这里
To: hardcoded@example.com
Subject: hello
cc: spamvictim@example.com
对这一段的评论会显示在这里

就像SQL注入那样,如果我们信任了用户提供的主题行,那样同样也会允许他构建一个头部恶意集,他也就能够利用联系人表单来发送垃圾邮件。

对这一段的评论会显示在这里

解决方案

对这一段的评论会显示在这里

我们能够采用与阻止SQL注入相同的方式来阻止这种攻击: 总是校验或者转义用户提交的内容。

对这一段的评论会显示在这里

Django内建邮件功能(在 django.core.mail 中)根本不允许在用来构建邮件头部的字段中存在换行符(表单,收件地址,还有主题)。 如果您试图使用 django.core.mail.send_mail 来处理包含换行符的主题时,Django将会抛出BadHeaderError异常。

对这一段的评论会显示在这里

如果你没有使用Django内建邮件功能来发送邮件,那么你需要确保包含在邮件头部的换行符能够引发错误或者被去掉。 你或许想仔细阅读 django.core.mail 中的 SateMIMEText 类来看看Django是如何做到这一点的。

对这一段的评论会显示在这里

目录遍历

对这一段的评论会显示在这里

目录遍历 :是另外一种注入方式的攻击,在这种攻击中,恶意用户诱骗文件系统代码对Web服务器不应该访问的文件进行读取和/或写入操作。

对这一段的评论会显示在这里

例子可以是这样的,某个视图试图在没有仔细对文件进行防毒处理的情况下从磁盘上读取文件:

对这一段的评论会显示在这里
def dump_file(request):
    filename = request.GET["filename"]
    filename = os.path.join(BASE_PATH, filename)
    content = open(filename).read()

    # ...
对这一段的评论会显示在这里

尽管一眼看上去,视图通过 BASE_PATH (通过使用 os.path.join )限制了对于文件的访问,但如果攻击者使用了包含 .. (两个句号,父目录的一种简写形式)的文件名,她就能够访问到 BASE_PATH 目录结构以上的文件。对她来说,发现究竟使用几个点号只是时间问题,比如这样:../../../../../etc/passwd

对这一段的评论会显示在这里

任何不做适当转义地读取文件操作,都可能导致这样的问题。 允许 操作的视图同样容易发生问题,而且结果往往更加可怕。

对这一段的评论会显示在这里

这个问题的另一种表现形式,出现在根据URL和其他的请求信息动态地加载模块。 一个众所周知的例子来自于Ruby on Rails。 在2006年上半年之前,Rails使用类似于 http://example.com/person/poke/1 这样的URL直接加载模块和调用函数。 结果是,精心构造的URL,可以自动地调用任意的代码,包括数据库的清空脚本。

对这一段的评论会显示在这里

解决方案

对这一段的评论会显示在这里

如果你的代码需要根据用户的输入来读写文件,你就需要确保,攻击者不能访问你所禁止访问的目录。

对这一段的评论会显示在这里

备注

对这一段的评论会显示在这里

不用多说,你 永远 不要在编写可以读取任何位置上的文件的代码!

xiaocainiaok 05-05 16:46

那要用到文件操作 怎么办?

xiatianxiao 03-02 10:24

例子中的代码是安全的,可以集中进行文件操作

对这一段的评论会显示在这里

Django内置的静态内容视图是做转义的一个好的示例(在 django.views.static 中)。这是相关代码:

对这一段的评论会显示在这里
import os
import posixpath

# ...

path = posixpath.normpath(urllib.unquote(path))
newpath = ''
for part in path.split('/'):
    if not part:
        # strip empty path components
        continue

    drive, part = os.path.splitdrive(part)
    head, part = os.path.split(part)
    if part in (os.curdir, os.pardir):
        # strip '.' and '..' in path
        continue

    newpath = os.path.join(newpath, part).replace('\\', '/')
对这一段的评论会显示在这里

Django不读取文件(除非你使用 static.serve 函数,但也受到了上面这段代码的保护),因此这种危险对于核心代码的影响就要小得多。

对这一段的评论会显示在这里

更进一步,URLconf抽象层的使用,意味着不经过你明确的指定,Django 决不会 装载代码。 通过创建一个URL来让Django装载没有在URLconf中出现的东西,是不可能发生的。

对这一段的评论会显示在这里

暴露错误消息

对这一段的评论会显示在这里

在开发过程中,通过浏览器检查错误和跟踪异常是非常有用的。 Django提供了漂亮且详细的debug信息,使得调试过程更加容易。

对这一段的评论会显示在这里

然而,一旦在站点上线以后,这些消息仍然被显示,它们就可能暴露你的代码或者是配置文件内容给攻击者。

对这一段的评论会显示在这里

还有,错误和调试消息对于最终用户而言是毫无用处的。 Django的理念是,站点的访问者永远不应该看到与应用相关的出错消息。 如果你的代码抛出了一个没有处理的异常,网站访问者不应该看到调试信息或者 任何 代码片段或者Python(面向开发者)出错消息。 访问者应该只看到友好的无法访问的页面。

对这一段的评论会显示在这里

当然,开发者需要在debug时看到调试信息。 因此,框架就要将这些出错消息显示给受信任的网站开发者,而要向公众隐藏。

对这一段的评论会显示在这里

解决方案

对这一段的评论会显示在这里

正如我们在第12章所提到的,Django的DEBUG 设置控制这些错误信息的显示。 当你准备部署时请确认把这个设置为:False

对这一段的评论会显示在这里

在Apache和mod_python下开发的人员,还要保证在Apache的配置文件中关闭 PythonDebug Off 选项,这个会在Django被加载以前去除出错消息。

对这一段的评论会显示在这里

安全领域的总结

对这一段的评论会显示在这里

我们希望关于安全问题的讨论,不会太让你感到恐慌。 Web是一个处处布满陷阱的世界,但是只要有一些远见,你就能拥有安全的站点。

对这一段的评论会显示在这里

永远记住,Web安全是一个不断发展的领域。如果你正在阅读这本书的停止维护的那些版本,请阅读最新版本的这个部分来检查最新发现的漏洞。 事实上,每周或者每月花点时间挖掘Web应用安全,并且跟上最新的动态是一个很好的主意。 花费很少,但是对你网站和用户的保护确是无价的。

对这一段的评论会显示在这里

接下来?

项超 02-07 06:01

我想知道附录在什么地方啊?

iseehe 08-07 06:22

我正在学习django做网站,能给具体的示例吗?

iseehe 08-07 06:23

我正在学习django做网站,能给具体的示例吗?书店能买到书吗?

roger 12-02 07:32

嗯看完了全部20章,留个爪印。另外给妹子们一个小提示,去看一下第13章的开头的第一个脚注:)

匿名读者 01-24 07:51

附录呢

hogan 02-12 13:02

到此一游~

匿名读者 02-19 08:31

看完了,到此一游~~~

苏哲 02-19 08:31

看完了,到此一游~~~

noooop 04-20 10:31

感谢django,作者和译者

fandyst 05-11 11:39

py3k.cn是翻译的djangobook. 附录地址在这里: http://www.djangobook.com/en/2.0/index.html

Jason Zhou 06-18 14:01

看完了,确有收获,感谢所有翻译人员的辛勤工作! 谢谢!

lincy 08-23 03:01

看完了,来留名。新的征程才刚刚开始啊

biaoqianwo.com 09-04 05:43

看完了,没有项目操作下是不行的。还是写个类似wp的博客系统练手。

大神请加我 05-19 07:10

看完了的基本都是大神,以后肯定有出息,留下QQ258568898,求大神带我飞

xavier 07-23 02:20

lol,楼上好有趣

wind 08-10 07:44

看完了, 有几章的内容没能理解 ...>_<...

路人 10-11 03:36

差不多两周,全看完了,非常感谢这个站点带给我的帮助!谢谢你们的辛勤付出! 另外,现在Django的版本在不断更新,就目前的1.7变化与文章也是有相当大的出入,或许这篇文章在以后还是能给好奇Django的小伙伴们指出一条道路,以后的路估计就是伴着文档了.

虎头蔓 02-02 06:04

看完了~! 感谢作者和翻译的辛勤劳作~! 喜欢Python,有没有什么好的论坛或者群什么的希望加一下以后共同学习 本人QQ: 272412527

MTH 02-12 05:53

看完了,Mark一下!谢谢翻译工作者!

匿名读者 07-02 03:15

下一个google从这里开始诞生

西木柚子 08-25 03:07

断断续续,花了两周的时间看完,除了最后的五章感觉暂时用不上,就大致浏览一遍,记住有这些内容,在大脑中留下印象。到时候用到了再来看看。其余的章节都详细阅读,并一行行的代码做实验。都是上班完成工作任务之后,剩下时间自学。决定从网络转到编程了,编程真好玩呀`~~ 留个爪印 瞄~

needle 11-05 09:46

一个多月~,终于,看完了~,同志们,握个爪!:)

Kimi 01-18 07:16

看完,最后几章对于一般django学习者真是比较没意思,有的内容略有过时,还是实践比较重要

匿名读者 06-20 09:18

看完了,用了一周时间,mark下

yiguxianyun 09-12 11:48

谢谢作者和译者!!!

yiguxianyun 09-13 00:46

虽然具体内容有点过时,坑有点多……但设计思想没过时,经验分享很大方……知无不言言无不尽,谆谆教诲诲人不倦,有我天朝上国古代宗师的风范!!!

littlej 05-15 09:09

先看了官网的教程,然后来这认真看了前面十多章,后面草草略过,留个爪, 感谢小伙伴们的一路陪伴。

guoqing jiang 05-15 09:13

http://djangobook.com/ 这是官方的最新版把。你们感兴趣去看看,貌似变化蛮大的。

尉迟胖 07-24 06:31

完结撒花

Ender451 09-16 01:39

感谢翻译者,就是很多地方都需要更新了~

猪头哥哥 12-13 01:25

很棒的教程 希望在以后的工作中碰到的问题都能解决

一个菜逼 04-20 09:33

真心感谢作者和译者,也感谢在评注中指出更新内容的各位前辈,虽然网站上的部分内容过于陈旧,但是思想是不变的,善用google translate总是好的

rainbow 12-12 07:23

感谢

对这一段的评论会显示在这里

你已经完成了我们安排的程序。 以下的附录内容中包含了可能在你的Djang项目中用得上的引用资源.

对这一段的评论会显示在这里

在运行你的Django网站时,无论是为你或几个朋友的小网站,或者是下一个google,我们祝你好运。

对这一段的评论会显示在这里