Django Step by Step (十二)

作者:limodou
联系:limodou@gmail.com
版本:0.1
主页:http://wiki.woodpecker.org.cn/moin/NewEdit
BLOG:http://www.donews.net/limodou
版权:FDL

目录

1   引言

如果通讯录中的记录很多,我希望有一种搜索的方法,下面就让我们加一个搜索功能吧。当然,这个搜索功能是很简单的。在 Django 邮件列表中看到 WorldOnline(好象是它)有一个搜索的框架,可以定义哪些模块的哪些字段要参加搜索。这样在处理时会自动将相应的信息加入到搜索数据库中进行预处理。现在这个框架并没有开放源码,而且它底层使用的搜索的东西并不是 Django 本身的。这里我只是对姓名字段进行查找。

2   修改 templates/address/address_list.html

[...]
<hr>
<div id="content-main">
    <table border="0" width="500">
    <tr align="right"><td>
    <form method="GET" action="/address/search/">
    搜索姓名:<input name="search" type="text" value="{{ searchvalue }}"/>
    <input type="submit" value="提交"/>
    </form>
    </td></tr>
    </table>
    <table border="0" width="500">
    <tr align="right">
      <td>{% if has_previous %}
[...]

在显示分页的代码上面增加了搜索的处理。

从上面可以看到,条件输入处我增加了一个 searchvalue 的变量,希望在提交一个搜索后,显示页面的同时显示当前显示时使用的条件。

这里存在一个困难:如何把搜索条件,搜索字符串与通用 view 相关联呢?只要我们生成正确的 queryset(结果集) 即可。但这个结果集需要查询姓名为指定名称的记录,如何实现呢?

Note

在以前 object_list 可以传入 extra_lookup_kwargs 参数,但后来由于使用了 queryset ,则这个参数不再需要了。

通过 urls.py 我想是不行的,因为它只从 url 解析,而且对于 QUERY_STRING 是不进行解析的(QUERY_STRING 是指: http://example.com/add/?name=test? 后面的东西,也就是 name=test )。对于搜索条件,我会使用一个 form 来处理, method 会设为 GET ,因此生成的 url 中,查询条件正如这个例子,如: http://localhost:8000/address/search/?search=limodou 。这样无法变成上面所要用到的参数。

因此我决定自定义一个新的 view 方法。

3   修改 address/views.py

from django.views.generic.list_detail import object_list
def search(request):
    name = request.REQUEST['search']
    if name:
        extra_lookup_kwargs = {'name__icontains':name}
        extra_context = {'searchvalue':name}
#        return object_list(request, Address,
#            paginate_by=10, extra_context=extra_context,
#            extra_lookup_kwargs=extra_lookup_kwargs)
        return object_list(request, Address.objects.filter(name__icontains=name),
            paginate_by=10, extra_context=extra_context)
    else:
        return HttpResponseRedirect('/address/')

上述代码加到最后去。这里并没有完全重写,而是在 object_list 外面封装了一层,主要是生成要用在 object_list 的中参数。

Note

extra_context 是可以传入到模板中的上下文字典。

Note

name_icontains 是 Django 中过滤条件的写法。这里是说只要包含指定的字符的即可,而且不区分大小写。详细地要看 Django 的 DB-API 文档。

request.REQUEST ['search'] 或者从 GET 或者从 POST 中得到数据,是一个方便的用法。它将得到提交的查询姓名条件,如果存在,则生成 extra_lookup_kwargsextra_context 参数,然后按 object_list 的要求传入。如果没有提交,则回到 address 的起始页面。

4   修改 address/urls.py

from django.conf.urls.defaults import *
from newtest.address.models import Address

info_dict = {
#    'model': Address,
    'queryset': Address.objects.all(),
}
urlpatterns = patterns('',
    (r'^/?$', 'django.views.generic.list_detail.object_list',
        dict(paginate_by=10, **info_dict)),
    (r'^upload/$', 'address.views.upload'),
    (r'^output/$', 'address.views.output'),
    (r'^search/$', 'address.views.search'),
)

增加了一个 search 的 url 链接映射。

5   启动 server 测试

感觉这个通讯录也差不多了,现在让我们将其部署到 Apache 上去跑一跑吧。

但部署到 apache 时才知道,问题很多啊。主要问题如下:

  1. 模块名不全

    比如许多例子我都是从当前目录(newtest)下开始计算,因为在 Windows 下,Python_ 会自动将当前目录加入到 sys.path 中,因此直接使用 address.* 之类的不会出错,但在 Apache 下需要使用 newtest.address.* 这样的方式。必须按教程的方式处理。主要修改 urls.py 文件。

  1. 相对路径的问题

    许多使用相对路径的地方都不对了。必须使用绝对路径。不过这一点对于部署来说的确有些麻烦,好在要改动的地方不多,主要在 settings.py 中。如数据库名字(sqlite3),模板的位置。

其它的就是要注意的地方了。

6   部署到 Apache 上的体验

只能说是体验了,因为我不是 Apache 的专家,也不是 mod_python 的专家,因此下面的内容只能算是我个人的配置记录,希望对大家有所帮助。

6.1   安装 mod_python 模块

Django 对于 Apache 使用 2.X ,对于 mod_python 使用 3.X。安装 mod_python(在windows下)倒是不麻烦。但在 Django 的邮件列表中却有人对于 mod_python 和 Apache 有所讨论,主要的问题是这些改动相对较大,比如说复载,安装需要 root 权限,要重启 Apache 等。这的确是一个要注意的问题,因此有人建议使用 FastCGI 或 SCGI 来处理。

6.2   修改 httpd.conf 文件

Listen 127.0.0.1:8888
<VirtualHost 127.0.0.1:8888>
    <Location "/">
        SetHandler python-program
        PythonPath "['D:/project/svn/limodou/django-stepbystep/newtest'] + sys.path"
        PythonHandler django.core.handlers.modpython
        SetEnv DJANGO_SETTINGS_MODULE newtest.settings_apache
        PythonAutoReload Off
        PythonDebug On
    </Location>
    Alias /site_media d:/project/svn/limodou/django-stepbystep/newtest/newtest/media
    Alias /media C:/Python24/Lib/site-packages/Django-0.91-py2.4.egg/django/contrib/admin/media
    <Location "/site_media">
        SetHandler None
    </Location>
    <Location "/media">
        SetHandler None
    </Location>
</VirtualHost>

这里我使用了虚拟主机(参考文档) 来设置。即使用一台机器,不同的端口来对应不同的服务。主要原因是我希望 Django 的服务可以从 / 开始,但我还有其它的一些东西要处理,因此不希望对其它的东西有所影响。我没有两个域名,或两个 IP ,因此采用了两个不同的端口。这只是我的一种方式。因为我在本机处理,因此 IP 是 127.0.0.1 。实际中你应该进行修改。

上面 PythonPath 主要是将 newtest 的目录加入到 sys.path ,以便 Django 可以找到。需要使用绝对路径。

SetEvn 中设置的 DJANGO_SETTINGS_MODULE 就对应于你的 项目名.配置文件。因此为了能导入 项目名.配置文件,就需要前面的 PythonPath 的设置。

PythonDebugPythonAutoReload 建议在生产时设为 Off 。这里我还设了两个别名,用来指向 site_mediamedia 目录。

Alias /site_media
是用来将 newtest 的静态文件设置一个 URL 访问的别名。
Alias /media
是将 Django Admin 的静态文件设置一个 URL 的访问别名。

site_mediamediaLocation 中设置不进行脚本的解析。

Note

上面的 media 路径是指向 Django Admin 所在的目录。你完全可以将其拷贝出来,这样可能要方便得多。另外在 linux 下使用 ln 也相当的方便。

同时可以注意到 settings 我改为了 settings_apache 了。一方面将要把其中的内容有关相对路径的东西改为绝对路径,另一方面我还想保持现在的 settings.py

6.4   修改 settings_apache.py

将相对路径改为绝对路径。主要有:

  • DATABASE_NAME
  • MEDIA_ROOT
  • TEMPLATE_DIRS
  • STATIC_PATH

DEBUGTEMPLATE_DEBUG 改为 False 。这样静态文件 serverview 就无效了。这就是为什么上面的 Apache 的配置中要配置 site_media 的原因。

6.5   测试

http://localhost:8888/address

更详细的内容请参见 mod_python 文档。关于 admin 的 media 和 template 好象并不需要配置,大家有什么结果可以告诉我。

同时如果你不想每次重启 Apache 来进行测试,可以将:

MaxRequestsPerChild 0

改为:

MaxRequestsPerChild 1

7   后话

上面的步骤是直接把开发的东西发布到了 Apache 中去,但实际中开发与运行可能环境根本不一样,最主要可能就是数据库方面的变化,如果model变化,则有可能要编写数据切换程序。许多实际的问题都需要仔细地考虑。