django实战系列.doc
Django实战系列(博客园专家ThinkInside 2012年初原创)Django实战系列的内容:0. 如果你以前没有接触过Django,你可能需要这些准备知识: URLconf+MTV:Django眼中的MVC Django第一步1. 实战系列的开发目标 需求分析和设计2. 从Model开始 创建第一个模型类3. Model之外,你还需要知道什么 Django也可以有scaffold scaffold生成物分析4. 关于界面:静态资源,模板,及其使用 引入bootstrap,设置静态资源 对比RoR和Django的模板系统 改造ProductList界面5. 逻辑层 对比RoR与Django的输入校验机制 实现Product的输入校验 单元测试6. 变更 修改Model类 增加目录页,设定统一布局7.关于会话 在session中保存购物车 让页面联动起来8. ajax Django实现RESTful web service Django+jquery ajax !9. 另一轮变更 提交订单 自定义many-to-many关系,实现Atom订阅 分页(Pagination)10. 用户和权限 使用内置的Amin管理用户 处理登录和注销 权限控制URLconf+MTV:Django眼中的MVC MVC是众所周知的模式,即:将应用程序分解成三个组成部分:model(模型),view(视图),和 controller(控制 器)。其中: M 管理应用程序的状态(通常存储到数据库中),并约束改变状态的行为(或者叫做“业务规则”)。 C 接受外部用户的操作,根据操作访问模型获取数据,并调用“视图”显示这些数据。控制器是将“模型”和“视图”隔离,并成为二者之间的联系纽带。 V 负责把数据格式化后呈现给用户。在Agile Web Development with Rails中有这样一张图对MVC模式进行了很好的解释:Django也是一个MVC框架。但是在Django中,控制器接受用户输入的部分由框架自行处理,所以 Django 里更关注的是模型(Model)、模板(Template)和视图(Views),称为 MTV模式:M 代表模型(Model),即数据存取层。 该层处理与数据相关的所有事务: 如何存取、如何验证有效性、包含哪些行为以及数据之间的关系等。T 代表模板(Template),即表现层。 该层处理与表现相关的决定: 如何在页面或其他类型文档中进行显示。V 代表视图(View),即业务逻辑层。 该层包含存取模型及调取恰当模板的相关逻辑。 你可以把它看作模型与模板之间的桥梁。需要注意的是,不能简单的把 Django 视图认为是MVC控制器,把 Django 模板认为MVC视图。 区别在于: Django 视图 不处理用户输入,而仅仅决定要展现哪些数据给用户; Django 模板 仅仅决定如何展现Django视图指定的数据。或者说, Django将MVC中的视图进一步分解为 Django视图 和 Django模板两个部分,分别决定 “展现哪些数据” 和 “如何展现”,使得Django的模板可以根据需要随时替换,而不仅仅限制于内置的模板。至于MVC控制器部分,由Django框架的URLconf来实现。URLconf设计非常巧妙,其机制是使用正则表达式匹配URL,然后调用合适的Python函数。虽然一开始有些不习惯,但是你很快就会喜欢上它,因为URLconf对于URL的规则没有任何限制,你完全可以设计成任意的URL风格,不管是传统的,RESTful的,或者是另类的。Django第一步对于一个web框架,掌握了三部分的内容,就可以说是迈出了第一步。1. 准备开发环境2. 创建一个工程,并运行3. 开发hello world应用1. 准备环境 首先应该是安装python和django。这点官方网站有很详细的说明,网上也有很多教程,这里就不再重复了,只是表达一个对操作系统的观点: Mac OS:对程序员和用户都很友好 Linux: 对程序员很友好 Widows:对用户貌似友好 到底使用哪个操作系统,仁者见仁。 然后是开发工具的选择。建议抛弃IDE,使用一个好的文本编辑器。强烈推荐vim。但如果你选择Emacs,我无话可说。2. 创建工程 Django作为一个web框架,第一步应该是能够在浏览器中看到页面。如果已经安装好环境的话。 首先创建工程:django-admin.py startproject depot,即开始创建名为depot的工程。 与rails相比,过程很安静,结果很干净。如下: depot/ _init_.py manage.py settings.py urls.py这几个文件的作用如下: _init_.py :Python的模块定义文件。 这是一个空文件,一般你不需要修改它。 manage.py :一个命令行工具,生成这个文件仅仅是为了方便。可以通过python manage.py help 查看该工具的功能。完全不需要编辑这个文件。 settings.py :该 Django 项目的设置或配置。 urls.py:Django项目的URL设置。 与rails不同,django初始工程的文件很少,可以很容易地阅读所有的代码。但这些文件已经构成了一个可运行的Django应用。进入工程目录并运行该工程:cd depot/python manage.py runserver可以看到一些提示信息:Validating models.0 errors foundDjango version 1.3, using settings 'depot.settings'Development server is running at http:/127.0.0.1:8000/Quit the server with CONTROL-C.29/Jan/2012 02:09:17 "GET / HTTP/1.1" 200 2049此时web server(开发环境!)已经运行了,用浏览器访问http:/127.0.0.1:8000/,可以看到如下的界面:说明Django已经开始工作了。3. hello Django! 与rails不同,django不需要生成controller,helper, view 等等一大堆文件,要实现一个hello程序,只需要几行代码。Django Web应用中通常包含URLconf, view, template, model 四个部分(参考URLconf+MTV:Django眼中的MVC)。但这些部分不是完全必需的。比如我们要实现一个最简单的 "hello, Django!", 只需要定义URLconf和view即可。 让我们把“需求”明确一下,hello,Django!实现如下功能:在浏览器中输入http:/127.0.0.1:8000/hello, 显示“hello Django!”。首先要实现一个视图(view) 来响应请求。在Django中视图是一个函数,该函数接受一个HttpRequest参数,并返回一个HttpResponse。我们可以在任何地方定义这个函数,但通常会放在Django app 的 views.py 文件中。在hello,Django中我们不需要创建一个Django app(因为不需要model),所以可以在project 目录中创建一个view.py 文件,并定义hello(request)视图函数:depot/views.py:python view plaincopy1. from django.http import HttpResponse 2. 3. def hello(request): 4. return HttpResponse("hello, Django!") 接下来是将前面定义的URL 映射到 这个视图函数。这是由URLconf完成的。URLconf的本质是 URL 模式以及要为该 URL 模式调用的视图函数之间的映射表。打开生成的urls.py文件,先在文件前面import刚才创建的view,然后在tuple类型的变量urlpatterns中加入hello的映射关系:python view plaincopy1. from django.conf.urls.defaults import patterns, include, url 2. from depot.views import hello 3. 4. urlpatterns = patterns('', 5. url(r'hello/$', hello), 6. ) 此时访问 http:/127.0.0.1:8000/hello, 将会显示 hello, Django!URLconf理解起来也很容易,即 urlpatterns中的每一项是一个二元组(正则表达式,视图函数)。当Django 接收到HTTP请求的时候,从urlpatterns中找到匹配的表达式,并将请求发生给对应的视图函数,最后视图函数返回一个HTTP响应,交给Django处理。如此而已。至此,Django的第一步已经迈出,你至少已经可以开始CGI风格的web开发了。Django实战(1):需求分析和设计Depot是Agile Web Development with Rails中的一个购物车应用。该书中用多次迭代的方法,逐步实现购物车应用,使很多人走上了rails开发的道路。遗憾的是Django世界中好像没有类似的指引,也许是因为pythoner 不需要具体的例子。但是如果通过这样一个例子能够让更多的人加入pythoner的队伍,也是一大幸事。本文首先回顾一下depot的需求,在后续内容中将会按照Agile Web Development with Rails中的迭代进度,逐步用Django实现depot购物车应用。在原例子的基础上,还会增加一些新的内容,以适应企业级应用的开发。同时,会尽量展示敏捷开发的特性。原书中,初始阶段的需求整理如下:角色 买方,卖方。用例 买方:浏览产品,创建订单 卖方:管理产品,处理订单,管理发货界面设计 买方界面包括: 目录页:可以选择一个产品,选中产品会打开购物车页,同时该产品被加入购物车 购物车页:显示所有已选择的产品,可以返回分类页,也可以进入支付页进行支付 订单页:填写一些要素信息,确认支付后显示收据页 收据页:通知买方订单已被接收 买方界面流程如下图所示: 卖方界面包括: 登录页:卖方要登录后才能使用系统,登录后通过菜单选择其要使用的功能 菜单页:选择维护产品或者查看订单 创建产品页:用于加入新的产品 产品信息页:显示已经加入的产品,可以进行修改或者删除 订单页:显示订单信息,可以忽略或者处理 卖方界面流程如下图所示:领域模型从界面设计中可以很容易得出初步的模型,如下图:以上,就是开始阶段所能得到的“需求”。尽管其中还有一些不确定的因素,但是敏捷方法认为应该尽快开始开发,这些不确定的因素会在后续的迭代过程中逐步明确。接下来,就可以开始第一轮迭代开发了。Django实战(2):创建第一个模型类从模型开始开发似乎是个好主意。一方面模型是整个应用的核心,实现了应用的业务数据和对业务数据进行操作的约束,而视图和模板只是向用户提供操作和展现这些数据的界面;另一方面模型相对于系统的其他部分更加稳定,将模型先确定下来有助于系统其他部分的实现。DDD(领域驱动设计)更进一步将模型中的核心对象抽取出来作为“领域模型”。从Depot应用来看,产品(Product) 应该是模型中的核心对象之一。就让我们先来实现Product模型。创建app我们可以从Django第一步中实现的工程开始。在继续之前,还要进行一些准备工作。Django约定必须要创建app才能使用模型。这也是Django的哲学之一:Django认为一个project包含很多个Django app;project提供配置文件,比如数据库连接信息、 安装的app清单、模板路径等等;而一个app是一套Django功能的集合,通常包括模型和视图,按Python的包结构的方式存在。app可以在多个project之间很容易的复用。比如Django自带的注释系统和自动管理界面。所以我们在原有工程的基础上还需要创建一个app。现在假设我们只需要一个app,并将其命名为depotapp。创建应用的脚本也是使用project目录下的managy.py:$python manage.py startapp depotapp就会在工程目录下创建一个depotapp目录:depotapp/ _init_.py models.py tests.py views.py用python代码定义数据库在Django的第一印象中介绍过,Django的设计是 以Python类的形式定义数据模型。之所以没有采用rails的运行时自动获取数据库schema的”魔术方式“,是出于以下的考虑: 1. 效率。运行时扫描数据库可能会带来性能问题。 2. 明确性。只通过Model类就完全知道数据库中有哪些字段,而不需要再切换到migration或schema文件中去查看,更不需要去查看数据库结构。 3. 一致性。你看到的只是Python代码,完全不需要将大脑切换到”数据库模式“,能极大提高开发效率。 4. 版本控制。rails中的数据库结构版本保存在一个个的migration文件中,这简直就是版本管理的”反模式“。Django的方式是管理Model代码文件的版本。 5. 可扩展性。可以定义数据库中不存在的”字段类型“。比如Email,URL,等等。当然,Django也提供从现有数据库表中自动扫描生成模型的工具。so,Agile Web Development with Rails中的做法是先创建数据库表:sql view plaincopy1. drop table if exists products; 2. create table products ( 3. id int not null auto_increment, 4. title varchar(100) not null, 5. description text not null, 6. image_url varchar(200) not null, 7. price decimal(10,2) not null, 8. primary key (id) 9. ); 然后再生成scaffold(包括model,controller,test,4个views等等)。而Django的做法是,编写下面的Model类:python view plaincopy1. depot/depotapp/models.py: 2. 3. from django.db import models 4. 5. class Product(models.Model): 6. title = models.CharField(max_length=100) 7. description = models.TextField() 8. image_url = models.CharField(max_length=200) 9. price = models.DecimalField(max_digits=8,decimal_places=2) 如同其他的ORM,ID字段是默认声明的,不需要单独处理.部署模型Django 中的每一件事情都需要明确声明,也就是说,没有你的允许,Django不会主动去碰你的代码。所以我们还需要在project中进行一些配置工作才能让app生效。不过这样的配置只需要做一次。首先要创建数据库并配置整个project的数据库连接,为了简单起见,使用sqlite数据库。在工程文件夹下创建db文件夹和sqlite数据库文件:$mkdir db$cd db$sqlite3 development.sqlite3然后修改配置文件settings.py, 将DATABASES改为:python view plaincopy1. DATABASES = 2. 'default': 3. 'ENGINE': 'django.db.backends.sqlite3', 4. 'NAME': 'db/development.sqlite3', 5. 'USER': '', 6. 'PASSWORD': '', 7. 'HOST': '', 8. 'PORT': '', 9. 10. 就完成了数据库的配置。还需要配置project让depotapp生效,还是在settings.py中,将INSTALLED_APPS改为:python view plaincopy1. INSTALLED_APPS = ( 2. #'django.contrib.auth', 3. #'django.contrib.contenttypes', 4. #'django.contrib.sessions', 5. #'django.contrib.sites', 6. #'django.contrib.messages', 7. #'django.contrib.staticfiles', 8. 9. # Uncomment the next line to enable the admin: 10. # 'django.contrib.admin', 11. # Uncomment the next line to enable admin documentation: 12. # 'django.contrib.admindocs', 13. 'depot.depotapp', 14. ) 接下来就可以使用模型了。先验证一下:$python manage.py validate0 errors found然后可以看一下这个Model将会生成什么样的数据库:$ python manage.py sqlall depotappBEGIN;CREATE TABLE "depotapp_product" ( "id" integer NOT NULL PRIMARY KEY, "title" varchar(100) NOT NULL, "description" text NOT NULL, "image_url" varchar(200) NOT NULL, "price" decimal NOT NULL);COMMIT;最后,将模型导入数据库:$ python manage.py syncdbCreating tables .Creating table depotapp_productInstalling custom SQL .Installing indexes .No fixtures found.至此,完成了第一个模型类的创建。Django实战(3):Django也可以有scaffoldrails有一个无用的”神奇“功能,叫做scaffold。能够在model基础上,自动生成CRUD的界面。说它无用,是因为rails的开发者David说,scaffold”不是应用程序开发的目的。它只是在我们构建应用程序时提供支持。当你设计出产品的列表该如何工作时,你依赖于“支架”“生成器”产生创建,更新,和删除的行为。然后在保留这个“动作”时你要替换由“生成器”生成的行为。有时候当你需要一个快速接口时,并且你并不在乎界面的丑陋,“支架”就足够用了。不要指望scaffold能满足你程序的所有需要"。说它神奇,是因为在rails中你不清楚他是怎么实现的。只告诉你一句话:约定优先于配置。只要名字xxx,就会xxx。说得人云里雾里,认为rails真是一个伟大的框架。在Django的世界中没有这种无用的东西。但是如果你一定要,可以很容易地创建这么一套东西。下面我们就在project中引入一个“插件”。前面说过,app可以在多个project之间很容易的复用,我们要引入的就是一个第三方的app,无需修改,只需要简单配置即可使用。这个app叫做django-groundwork 。它不实现具体的功能,而是扩展了manage.py 的命令,使得通过命令行可以生成一些代码/文件。下载django-groundwork的代码:$git clone $ls django-groundworkAUTHORS LICENSE README.rst django-groundwork将其中的django-groundwork文件夹复制到project文件夹,然后在settings.py中加入该app:python view plaincopy1. INSTALLED_APPS = ( 2. #'django.contrib.auth', 3. #'django.contrib.contenttypes', 4. #'django.contrib.sessions', 5. #'django.contrib.sites', 6. #'django.contrib.messages', 7. #'django.contrib.staticfiles', 8. 9. # Uncomment the next line to enable the admin: 10. # 'django.contrib.admin', 11. # Uncomment the next line to enable admin documentation: 12. # 'django.contrib.admindocs', 13. 'depot.depotapp', 14. 'django-groundwork', 15. ) 即完成了安装。(如果遇到了什么麻烦,也可以下载本文附带的源代码包)安装后,使用$python manage.py help可以看到,列出的可用命令中多了一个groundwork。其语法是:$python manage.py groundwork appname ModelName1 ModelName2接下来使用这个app为Product生成scaffold:$python manage.py groundwork depotapp Product,就会生成所谓的scaffold。 此时运行开发服务器(python manage.py runserver),就可以访问下面的地址:http:/localhost:8000/depotapp/product/list/ 访问Product列表,并链接到create,edit,view等界面。可以下载本次迭代的源代码:Django实战(4):scaffold生成物分析在上一节用一个插件生成了类似rails的scaffold,其实无非就是URLconf+MTV。让我们看看具体都生成了哪些东西。首先是“入口”的定义即URLconf,打开urls.py:python view plaincopy1. from django.conf.urls.defaults import patterns, include, url 2. from depot.views import hello 3. 4. urlpatterns = patterns('', 5. url(r'hello/ hello), 6. ) 7. urlpatterns += patterns ('', 8. (r'depotapp/', include('depotapp.urls'), 9. ) 上面的代码中增加的配置行表示:以depotapp开头的url由depotapp/urls.py文件进行处理。django的url配置中,除了(正则表达式,view函数)的方式外,还支持(正则表达式,include文件)的方式。通常把app自身相关的url写到自己的url配置文件中,然后在project中引用。接下来看一下生成的depotapp/urls.py的内容:python view plaincopy1. from django.conf.urls.defaults import * 2. from models import * 3. from views import * 4. 5. urlpatterns = patterns('', 6. (r'product/create/$', create_product), 7. (r'product/list/$', list_product ), 8. (r'product/edit/(?P<id>/+)/$', edit_product), 9. (r'product/view/(?P<id>/+)/$', view_product), 10. ) 将CRU(没有D)的URL映射到了视图。而视图在depotapp/views.py中定义:python view plaincopy1. from django import forms 2. from django.template import RequestContext 3. from django.http import HttpResponse, HttpResponseRedirect 4. from django.template.loader import get_template 5. from django.core.paginator import Paginator 6. from django.core.urlresolvers import reverse 7. 8. # app specific files 9. 10. from models import * 11. from forms import * 12. 13. 14. def create_product(request): 15. form = ProductForm(request.POST or None) 16. if form.is_valid(): 17. form.save() 18. form = ProductForm() 19. 20. t = get_template('depotapp/create_product.html') 21. c = RequestContext(request,locals() 22. return HttpResponse(t.render(c) 23. 24. 25. 26. def list_product(request): 27. 28. list_items = Product.objects.all() 29. paginator = Paginator(list_items ,10) 30. 31. 32. try: 33. page = int(request.GET.get('page', '1') 34. except ValueError: 35. page = 1 36. 37. try: 38. list_items = paginator.page(page) 39. except : 40. list_items = paginator.page(paginator.num_pages) 41. 42. t = get_template('depotapp/list_product.html') 43. c = RequestContext(request,locals() 44. return HttpResponse(t.render(c) 45. 46. 47. 48. def view_product(request, id): 49. product_instance = Product.objects.get(id = id) 50. 51. t=get_template('depotapp/view_product.html') 52. c=RequestContext(request,locals() 53. return HttpResponse(t.render(c) 54. 55. def edit_product(request, id): 56. 57. product_instance = Product.objects.get(id=id) 58. 59. form = ProductForm(request.POST or None, instance = product_instance) 60. 61. if form.is_valid(): 62. form.save() 63. 64. t=get_template('depotapp/edit_product.html') 65. c=RequestContext(request,locals() 66. return HttpResponse(t.render(c) 视图中的相关内容比较多,主要的是模板,其次还有模型类、Paginator分页器、Form表单等等。基本涵盖了典型的web应用交互的内容。Django实战(5):引入bootstrap,设置静态资源之前生成了Product类的scaffold,但是如同rails的开发者David所讲的那样,scaffold几乎没什么用。所以按照Agile Web Development with Rails 4th中的迭代计划,下一步的修改是美化list页面:但是这个界面还是太丑陋了。其实,有了bootstrap后,很多站点都变成了“又黑又硬”的工具条+“小清新”风格。我们即不能免俗,又懒得自已设计风格,不妨用bootstrap将产品清单界面重新设计成如下的风格:下面让我们来实现这个界面。显然web界面会使用一些静态资源(css,js,image等),要在Django中引入静态资源。Django在正式部署的时候对于静态资源有特殊的处理(怎么处理?),在开发阶段,可以有简单的方式让静态资源起作用。首选在project目录下面创建一个static目录,并将静态资源按合理的组织方式放入其中:static/ css/ bootstrap.min.css js/ images/ productlist.html其中productlist.html是请界面设计师实现的产品清单静态页面;css/bootstrap.min.css 是该页面使用的样式表,来自bootstrap,将来整个系统都将使用这一套样式风格;js目录现在为空,以后可以将javascript代码放在这里;images文件夹同理。我们可以看到,Django对于静态内容的管理非常符合管理。相比之下,rails要求你将静态内容放到很怪异的结构中:app/assets/ images/ javascripts/ stylesheets/界面设计师实现的界面要想运行起来,还需要修改相关的路径,或者改变自己的目录设置习惯。这种设计让人难以理解。回到Django,让静