Python 下的 ip2long 和 long2ip

PHP 提供了 ip2long 和 long2ip 两个函数,实现了 IP 字符串与其对应整型值之间的转换。而在 Python 中没有类似的函数,所以自己实现了一下,很简单:

import socket
import struct

def ip2long(ipstr):
    return struct.unpack("!I", socket.inet_aton(ipstr))[0]

def long2ip(ip):
    return socket.inet_ntoa(struct.pack("!I", ip))

测试:

>>> ip2long(‘127.0.0.1’)
2130706433
>>> long2ip(2130706433)
‘127.0.0.1’

— EOF —

lighttpd + webpy 启动时出错

调了大半天,火挺大,还好总算解决了。

运行环境:

  • gentoo
  • python 2.5.2
  • lighttpd 1.4.21
  • webpy 0.31

直接跑 /work/www/test/main.py 没问题,交给跑 fastcgi 就有问题,错误提示:

2009-02-22 06:09:47: (log.c.97) server started
2009-02-22 06:09:47: (mod_fastcgi.c.1051) the fastcgi-backend /work/www/test/main.py failed to start:
2009-02-22 06:09:47: (mod_fastcgi.c.1055) child exited with status 1 /work/www/test/main.py
2009-02-22 06:09:47: (mod_fastcgi.c.1058) If you’re trying to run PHP as a FastCGI backend, make sure you’re using the FastCGI-enabled version.
You can find out if it is the right one by executing ‘php -v’ and it should display ‘(cgi-fcgi)’ in the output, NOT ‘(cgi)’ NOR ‘(cli)’.
For more information, check http://trac.lighttpd.net/trac/wiki/Docs%3AModFastCGI#preparing-php-as-a-fastcgi-programIf this is PHP on Gentoo, add ‘fastcgi’ to the USE flags.
2009-02-22 06:09:47: (mod_fastcgi.c.1365) [ERROR]: spawning fcgi failed.

看了下lighttpd 的代码,child exited with status 1 是程序正常退出,看来不像是 lighttpd 的问题了,问题可能出在 flup 上?

一直在 lighttpd 和 flup 上纠结了很久,还是没找出问题所在。

最后把 main.py 写成最简单的:

import web

urls = (
    ‘/(.*)’, ‘hello’
)
app = web.application(urls, globals())

class hello:       
    def GET(self, name):
        if not name:
            name = ‘world’
        return ‘Hello, ‘ + name + ‘!’

if __name__ == “__main__”:
    app.run()

好家伙,居然可以跑起来!看样子是程序的问题了。

不断删除程序代码后,最终定位问题位于 import MySQLdb 语句上,每当执行这句时就出错,注释掉就正常了。

可是用 python 命令行测试 import MySQLdb 又正常,见鬼了!

难道是 www 用户的 python 环境变量设置有问题?在 webpy 时直接输出后如下,和在 python 命令行里执行的一样:

[”, ‘/usr/lib64/python2.5/site-packages/MySQL_python-1.2.2-py2.5-linux-x86_64.egg’, ‘/usr/lib64/python2.5/site-packages/flup-1.0.1-py2.5.egg’, ‘/usr/lib64/portage/pym’, ‘/usr/lib64/python25.zip’, ‘/usr/lib64/python2.5’, ‘/usr/lib64/python2.5/plat-linux2’, ‘/usr/lib64/python2.5/lib-tk’, ‘/usr/lib64/python2.5/lib-dynload’, ‘/usr/lib64/python2.5/site-packages’]

好吧,彻底没折,直接让程序报错好了:

#!/usr/bin/env python

import sys
import web

urls = (
    ‘/(.*)’, ‘hello’
)
app = web.application(urls, globals())

class hello:
    def GET(self, name):
        if not name:
            try:
                import MySQLdb
            except Exception, e:
                return str(e);
        return ”

if __name__ == “__main__”:
    app.run()

这下子真相大白:

Can’t extract file(s) to egg cache The following error occurred while trying to extract file(s) to the Python egg cache: [Errno 13] Permission denied: ‘/root/.python-eggs’ The Python egg cache directory is currently set to: /root/.python-eggs Perhaps your account does not have write access to this directory? You can change the cache directory by setting the PYTHON_EGG_CACHE environment variable to point to an accessible directory.

见鬼了,egg 也有缓存?干啥用滴? 暂且不管干什么用的,为什么默认要把 PYTHON_EGG_CACHE 设置到 /root 目录下呢,因为我是以 root 用户安装的?好傻。 用 baidu 和 google 查找“The following error occurred while trying to extract file(s) to the Python egg”,结果就多了。 可以修改缓存文件夹的位置或权限,也可以把 egg 直接解压。 权限已经让我纠结这么久了,我就不用这种方式了,直接解压: 解压前先从 easy-install.pth 中删除 MySQLdb 的 egg 包路径:

# cd /usr/lib64/python2.5/site-packages/
# mkdir t
# unzip MySQL_python-1.2.2-py2.5-linux-x86_64.egg -d t
# rm MySQL_python-1.2.2-py2.5-linux-x86_64.egg
# mv t MySQL_python-1.2.2-py2.5-linux-x86_64.egg

从 sys.path 中可以看到,我的 flup 也是 egg,顺带也把它解压了:

# mkdir t
# unzip flup-1.0.1-py2.5.egg -d t
# rm flup-1.0.1-py2.5.egg
# mv t flup-1.0.1-py2.5.egg

终于搞定!

— EOF —

Django 快速实战入门(八):部署 Django

本节中,我们要彻底脱离测试服务器,以 gentoo+nginx+flup 为例,把我们的留言板部署到正式的服务器中。

在我们的 Web 服务器架构中,nginx 作为前端的 HTTP 服务器,而 flup 在后端以 fastcgi 的方式运行。在 nginx 中设置所有对动态内容的请求都交由后端的 flup 来处理。

安装配置 nginx

如果使用 gentoo 的 emerge 安装,则直接执行以下命令即可安装 nginx:

USE=fastcgi emerge nginx

如果要进行编译方式安装,在运行 ./configure 时请启动 fastcgi,可以参考以下选项:

gentoo ~ # /usr/sbin/nginx -V
nginx version: nginx/0.7.19
configure arguments: –prefix=/usr –conf-path=/etc/nginx/nginx.conf –http-log-path=/var/log/nginx/access_log –error-log-path=/var/log/nginx/error_log –pid-path=/var/run/nginx.pid –http-client-body-temp-path=/var/tmp/nginx/client –http-proxy-temp-path=/var/tmp/nginx/proxy –http-fastcgi-temp-path=/var/tmp/nginx/fastcgi –with-md5-asm –with-md5=/usr/include –with-sha1-asm –with-sha1=/usr/include –with-http_realip_module –with-http_ssl_module –with-http_perl_module

配置 /etc/nginx/nginx.conf:

user nginx nginx;
worker_processes 1;

error_log /var/log/nginx/error_log info;

events {
        worker_connections  8192;
        use epoll;
}

http {
        include         /etc/nginx/mime.types;
        default_type    application/octet-stream;
        charset         utf-8;

        log_format main
                ‘$remote_addr – $remote_user [$time_local] ‘
                ‘”$request” $status $bytes_sent ‘
                ‘”$http_referer” “$http_user_agent” ‘
                ‘”$http_x_forwarded_for” “$gzip_ratio”‘;

        client_header_timeout   10m;
        client_body_timeout     10m;
        send_timeout            10m;

        connection_pool_size            256;
        client_header_buffer_size       1k;
        large_client_header_buffers     4 2k;
        request_pool_size               4k;

        gzip on;
        gzip_min_length 1100;
        gzip_buffers    4 8k;
        gzip_types      text/plain;

        output_buffers  1 32k;
        postpone_output 1460;

        sendfile        on;
        tcp_nopush      on;
        tcp_nodelay     on;

        keepalive_timeout       75 20;

        ignore_invalid_headers  on;

        index index.html;

        include sites/*.enable;
}

新建 /etc/nginx/sites/myblog.enable:

server {
        listen          80;
        server_name     192.168.1.6;
        charset         utf-8;

        access_log      /var/log/nginx/myblog.access.log main;
        error_log       /var/log/nginx/myblog.error.log info;

        location /media {
                root    /home/www/myblog/media;
        }

        location / {
                include flup_params;
        }
}

管理后台中有部分静态资源,将它们复制到 /home/www/myblog/media 目录下:

cp -r /usr/lib/python2.4/site-packages/django/contrib/admin/media /home/www/myblog/media

/etc/nginx/flup_params 内容为:

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;
fastcgi_param  PATH_INFO          $fastcgi_script_name;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with –enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

fastcgi_pass   127.0.0.1:2380;

这里设置 fastcgi 的 ip:port 为 127.0.0.1:2380。

配置完毕后重新启动 nginx:

/etc/init.d/nginx restart

安装 flup 并启动 fastcgi

如果使用 gentoo 的 emerge 方式安装,输入以下命令即可:

emerge -pv flup

如果使用 Python eggs 方式安装,则需要到 flup 官网 http://trac.saddi.com/flup 上下载压缩包,再进行安装:

gentoo ~ # wget http://www.saddi.com/software/flup/dist/flup-1.0.1.tar.gz
gentoo ~ # tar zxf flup-1.0.1.tar.gz
gentoo ~ # cd flup-1.0.1
gentoo flup-1.0.1 # python setup.py install

启动 fastcgi:

python manage.py runfcgi host=127.0.0.1 port=2380

打开 http://192.168.1.6/message/http://192.168.1.6/admin/, 看看是否与测试服务器时一致呢?

结束

本文到此已结束,希望通过本文你已经大致了解 Django 的 MVT 架构,并能利用 Django 进行应用开发。

最后附上文中代码的压缩包:myblog.zip

— EOF —

Django 快速实战入门(七):admin 应用

管理后台对于每个网站来说都是必须,在管理后台中,我们可以添加、编辑和删除网站内容。

Django 中引入了 admin 应用,可以帮助我们快速地建立管理后台。

下面,我们就要利用 Django 附带的 admin 应用来为我们的留言板创建一个管理后台。

激活 admin 应用

修改 settings.py,在 INSTALLED_APPS 列表中加入 ‘django.contrib.admin’:

INSTALLED_APPS = (
    ‘django.contrib.auth’,
    ‘django.contrib.contenttypes’,
    ‘django.contrib.sessions’,
    ‘django.contrib.sites’,
    ‘django.contrib.admin’,
    ‘myblog.messages’,
)

在第五节创建留言板的数据模型过程中,为了在安装模型过程中不至于产生许多不需要的表,我们在 settings.py 中禁用了许多应用。在这里,因为 admin 应用依赖于 ‘django.contrib.auth’、’django.contrib.contenttypes’ 和 ‘django.contrib.sessions’,因此我们需要同时重新激活它们。另外,如果需要对多个站点进行管理,还需要激活 ‘django.contrib.sites’。

激活后,我们需要再执行一次同步数据库的操作,来将这些应用中需要用到的表安装到 MySQL:

gentoo myblog # python manage.py syncdb
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log

You just installed Django’s auth system, which means you don’t have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use ‘root’):
E-mail address: hily@test.com
Password:
Password (again):
Superuser created successfully.
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model

如上所示,安装过程中会提示您创建用于管理后台的超级管理员的用户名和密码,按照提示输入即可。

安装完成后,MySQL 中的数据表如下:

mysql> show tables;
+—————————-+
| Tables_in_mydb             |
+—————————-+
| auth_group                 |
| auth_group_permissions     |
| auth_message               |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_session             |
| django_site                |
| messages_message           |
+—————————-+
12 rows in set (0.01 sec)

配置管理后台入口

修改项目目录下的 urls.py,将以下行前面的注释去掉即可:

from django.contrib import admin
admin.autodiscover()

(r’^admin/(.*)’, admin.site.root),

访问 http://192.168.1.6:8080/admin/,就可以看到后台管理的登录界面了:

使用刚刚设置的超级管理员用户名和密码就可以登录到管理后台了:

将留言板加入后台管理

到此为止,我们还看不到留言板的管理界面,在这一步我们来实现把留言板加入到管理界面中。

Django 提供了一种简易的方法来将需要进行管理的数据模型添加到管理列表中:

admin.site.register(ModelName)

ModelName 就是需要添加到后台管理菜单中的数据模型类名。

在留言板应用目录 messages/ 下新建一个 admin.py(也可以用其它名称),存储后台的管理菜单配置信息:

from django.contrib import admin
from myblog.messages.models import Message

admin.site.register(Message)

重新启动测试服务器,进入后台,这时候在菜单列表中就出现了 Messages:

点击 Messages 后就可以看到留言的列表了。在这里我们重新修改一下 models.py 中 Message 的 __str__,以便留言列表看起来更简洁:

from django.db import models

class Message(models.Model):
    name = models.CharField(max_length=30)
    email = models.EmailField()
    homepage = models.URLField()
    title = models.CharField(max_length=200)
    content = models.CharField(max_length=500)
    time = models.DateTimeField(auto_now_add=True)
    ip = models.CharField(max_length=20)

def __str__(self):
        return ‘%s: %s’ % (self.name, self.title)

留言列表效果截图:

编码问题

在点击第一条记录“江:留言测试”时,Django 返回错误提示,类似如下:

TemplateSyntaxError at /admin/messages/message/5/

Caught an exception while rendering: 'ascii' codec can't encode character u'\u6c5f' in position 0: ordinal not in range(128)

Original Traceback (most recent call last):
  File "/usr/lib/python2.4/site-packages/django/template/debug.py", line 71, in render_node
    result = node.render(context)
  File "/usr/lib/python2.4/site-packages/django/template/debug.py", line 87, in render
    output = force_unicode(self.filter_expression.resolve(context))
  File "/usr/lib/python2.4/site-packages/django/template/__init__.py", line 559, in resolve
    new_obj = func(obj, *arg_vals)
  File "/usr/lib/python2.4/site-packages/django/template/defaultfilters.py", line 37, in _dec
    args[0] = force_unicode(args[0])
  File "/usr/lib/python2.4/site-packages/django/utils/encoding.py", line 52, in force_unicode
    s = unicode(str(s), encoding, errors)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u6c5f' in position 0: ordinal not in range(128)

这是编码类型有问题造成的,在多语言编码环境中,__str__ 经常会发生问题,因为它只支持 ascii 编码。所以我们修改 messages/models.py 中 Message 的 __str__,将其改为 __unicode__,问题就可以解决了:

from django.db import models

class Message(models.Model):
    name = models.CharField(max_length=30)
    email = models.EmailField()
    homepage = models.URLField()
    title = models.CharField(max_length=200)
    content = models.CharField(max_length=500)
    time = models.DateTimeField(auto_now_add=True)
    ip = models.CharField(max_length=20)

def __unicode__(self):
        return ‘%s: %s’ % (self.name, self.title)

编辑留言页面截图:

定制管理界面

也许你对现在看到的管理界面还不满意,那么你可以通过模板继承的方式从默认模板中继承,并对相应部分进行修改。

Django 管理界面的默认模板位于类似以下路径中,每个管理界面都有一个模板与它相对应:

gentoo myblog # ls -F /usr/lib/python2.4/site-packages/django/contrib/admin/templates/admin/
404.html          change_list_results.html  login.html
500.html          date_hierarchy.html       object_history.html
app_index.html    delete_confirmation.html  pagination.html
auth/             edit_inline/              prepopulated_fields_js.html
base.html         filter.html               search_form.html
base_site.html    includes/                 submit_line.html
change_form.html  index.html                template_validator.html
change_list.html  invalid_setup.html

作为示例,我们接下来就来修改一下留言列表顶部的“Django 管理”,将它替换为“留言管理”。

Django 的 admin 应用查找模板的顺序如下:

  • admin/<应用名称>/<对象名称>/<模板名称>.html
  • admin/<应用名称>/<模板名称>.html
  • admin/<模板名称>.html

留言列表的默认模板为 change_list.html,我们实现一个新的模板来继承并覆盖它。创建 messages/templates/admin/messages/message/change_list.html,内容如下(注意文件编码一定要保存为 UTF8):

{% extends “admin/change_list.html” %}

{% block branding %}
<h1 id=”site-name”>留言管理</h1>
{% endblock %}

这样就修改成功了,看看效果:

下一节 >>>

Django 快速实战入门(八):部署 Django

以 gentoo+nginx+flup 为例,部署我们的留言板到生产服务器中。

— EOF —