本系列文章一共有两篇,本文是其中
第一篇。在这一篇文章中,我们将展示 Django
用法,Django 是 Python 编程语言驱动
一个开源模型-视图-控制器(MVC)风格
Web 应用程序框架。使用 Django,我们在几分钟之内就可以创建高品质、易维护、数据库驱动
应用程序。
Django 项目是一个定制框架,它源自一个在线新闻 Web 站点,于 2005 年以开源
形式被释放出来。Django 框架
核心组件有: 字串2
用于创建模型
对象关系映射 字串4
为最终用户设计
完美管理界面
一流
URL 设计
字串1
设计者友
模板语言
字串7
缓存系统 字串3
本文是有关 Python Web 框架
由两篇文章组成
系列文章
第一篇。第二篇文章将向您介绍 TurboGears 框架。 字串1
要使用并理解本文中提供
代码,则需要安装 Python,并了解在初学者
水平上如何使用 Python。要查看是否安装了 Python 以及 Python
版本号,可以输入 python -V。Django 至少需要 2.3.5 版本
Python,可以从 Python Web 站点上下载它(关于链接请参阅本文后面 参考资料 部分)。我们至少还应该顺便熟悉一下 MVC 架构。
安装 Django 字串4
本文使用了 Django
开发版本,以便能够利用 Django 框架
最新改进。建议您在 0.95 版正式发布之前使用这个版本。关于最新发行版本,请参阅 Django
Web 站点(再次请您参阅 参考资料 来获得链接)。 字串1
按照以下步骤下载并安装 Django:
清单 1. 下载并安装 Django~/downloads# svn co http://code.djangoproject.com/svn/django/trunk/ django_src
~/downloads# cd django_src
~/downloads# python setup.py install
Django 管理工具 字串3
在安装 Django 之后,您现在应该已经有了可用
管理工具 django-admin.py。清单 2 给出了这个管理工具中可以使用
一些命令:
清单 2. 使用 Django 管理工具~/dev$ django-admin.py
usage: django-admin.py action [options]
actions:
adminindex [modelmodule ...]
Prints the admin-index template snippet for the given model
module name(s).
... snip ...
startapp [appname]
Creates a Django app directory structure for the given app name
in the current directory.
startproject [projectname]
Creates a Django project directory structure for the given
project name in the current directory.
validate
Validates all installed models.
options:
-h, --help show this help message and exit
--settings=SETTINGS Python path to settings module, e.g.
\"myproject.settings.main\". If this isn't
provided, the DJANGO_SETTINGS_MODULE
environment variable will be used.
--pythonpath=PYTHONPATH
Lets you manually add a directory the Python 字串8
path, e.g. \"/home/djangoprojects/myproject\".
字串9
Django 项目和应用程序
要启动 Django 项,请使用 django-admin startproject 命令,如下所示: 字串6
清单 3. 启动项目~/dev$ django-admin.py startproject djproject
字串9
上面这个命令会创建一个 djproject 目录,其中包含了运行 Django 项目所需要
基本配置文件: 字串8
清单 4. djproject 目录
内容 字串2
__init__.py
manage.py
settings.py
urls.py 字串4 对于这个项目来说,我们要构建一个职位公告板应用程序 “jobs”。要创建应用程序,可以使用 manage.py 脚本,这是一个特定于项目
django-admin.py 脚本,其中 settings.py 文件可以自动提供: 字串7
清单 5. 使用 manage.py startapp
~/dev$ cd djproject
~/dev/djproject$ python manage.py startapp jobs 字串3
这将创建一个应用程序骨架,其中模型有一个 Python 模块,视图有另外一个 Python 模块。jobs 目录中包含以下文件: 字串1
清单 6. jobs 应用程序目录中
内容
__init__.py
models.py
views.py 字串7
提供应用程序在项目中
位置纯粹是为新 Django 开发人员建立
一种惯例,并不是必需
。一旦开始在几个项目中混合使用应用程序,就可以将应用程序放到自己
命名空间中,并使用设置和主 URL 文件将它们绑定在一起。现在,请按照下面给出
步骤执行操作。 字串2
为了使 Django 认识到新应用程序
存在,还需要向 settings.py 文件中
INSTALLED_APPS 添加一个条目。对于这个职位公告板应用程序来说,我们必须添加字符串 djproject.jobs:
字串1
清单 7. 向 settings.py 中添加一个条目 字串3
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'djproject.jobs',
) 字串5 创建一个模型
字串3
Django 提供了自己
对象关系型数据映射组件(object-relational mapper,ORM)库,它可以通过 Python 对象接口支持动态数据库访问。这个 Python 接口非常有用,功能十分强大,但如果需要,也可以灵活地不使用这个接口,而是直接使用 SQL。
字串1
ORM 目前提供了对 PostgreSQL、MySQL、SQLite 和 Microsoft® SQL 数据库
支持。
这个例子使用 SQLite 作为后台数据库。SQLite 是一个轻量级数据库,它不需要进行任何配置,自身能够以一个简单文件
形式存在于磁盘上。要使用 SQLite,可以简单地使用 setuptools 来安装 pysqlite(有关 setuptools
更多资料,尤其是有关 easy_install 工具(需要单独安装)
资料,请参阅 参考资料): 字串9
字串3
easy_install pysqlite
字串9
在使用这个模型之前,需要在设置文件中对数据库进行配置。SQLite 只需要指定数据库引擎和数据库名即可。
清单 8. 在 settings.py 中配置数据库 字串4
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = '/path/to/dev/djproject/database.db'
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = '' 字串9
这个职位公告板应用程序有两种类型
对象:Location 和 Job。Location 包含 city、state(可选)和 country 字段。Job 包含 location、title、description 和 publish date 字段。 字串5
清单 9. jobs/models.py 模块
字串2
from django.db import models
class Location(models.Model):
city = models.CharField(maxlength=50)
state = models.CharField(maxlength=50, null=True, blank=True)
country = models.CharField(maxlength=50)
def __str__(self):
if self.state:
return \"%s, %s, %s\" % (self.city, self.state, self.country)
else:
return \"%s, %s\" % (self.city, self.country)
class Job(models.Model):
pub_date = models.DateField()
job_title = models.CharField(maxlength=50)
job_description = models.TextField()
location = models.ForeignKey(Location)
def __str__(self):
return \"%s (%s)\" % (self.job_title, self.location) 字串6 __str__ 方法是 Python 中
一个特殊类,它返回对象
字符串表示。Django 在 Admin 工具中显示对象时广泛地使用了这个方法。 字串9
要设置这个模型
模式,请返回 manage.py
sql 命令。此时模式尚未确定。
字串9
清单 10. 使用 manage.py sql 命令查看数据库模式 字串9
~/dev/djproject$ python manage.py sql jobs
BEGIN;
CREATE TABLE \"jobs_job\" (
\"id\" integer NOT NULL PRIMARY KEY,
\"pub_date\" date NOT NULL,
\"job_title\" varchar(50) NOT NULL,
\"job_description\" text NOT NULL,
\"location_id\" integer NOT NULL
);
CREATE TABLE \"jobs_location\" (
\"id\" integer NOT NULL PRIMARY KEY,
\"city\" varchar(50) NOT NULL,
\"state\" varchar(50) NULL,
\"country\" varchar(50) NOT NULL
);
COMMIT; 字串7
为了初始化并安装这个模型,请运行数据库命令 syncdb:
~/dev/djproject$ python manage.py syncdb 字串9
注意,syncdb 命令要求我们创建一个超级用户帐号。这是因为 django.contrib.auth 应用程序(提供基本
用户身份验证功能)默认情况下是在 INSTALLED_APPS 设置中提供
。超级用户名和密码用来登录将在下一节介绍
管理工具。记住,这是 Django
超级用户,而不是系统
超级用户。 字串6
查询集 字串4
Django 模型通过默认
Manager 类 objects 来访问数据库。例如,要打印所有 Job
列表,则应该使用 objects 管理器
all 方法: 字串4
清单 11. 打印所有
职位
>>> from jobs.models import Job
>>> for job in Job.objects.all():
... print job 字串9 Manager 类还有两个过滤方法:一个是 filter,另外一个是 exclude。过滤方法可以接受满足某个条件
所有方法,但是排除不满足这个条件
其他方法。下面
查询应该可以给出相同
结果(“gte” 表示 “大于或等于”,而 “lt” 表示 “小于”)。 字串4
清单 12. 排除和过滤职位
>>> from jobs.models import Job
>>> from datetime import datetime
>>> q1 = Job.objects.filter(pub_date__gte=datetime(2006, 1, 1))
>>> q2 = Job.objects.exclude(pub_date__lt=datetime(2006, 1, 1)) 字串3 filter 和 exclude 方法返回一些 QuerySet 对象,这些对象可以链接在一起,甚至可以执行连接操作。下面
q4 查询会查找从 2006 年 1 月 1 日开始在俄亥俄州
Cleveland 张贴
职位: 字串6
清单 13. 对职位进行更多
排除和过滤
字串7
>>> from jobs.models import Job
>>> from datetime import datetime
>>> q3 = Job.objects.filter(pub_date__gte=datetime(2006, 1, 1))
>>> q4 = q3.filter(location__city__exact=\"Cleveland\",
... location__state__exact=\"Ohio\") 字串3
QuerySets 是惰性
,这一点非常不错。这意味着只在对数据库进行求值之后才会对它们执行查询,这会比立即执行查询
速度更快。 字串2
这种惰性利用了 Python
分片(slicing)功能。下面
代码并没有先请求所有
记录,然后对所需要
记录进行分片,而是在实际
查询中使用了 5 作为 OFFSET、10 作为 LIMIT,这可以极大地提高性能。 字串3
字串4
清单 14. Python 分片
字串7
>>> from jobs.models import Job
>>> for job in Job.objects.all()[5:15]
... print job 字串1 注意:使用 count 方法可以确定一个 QuerySet 中有多少记录。Python
len 方法会进行全面
计算,然后统计那些以记录形式返回
行数,而 count 方法执行
则是真正
SQL COUNT 操作,其速度更快。我们这样做,数据库管理员会感激我们
。
字串4
清单 15. 统计记录数 字串9
>>> from jobs.models import Job
>>> print \"Count = \", Job.objects.count() # GOOD!
>>> print \"Count = \", len(Job.objects.all()) # BAD! 字串3 有关
更多信息,请参阅 参考资料 部分给出
Django “Database API reference”
链接。
管理员工具 字串2
Django
最大卖点之一是其一流
管理界面。这个工具是按照最终用户
思路设计
。它为我们
项目提供了很多数据输入工具。
字串2
管理工具是 Django 提供
一个应用程序。与 jobs 应用程序一样,在使用之前也必须进行安装。第一个步骤是将应用程序
模块(django.contrib.admin)添加到 INSTALLED_APPS 设置中:
字串8
清单 16. 修改 settings.py 字串4
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'djproject.jobs',
'django.contrib.admin',
) 要让该管理工具可以通过 /admin URL 使用,只需要简单地取消项目
urls.py 文件中提供
对应行
内容即可。下一节将详细介绍 URL
配置。
字串6
清单 17. 使管理工具可以通过 urls.py 使用
字串8
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^admin/', include('django.contrib.admin.urls.admin')),
) 这个管理应用程序有自己
数据库模型,但也需要进行安装。我们可以再次使用 syncdb 命令来完成这个过程:
python manage.py syncdb 字串3
要查看这个管理工具,可以使用 Django 提供
测试服务器。
字串3
清单 18. 使用测试服务器来查看管理工具 字串2
~/dev/djproject$ python manage.py runserver
Validating models...
0 errors found.
Django version 0.95 (post-magic-removal), using settings 'djproject.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C (Unix) or CTRL-BREAK (Windows). 字串8
现在可以使用 http://localhost:8000/admin 启动管理工具,并使用前面创建
超级用户帐号进行登录。我们注意到现在还没有可用
模块。 字串2
要让一个类可以通过管理工具进行访问,我们需要为其创建一个 Admin 子类。然后可以通过为这个子类添加类属性来定制如何对每个类进行管理。清单 19 展示了如何将 Location 类添加到这个管理工具中。
清单 19. 使用管理工具添加 Location 类
class Location(meta.Model):
...
class Admin:
list_display = (\"city\", \"state\", \"country\") 字串1 现在就可以通过管理界面来创建、更新和删除 Location 记录了。
图 1. 使用管理工具编辑位置

可以按照 list_display 类
属性指定
城市、州和国家来列出记录并对它们进行排序。 字串6
图 2. 使用管理工具显示位置 字串4

管理工具有无数用来管理每种模块类
选项。清单 20 给出了几个适用于 Job 类
例子: 字串7
清单 20. 管理模块类
选项 字串2
class Job(meta.Model):
...
class Admin:
list_display = (\"job_title\", \"location\", \"pub_date\")
ordering = [\"-pub_date\"]
search_fields = (\"job_title\", \"job_description\")
list_filter = (\"location\",) 字串9 字串2
根据以上设置,职位
标题、位置和发布日期都会在显示职位记录时用到。职位可以按照发布时间进行排序,最开始是最近发布
职位(减号表示降序)。用户可以按照标题和说明来查找职位,管理员可以根据位置对记录进行过滤。 字串8
图 3. 使用管理工具显示职位

设计 URL 方案
字串2
Django URL 分发系统使用了正则表达式配置模块,它可以将 URL 字符串模式映射为 Python 方法 views。这个系统允许 URL 与底层代码完全脱节,从而实现最大
控制和灵活性。
字串9
urls.py 模块被创建和定义成 URL 配置
默认起点(通过 settings.py 模块中
ROOT_URLCONF 值)。URL 配置文件
惟一要求是必须包含一个定义模式 urlpatterns
对象。
字串6
这个职位公告板应用程序会在启动时打开一个索引和一个详细视图,它们可以通过以下
URL 映射进行访问: 字串5
/jobs 索引视图:显示最近
10 个职位 字串9
/jobs/1 详细视图:显示 ID 为 1
职位信息
这两个视图(索引视图和详细视图)都是在这个 jobs 应用程序
views.py 模块中实现
。在项目
urls.py 文件中实现这种配置看起来如下所示:
清单 21. 在 djproject/urls.py 中实现视图
配置 字串5
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^admin/', include('django.contrib.admin.urls.admin')),
(r'^jobs/$', 'djproject.jobs.views.index'),
(r'^jobs/(?Pd )/$', 'djproject.jobs.views.detail'),
) 字串9
注意
最佳实践是提取出应用程序特有
URL 模式,并将它们放入应用程序自身中。这样可以取消应用程序与项目
耦合限制,从而更
地实现重用。jobs 使用
应用程序级
URL 配置文件如下所示:
清单 22. 应用程序级
URL 配置文件 urls.py
字串9
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^$', 'djproject.jobs.views.index'),
(r'^(?Pd )/$', 'djproject.jobs.views.detail'),
) 字串5
由于 view 方法现在都是来自同一个模块,因此第一个参数可以使用这个模块
根名称来指定 djproject.jobs.views,Django 会使用它来查找 index 方法和 detail 方法:
清单 23. jobs/urls.py:查找 index 和 detail 方法 字串3
from django.conf.urls.defaults import *
urlpatterns = patterns('djproject.jobs.views',
(r'^$', 'index'),
(r'^(?Pd )/$', 'detail'),
) 字串2 尝试上面
jobs URL 会返回到这个项目中,因为它们是使用 include 函数将其作为一个整体来实现
。应用程序级
URL 被绑定到下面
/jobs 部分:
清单 24. djproject/urls.py:将 URL 送回该项目 字串1
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^admin/', include('django.contrib.admin.urls.admin')),
(r'^jobs/', include('djproject.jobs.urls')),
) 字串7 如果现在尝试使用测试服务器来访问索引页(http://localhost:8000/jobs),会得到一个错误,因为正在调用
视图(djproject.jobs.views.index)不存在。
实现视图
视图是一个简单
Python 方法,它接受一个请求对象,负责实现: 字串4
任何业务逻辑(直接或间接)
字串9
上下文字典,它包含模板数据
使用一个上下文来表示模板 字串8
响应对象,它将所表示
结果返回到这个框架中
字串2
在 Django 中,当一个 URL 被请求时,所调用
Python 方法称为一个视图(view),这个视图所加载并呈现
页面称为模板(template)。由于这个原因,Django 小组将 Django 称为一个 MVT(model-view-template)框架。另一方面,TurboGears 把自己
方法称作控制器(controller),将所呈现
模板称为视图(view),因此缩写也是 MVC。其区别在于广义
语义,因为它们所实现
内容是相同
。
最简单
视图可能会返回一个使用字符串初始化过
HttpResponse 对象。创建下面
方法,并生成一个 /jobs HTTP 请求,以确保 urls.py 和 views.py 文件都已经正确设置。
字串3
清单 25. jobs/views.py (v1)
字串5
from django.utils.httpwrappers import HttpResponse
def index(request):
return HttpResponse(\"Job Index View\") 字串4
下面
代码将获取最近
10 个职位,并通过一个模板呈现出来,然后返回响应。没有 下一节 中
模板文件,这段代码就无法 正常工作。 字串7
清单 26. jobs/views.py (v2) 字串3
from django.template import Context, loader
from django.http import HttpResponse
from jobs.models import Job
from django.template import Context, loader
from django.http import HttpResponse
from jobs.models import Job
def index(request):
object_list = Job.objects.order_by('-pub_date')[:10]
t = loader.get_template('jobs/job_list.html')
c = Context({
'object_list': object_list,
})
return HttpResponse(t.render(c)) 字串3
在上面
代码中,模板是由 jobs/job_list.html 字符串进行命名
。该模板是使用名为 object_list
职位列表
上下文呈现
。所呈现
模板字符串随后被传递到 HTTPResponse 构造器中,后者通过这个框架被发送回请求客户机那里。 字串2
加载模板、创建内容以及返回新响应对象
步骤在下面都被 render_to_response 方法取代了。新增内容是详细视图方法使用了一个 get_object_or_404 方法,通过该方法使用所提供
参数获取一个 Job 对象。如果没有找到这个对象,就会触发 404 异常。这两个方法减少了很多 Web 应用程序中
样板代码。 字串4
清单 27. jobs/views.py (v3) 字串7
from django.shortcuts import get_object_or_404, render_to_response
from jobs.models import Job
def index(request):
object_list = Job.objects.order_by('-pub_date')[:10]
return render_to_response('jobs/job_list.html',
{'object_list': object_list})
def detail(request, object_id):
job = get_object_or_404(Job, pk=object_id)
return render_to_response('jobs/job_detail.html',
{'object': job}) 字串4
注意,detail 使用 object_id 作为一个参数。这是前面提到过
jobs urls.py 文件中 /jobs/ URL 路径后面
数字。它以后会作为主键(pk)传递给 get_object_or_404 方法。
字串6
上面
视图仍然会失败,因为它们所加载和呈现
模板(jobs/job_list.html and jobs/job_detail.html)不存在。 字串7
创建模板 字串9
Django 提供了一种模板语言,该语言被设计为能够快速呈现且易于使用。Django 模板是利用 {{ variables }} 和 {% tags %} 中嵌入
文本创建
。变量会使用它们表示
值进行计算和替换。标记用来实现基本
控制逻辑。模板可以用来生成任何基于文本
格式,包括 HTML、XML、CSV 和纯文本。 字串8
第一个步骤是定义将模板加载到什么地方。为了简便起见,我们需要在 djproject 下面创建一个 templates 目录,并将这个路径添加到 settings.py
TEMPLATE_DIRS 条目中:
字串4
清单 28. 在 settings.py 中创建一个 templates 目录 字串3
TEMPLATE_DIRS = (
'/path/to/devdir/djproject/templates/',
) 字串9 Django 模板支持称为模板继承(template inheritance)
概念,它允许站点设计人员创建一个统一
外表,而不用替换每个模板
内容。我们可以通过使用块标记定义骨干文档或基础文档来使用继承。这些块标记都是使用一些包含内容
页面模板来填充
。这个例子给出了一个包含称为 title、extrahead 和 content
块
HTML 骨干:
字串6
清单 29. 骨干文档 templates/base.html
字串9
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
Company Site: {% block title %}Page{% endblock %}
{% block extrahead %}{% endblock %}
{% block content %}{% endblock %}
字串6
为了取消应用程序与项目之间
耦合,我们使用了一个中间基本文件作为 Job 应用程序所有页面文件
基础。对于这个例子来说,为了简便起见,我们将应用程序
CSS 放到这个基本文件中。在实际
应用程序中,需要有一个正确配置
Web 服务器,将这个 CSS 提取出来,并将其放到 Web 服务器所服务
静态文件中。
清单 30. 中间基础文件 templates/jobs/base.html
字串2
{% extends \"base.html\" %}
{% block extrahead %}
{% endblock %} 字串7
字串1
默认情况下,Django 测试服务器并不会为静态文件提供服务,因为这是 Web 服务器
工作。但是在开发过程中,如果您希望 Django 可以提供图像、样式表等,那么请参阅 参考资料 中有关如何激活这个特性
链接。
现在我们要创建视图所加载并呈现
两个页面模板。jobs/job_list.html 模板简单地循环遍历 object_list,它通过索引视图遍历其内容,并显示一个到每条记录
详细页面
链接。 字串7
清单 31. templates/jobs/job_list.html 模板
字串7
{% extends \"jobs/base.html\" %}
{% block title %}Job List{% endblock %}
{% block content %}
Job List
{% for job in object_list %}
- {{ job.job_title }}
{% endfor %}
{% endblock %} jobs/job_detail.html 页面会显示一条称为 job
记录: 字串1
清单 32. templates/jobs/job_detail.html 页面 字串3
{% extends \"jobs/base\" %}
{% block title %}Job Detail{% endblock %}
{% block content %}
Job Detail
{{ job.job_title }}
-
{{ job.location }}
Posted: {{ job.pub_date|date:\"d-M-Y\" }}
{{ job.job_description }}
{% endblock %} 字串3 Django 模板语言已经被设计为只能实现有限
功能。这种限制可以为非程序员保持模板
简单性,同时还可以让程序员不会将业务逻辑放到不属于自己
地方,即表示层。请参阅 参考资料 中模板语言文档
链接。
字串8
通用视图
字串7
Django 提供了 4 种通用视图(generic view),它们可以让开发人员创建遵循典型模式
应用程序:
字串2
页面列表/详细页面(与上面
例子类似) 字串7
基于数据
记录分类(对于新闻或 blog 站点非常有用) 字串2
字串2
对象
创建、更新和删除(CRUD)
简单直接
模板表示或简单地对 HTTP 重新进行定向
我们没有创建样板视图方法,而是将所有
业务逻辑都放入了 urls.py 文件中,它们都由 Django 提供
通用视图进行处理。
字串9
清单 33. jobs/urls.py 中
通用视图 字串5
from django.conf.urls.defaults import *
from jobs.models import Job
info_dict = {
'queryset': Job.objects.all(),
}
urlpatterns = patterns('django.views.generic.list_detail',
(r'^$', 'object_list', info_dict),
(r'^(?Pd )/$', 'object_detail', info_dict),
) 字串2 这个 urls.py 文件中
3 个主要变化如下: 字串5
info_dict 映射对象会为要访问
Job 提供一个查询集。 字串6
它使用了 django.views.generic.list_detail,而不是 djproject.jobs.views。 字串5
真正
视图调用是 object_list 和 object_detail。
字串6
这个项目需要遵循一些要求才能让通用视图自动工作: 字串2
通用详细视图期望获得一个 object_id 参数。 字串9
模板遵循下面
命名模式:app_label/model_name_list.html (jobs/job_list.html) app_label/model_name_detail.html (jobs/job_detail.html) 字串2
列表模板处理一个名为 object_list
列表。 字串9
详细模板处理一个名为 object
对象。 字串5
更多选项可以通过 info_dict 来传递,其中包括指定每个页面中对象个数
paginate_by 值。
结束语 字串6
本系列
下一篇文章将介绍 TurboGears,这是另外一个 Python Web 框架;并且会将该框架与 Django 进行比较。
字串9
字串4![我要研发网[www.51dev.com]](/templets/images/toplogo.gif)
