Tatsuya Blog
msgbartop
ニートになりたいプログラマ
msgbarbottom

2009-11-05 00:34 [GAE/P]国際化に対応してみました 〜その3〜

  • Share

前回に引き続き国際化についてです。

前の記事で書いていた「Environment variable DJANGO_SETTINGS_MODULE is undefined.」のエラーについてですが、あの記事を書いた後も何度か発生しており、今日、GAE上にアップしたアプリを動かしていると、一時ページを表示した状態で放置し、改めて操作するとAjax部分の通信で上記のエラーが出ていました。

それで、色々調べているとimportの順番が問題だったようで、試してみるとエラーが出なくなりました。その修正内容を書いておきたいと思います。

■編集前

1
2
3
4
5
6
7
8
9
from django.utils import translation
from google.appengine.ext import webapp
from org.fukata.mapshare.system.utils.cookies import Cookies
import os
from django.conf import settings
 
os.environ.__setitem__('DJANGO_SETTINGS_MODULE', 'conf.settings')
# Force Django to reload settings
settings._target = None

■編集後

1
2
3
4
5
6
7
8
9
10
import os
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from django.utils import translation
from org.fukata.mapshare.system.utils.cookies import Cookies
from django.conf import settings
 
os.environ.__setitem__('DJANGO_SETTINGS_MODULE', 'conf.settings')
# Force Django to reload settings
settings._target = None

全体的に順序も入れ替わってしまっているので分かりにくいと思いますが、

from django.utils import translation

の前に

from google.appengine.ext import webapp
from google.appengine.ext.webapp import template

を読み込むということです。

また、エラーが出るようでしたら追記したいと思います。これで、最後であってほしい。。。(直接的な原因を調べろよ。。。)

Tags: , ,

2009-11-02 01:24 [GAE/P]国際化に対応してみました 〜その2〜

  • Share

前の記事でGAE/Pの標準環境にて国際化に対応した際の記事を書いたのですが、実際にGAE上にデプロイして動かしてみると何点かエラーが出たので、追加したいと思います。

■UnicodeDecodeErrorの発生
実際にアップロードして、.poファイルで日本語が定義しているmsgidが存在するページを開いた際に同様のエラーが発生しました。これは、.poファイルの文字コードが設定されていなかった為に起こったようです。下記が、現在の.poファイルになります。

msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Tatsuya Fukata <tatsuya .fukata@gmail.com>\n"
"Report-Msgid-Bugs-To: Tatsuya Fukata </tatsuya><tatsuya .fukata@gmail.com>\n"
"Language-Team: Japanese\n"
"Project-Id-Version: MapShare\n"
 
msgid "Spot"
msgstr "スポット"
 
msgid "Spot List"
msgstr "スポット一覧"
 
msgid "other"
msgstr "その他"
</tatsuya>

■DJANGO_SETTINGS_MODULEの未設定
次に、何度かデプロイしている間に上記のようなエラーが発生するようになりました。実際に使われているのは前の記事で作成したクラス「I18NRequestHandler」のファイル内で同様の記述が存在します。直接キーを指定している部分を下記のように修正して、エラーが出なくなりました。

#os.environ['DJANGO_SETTINGS_MODULE'] = 'conf.settings'
os.environ.__setitem__('DJANGO_SETTINGS_MODULE', 'conf.settings')

まだ、完全に国際化に対応されていないかもしれませんが、何かエラーが発生したら随時載せていきたいと思います。

Tags: , ,

2009-11-01 22:44 [GAE/P]国際化に対応してみました 〜その1〜

  • Share

現在、GAE/P環境にて作成しているアプリの国際化を対応してみました。Django-1.0やKayといったFWを使って国際化も対応できるようですが、今更変更するのもだるかったので、標準の環境で対応しました。

多分、次に作成するなら、標準の環境ではなく、他のFWを試すかもしれません。

とまぁ、自分のことは置いといて早速、国際化の手順を書いていきたいと思います。まずは、最終的な構成を書きます。

.
`-- src
    |-- app.yaml
    |-- conf
    |   |-- locale
    |   |   |-- en
    |   |   |   `-- LC_MESSAGES
    |   |   |       |-- django.mo
    |   |   |       `-- django.po
    |   |   `-- ja
    |   |       `-- LC_MESSAGES
    |   |           |-- django.mo
    |   |           `-- django.po
    |   `-- settings.py
    `-- org
        `-- fukata
            `-- mapshare
                |-- public
                |   |-- css
                |   |-- img
                |   `-- js
                `-- system
                    |-- handler
                    |   |-- i18NRequestHandler.py
                    |   |-- pc
                    |   |   `-- index.py
                    |   `-- webapi
                    |-- model
                    |-- template
                    |   `-- pc
                    `-- utils
                         `-- cookies.py

今回、国際化の為に新規に追加のは以下のファイル群都なります。

  • config/settings.py
  • config/locale/*
  • org/fukata/mapshare/system/i18NRequestHandler.py
  • org/fukata/mapshare/system/utils/cookies.py

次に各ファイルの内容になります。言語ファイル(.po, .mo)に関しては省きたいと思います。

■config/settings.py

1
2
3
4
5
6
7
8
9
10
11
 USE_I18N = True
 
 # Valid languages
 LANGUAGES = (
     # 'en', 'zh_TW' should match the directories in conf/locale/*
     ('en', _('English')),
     ('ja', _('Japanese')),
     )
 
 # This is a default language
 LANGUAGE_CODE = 'ja'

■org/fukata/mapshare/system/i18NRequestHandler.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 from django.utils import translation
 from google.appengine.ext import webapp
 from org.fukata.mapshare.system.utils.cookies import Cookies
 import os
 
 os.environ['DJANGO_SETTINGS_MODULE'] = 'conf.settings'
 from django.conf import settings
 # Force Django to reload settings
 settings._target = None
 
 class I18NRequestHandler(webapp.RequestHandler):
 
     def initialize(self, request, response):
         webapp.RequestHandler.initialize(self, request, response)
 
         self.request.COOKIES = Cookies(self)
         self.request.META = os.environ
         self.reset_language()
 
     def reset_language(self):
 
         # Decide the language from Cookies/Headers
         language = translation.get_language_from_request(self.request)
         translation.activate(language)
         self.request.LANGUAGE_CODE = translation.get_language()
 
         # Set headers in response
         self.response.headers['Content-Language'] = translation.get_language()
 #        translation.deactivate()

■org/fukata/mapshare/system/utils/cookies.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
 import UserDict
 from Cookie import BaseCookie
 class Cookies(UserDict.DictMixin):
     def __init__(self, handler, **policy):
         self.response = handler.response
         self._in = handler.request.cookies
         self.policy = policy
         if 'secure' not in policy and handler.request.environ.get('HTTPS', '').lower() in ['on', 'true']:
             policy['secure'] = True
         self._out = {}
     def __getitem__(self, key):
         if key in self._out:
             return self._out[key]
         if key in self._in:
             return self._in[key]
         raise KeyError(key)
     def __setitem__(self, key, item):
         self._out[key] = item
         self.set_cookie(key, item, **self.policy)
     def __contains__(self, key):
         return key in self._in or key in self._out
     def keys(self):
         return self._in.keys() + self._out.keys()
     def __delitem__(self, key):
         if key in self._out:
             del self._out[key]
             self.unset_cookie(key)
         if key in self._in:
             del self._in[key]
             p = {}
             if 'path' in self.policy: p['path'] = self.policy['path']
             if 'domain' in self.policy: p['domain'] = self.policy['domain']
             self.delete_cookie(key, **p)
     #begin WebOb functions
     def set_cookie(self, key, value='', max_age=None,
                    path='/', domain=None, secure=None, httponly=False,
                    version=None, comment=None):
         """
         Set (add) a cookie for the response
         """
         cookies = BaseCookie()
         cookies[key] = value
         for var_name, var_value in [
             ('max-age', max_age),
             ('path', path),
             ('domain', domain),
             ('secure', secure),
             ('HttpOnly', httponly),
             ('version', version),
             ('comment', comment),
             ]:
             if var_value is not None and var_value is not False:
                 cookies[key][var_name] = str(var_value)
             if max_age is not None:
                 cookies[key]['expires'] = max_age
         header_value = cookies[key].output(header='').lstrip()
         self.response.headers._headers.append(('Set-Cookie', header_value))
     def delete_cookie(self, key, path='/', domain=None):
         """
         Delete a cookie from the client.  Note that path and domain must match
         how the cookie was originally set.
         This sets the cookie to the empty string, and max_age=0 so
         that it should expire immediately.
         """
         self.set_cookie(key, '', path=path, domain=domain,
                         max_age=0)
     def unset_cookie(self, key):
         """
         Unset a cookie with the given name (remove it from the
         response).  If there are multiple cookies (e.g., two cookies
         with the same name and different paths or domains), all such
         cookies will be deleted.
         """
         existing = self.response.headers.get_all('Set-Cookie')
         if not existing:
             raise KeyError(
                 "No cookies at all have been set")
         del self.response.headers['Set-Cookie']
         found = False
         for header in existing:
             cookies = BaseCookie()
             cookies.load(header)
             if key in cookies:
                 found = True
                 del cookies[key]
             header = cookies.output(header='').lstrip()
             if header:
                 self.response.headers.add('Set-Cookie', header)
         if not found:
             raise KeyError(
                 "No cookie has been set with the name %r" % key)

となります。で、通常のハンドラに対して「webapp.RequestHandler」ではなく、「I18NRequestHandler」を継承するようにします。これで、基本的には大丈夫です。テンプレート内で、国際化のタグを使用するには、テンプレートの先頭などで、{% load i18n %}と記述します。これで、国際化用のタグが使えるようになります。

また、.poファイルに関してですが、sdkにスクリプトが付随しているようですが、自分は下記のように.moファイルを生成しています。まぁ、スクリプトを使った方が便利だと思います^^;

1
2
msgfmt django.po 
mv messages.mo  django.mo

今回使用した、コードに関しては、参考サイトの方にほぼすべてあります。というか、掲載されているコードそのままで動きました。

■参考サイト

Tags: , ,

2009-10-29 04:36 [GAE/P]必須カラムの登録について

  • Share

最近、GAE for Pythonでアプリを作っているのですが、必須カラムの値がうまく登録できなかった問題が解決したので、メモとして残しておきたいと思います。

■A.py

1
2
class A(db.Model):
    owner = db.UserProperty(required=True,auto_current_user_add=True)

■B.py

1
2
class B(db.Model):
    a = db.ReferenceProperty(required=True,reference_class=A)

■main.py

1
2
3
4
5
6
7
a_key = [エンティティAのKeyを取得]
a = A.get(a_key)
# コンストラクタで指定しないと登録時にエラーになります。
b = B(a=a)
# 以下のようにフィールドを指定して代入してもエラーとなる。
b.a = a
b.put()

ずっと、後者のようにフィールドを指定して代入していたので、「なんで代入してるのにエラーになるんだ?」という風に思っていて、コンストラクタに指定するというのに気づくのがかなり遅くなってしまいました。

とりあえず、なんとか登録することもでき、前に進むことができるようになりました。今作っているアプリはjsごりごりのアプリなので、デバッグがいつもに比べ手間がかかります^^;しかも、Python自体もまだ勉強し始めてそんなに時間が経っていないのでどういう関数があるのかすら分からない状態で、常にググりながらのコーディングとなっています。

Tags: , ,

2009-10-21 01:39 [Python]ディレクトリをまとめてzip圧縮

  • Share

pythonのライブラリリファレンスを見ているとディレクトリをzip圧縮するメソッドがなかったので作成してみました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
import os,zipfile
 
def zip_write_dir(base_dir, src_dir, zip_name):
    """ディレクトリをzip圧縮する
        base_dir: src_dirが属するディレクトリを表す
        src_dir: 圧縮対象のディレクトリを表す
        zip_name: 出力先のzipファイルのフルパスを表す
    """
    zf = zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED)
 
    current_dir = os.getcwd();
    os.chdir(base_dir)
 
    for root,dirs,files in os.walk(src_dir):
        for _file in files:
            filename = os.path.join(root,_file)
            arcname = filename
            zf.write(filename, arcname)
 
    zf.close()
    os.chdir(current_dir)

Tags: , ,

2009-10-21 01:36 [Python]mod_python使用時のダウンロード処理

  • Share

現在、pythonを勉強中でちょっとしたWEBアプリを作っていたのですが、ダウンロード処理を書かないといけなくなったので、モジュールを作成してみました。よかったらどうぞ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding: utf-8 -*-
 
import sys
import os
 
def download(req,filepath):
 
    req.content_type = 'application/octet-stream'
    req.headers_out['Content-Type'] = 'application/octet-stream'
    req.headers_out['Content-Disposition'] = "attachment; filename=\"" + os.path.basename(filepath)
    req.headers_out['Content-Length'] = str(os.path.getsize(filepath))
    req.headers_out['Expires'] = '0'
    req.headers_out['Cache-Control'] = 'must-revalidate, post-check=0,pre-check=0'
    req.headers_out['Pragma'] = 'private'
 
    if sys.platform == "win32":
        import msvcrt
        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
 
    return req.sendfile(filepath)

Tags: , ,