Surgo

lazy programmer. (simple is better than complex !)

Mar 3, 2010

Django のレスポンスを PDF で

Django を使う理由がもうひとつ増えました。@whosaysni (Yasushi Masuda) さんが、template2pdf というテンプレートから簡単に PDF を生成する Django アプリケーションを公開されています。主な特徴は以下の通りです。

  • ReportLab がオープンソースで公開している PDF ライブラリーを利用
  • 同 ReportLab が作成した Report Markup Language (略: rml) (仕様: PDF ) により、PDF ファイルを XML で定義
  • XML で定義できるので、Django の強力なテンプレートシステムが使える
  • rml -> pdf 変換は Rohit Sankaran 氏が公開している trml2pdf を利用

インストール

すべて setuptools でインストール可能です

利用方法

settings.py の INSTALLED_APPS への追加と、環境に応じて日本語用フォントを設定。

# settings.py
INSTALLED_APPS = (
    # django built-in apps
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    # external apps
    'django_trml2pdf', # thx 2 Yasushi Masuda
    # (以下省略)
}

# set unicode fonts for trml2pdf \
#   (see django_trml2pdf.__init__.py PRELOAD_FONTS)
TRML2PDF_PRELOAD_FONTS = [
    (None, [('UnicodeCIDFont', 'HeiseiKakuGo-W5', None, None)]),
    (None, [('UnicodeCIDFont', 'HeiseiMin-W3', None, None)]),
    ('IPA Mincho', [('TTFont', 'IPA Mincho', 'fonts/ipam.ttf', None),
    ('TTFont', 'IPA Mincho', 'fonts/ipam.ttf', None),
    ('TTFont', 'IPA Mincho', 'fonts/ipam.ttf', None),
    ('TTFont', 'IPA Mincho', 'fonts/ipam.ttf', None),]),
    (None, [('TTFont', 'IPA Gothic', 'fonts/ipag.ttf', None)]),
    (None, [('TTFont', 'IPA PMincho', 'fonts/ipapm.ttf', None)]),
    (None, [('TTFont', 'IPA PGothic', 'fonts/ipapg.ttf', None)]),
]

表示してみる

urls.py に以下のように追加すると使えます。

urlpatterns = patterns('django_trml2pdf',
    (r'^foo/$',
        'direct_to_pdf', {'template': 'trml2pdf/base.rml'}),
)

もちろん views.py の中でも使えます。使い方はアクセンスさんの オーメイク おまけ ページに記載されています。日本語を含むユニコードも問題なく使えます (もちテンプレートファイルはユニコードで作成)。おかげさまで帳票アプリケーションが簡単に作れるようになりましたね!

多謝: Yasushi Masuda さん

日本語の取り扱いについて (追記)

PDF で日本語を表示したい場合は、フォントを PDF ファイル内に内包するか、PDF 標準フォントを指定する必要があります。フォントを PDF に埋め込むと色々とめんどい。

  • 内包 = フォントのデータの分だけ、サイズがでかくなる
  • 配布時のライセンス問題 (フォントはホスト毎にライセンシングされてるのがほとんど

ということで PDF 標準フォントを指定しましょう。trml2pdf/base.rml ファイルで "Serif" って指定されている箇所を settings.py に記載した "HeiseiKakuGo-W5" か "HeiseiMin-W3" に変更します。また、base.rml ファイルに以下のように記述し、利用するフォントを埋め込みます。

<!DOCTYPE document SYSTEM "rml.dtd">
{% load pdf_tags %}
<document filename="{{ pdf_name }}">
  <docinit>
    <registerCidFont faceName="HeiseiKakuGo-W5"/>
    <!-- OR / AND -->
    <registerCidFont faceName="HeiseiMin-W3"/>
  </docinit>
  {% block template %}
<!-- 以下省略 -->

Feb 21, 2010

パーフェクトな Django の設定ファイル

"DAMON BLOGONS" の、 "The Perfect Django Settings File" という記事で紹介されていた Django の設定 (settings.py) が面白かったので、私が利用しているものと併せて紹介したいと思います。

環境による DEBUG の切り分け

開発環境では "DEBUG = True" と書くと幸せになれます。Django のデバッガーは強力です。ただし、本番環境にそのままデプロイしてしまうと・・・。デプロイを楽にするためにも、失敗を防ぐためにも自動的に切り分けるのが望ましいですよね。Damon 氏は以下のようなコードで切り分けているようです。

# Set DEBUG = True if on the production server
if socket.gethostname() == 'your.domain.com':
    DEBUG = False
else:
    DEBUG = True

開発環境 localhost (127.0.0.1) と運用環境 your.domain.com のホスト名の違いを利用して切り分けています。

データベース設定

データベースの設定も開発環境と本番環境では違います。Damon 氏は 2 つのファイルを使い切り分ける方法を紹介しています。この方法は私もよく使います。これは pinax でも利用されている方法です。1 つは本番環境の設定をそのまま記述したファイル (settings.py) で、もう 1 つが開発環境の設定を記述したファイル (local_settings.py) です。settings.py の最後に、以下のコードを書いておき、 local_settings.py は本番環境にデプロイしないことで設定を切り分けています。

try:
    from local_settings import *
except ImportError:
    pass

local_settings.py で settings.py の内容を上書きします。私はデータベースの設定だけでなく、上述した DEBUG 設定なんかの上書きに使っています。local_settings.py はバージョン管理ソフトの除外リスト(mercurial なら .hgignore、subversion なら global-ignores など) に追加しておくと便利です。※ このページの後述に DEBUG スイッチなるものが記述されていますが、私の場合はせっかくなので、すべて local_settings.py に記述しちゃいます。

メディアルート

フルパスで記述してしまうと再利用性が損なわれてしまいます。以下のコードのように、実行時に動的にパスを取得することにより、解決できます。これも Django の長所、"設定ファイルが python ファイル" を活用したいい例だと思います。

PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))
MEDIA_ROOT = os.path.join(PROJECT_PATH, 'media')

PROJECT_PATH で settings.py (__file__) の保存されているディレクトリまでのフルパスを取得し、そのディレクトリ内の media ディレクトリを MEDIA_ROOT としています。

以上、抜粋です。 Damon の settings.py は gist に保存されています。その他のチップスは Damon 氏のページを参照してみてください。

MEDIA の URL

せっかくなので、私が利用しているデフォルトの urls.py のチップスを 1 つ。静的ファイルがボトルネックになってしまってはいけないので、本番環境は静的ファイルを別サーバに保存することが多いでしょう。よって urls.py も本番環境と開発環境では変わることがありますよね。私は、local_settings.py と同様に local_urls.py を作っています。OAuth とかテスト用の Web サービスのテストなどにも便利ですよ。

# urls.py
urlpatterns = patterns(
   # 本番環境の URL たち
)

try:
    from local_urls import urlpatterns as test_patterns
    urlpatterns += test_patterns
except ImportError:
    pass
# local_urls.py
import re

from django.conf import settings
from django.conf.urls.defaults import patterns, url

base_url = re.escape(settings.MEDIA_URL[1:])
urlpatterns += patterns('django.views.static',
        url(r'^%s(?P<path>.*)$' % base_url, 'serve',
            {'document_root': settings.MEDIA_ROOT}),
    )

便利なのでぜひお試しください :-) それと、ぜひ皆さんが使っていらっしゃる テクニック も教えてほしいです。

Jan 15, 2010

Google Python スタイルガイド (日本語訳)

今日まで 5 日間実家の福岡 & 長崎に帰省しました。久しぶりの休みだったので、Sphinx テーマ作成、restructured text の復習、英語の勉強、綺麗な Python コードを書けるようになれるように Google Python スタイルガイド を日本語訳してみました。相変わらず英語が残念なので閲覧注意です。

http://works.surgo.jp/translation/pyguide.html

複数行にまたがる文字列でも、インデントを揃えた方が美しい時は非明示的な行継続をする等は知らなかったです。

# Yes:
print ("This is much nicer.\n"
       "Do it this way.\n")

# No:
print """This is pretty ugly.
Don't do this.
"""

たしかに見やすいですね。また、Eclipse では自動的にやってくれていましたが、インポートはジェネリックなものから非ジェネリックな順にインポートとかはついつい忘れがちです・・・。さらに、パスを通すのがめんどくさいので、作成しているモジュール内の特定のクラスをインポートする時はフルパスじゃなくて相対パスで書いてしまったり・・・。後からどうせ書かないといけないんですけどね。さらに、配列を返すよりも Yeild の方がメモリ効率が高いとかも勉強になりました。これは実戦投入したいと思いました。

英語教えてくだしあ ><

about

friends

archives

Kosei Kitahara.

surgo.jp at gmail.com

hosted at blogger.