« Python温泉終了とか日記 |Main| Django10日目日記 »

« Python温泉終了とか日記 | Python | Django10日目日記 »

Django7日目日記

多対多のリレーションを表現しているフィールド
    tags = models.ManyToManyField(Tag)
に追加をするには
    self.tags.add(tag)
でいい。 ただし、データベース上はself.idとtag.idの対をテーブルに追加するわけなので、 もちろんself.idが値を持っていなければ行けない。 だからこうした。
if not self.id:
    self.save()

= 書き忘れてたけどpygmentsで行番号がずれるのは 標準で付いてくるスタイルシートに行番号のレイアウトに関する記述がないせいで、 行番号の表示に使っているpreのマージンが0ではないせい。
.linenos pre {
  margin: 0pt;
}

= 学会費の請求が。
       請求額          :       12,540円
       入金額          :        4,800円
       不足額          :       -7,740円
しまった。社会人になったのに学生料金で払ったか。 請求書が届いているはずなので探さねば。
= フィードの出力、簡単だった。

引数を取るフィード出力がうまく動かない。 「Invalid feed parameters. Slug 'forlang' is valid, but other parameters, or lack thereof, are not.」 って言われてトレースバックとかが出ない。

Djangoのコードの中からエラーメッセージで検索してみた。 django.contrib.syndication.viewsの中でエラーが出ている。 print文を足してみる。

    try:
        feedgen = f(slug, request.path).get_feed(param)
    except feeds.FeedDoesNotExist:
        raise Http404, "Invalid feed parameters. Slug %r is valid, but other parameters, or lack thereof, are not." % slug
Feedクラスのインスタンスを作ってからget_feedにparamを渡す。 paramは'python'だった。

get_feedの中身:

if url:
    try:
        obj = self.get_object(url.split('/'))
    except (AttributeError, ObjectDoesNotExist):
        raise FeedDoesNotExist
else:
    obj = None
get_objectがどこで定義されているのかわからない。 検索したけど見つからない。tryを外してみたら 「AttributeError at /doukaku/feeds/forlang/python/ 'ForLang' object has no attribute 'get_object'」

ドキュメント(配信フィードフレームワーク : Django オンラインドキュメント和訳)には 「get() は指定された bit を使って適切な巡回区域を選択する役割を担っています.」 と書いてあったのでgetで実装したけど、 そこに載っているサンプルコードはget_objectになっているなぁ。


= Pythonのコードをいじって、 一つのプロセスの中でN個のインタプリタを起動するようにすれば、 Nコア対応なんじゃないか?という話だけど、 帰ってきて考えると 「なんか片方のインタプリタが使っているオブジェクトをもう片方がGCしちゃったりしないかな」 と思った。

でも、あらかじめCPUの個数にあわせてNが決まっているなら、 「このオブジェクトはインタプリタ間でシェアします」って宣言したオブジェクトの 参照カウンタをN+1くらい増やしておくだけでいいんじゃないかとも思った。

ということをここに書いておくとささださんが「それ○○だからダメ」とつっこんでくれるんじゃないかと期待(ぉ


= 「テスト用に作った、本文が「a」になっているコメントを全部削除」はシェルでこんな感じに実行。
models.Comment.objects.filter(body="a").delete()
らくちん。
= こんなビュー
def lang_index(request):
    langs =  models.Lang.objects.all()
    return render_to_response(
        'doukaku/lang_index.html',
        dict(
          langs=langs)) 
をこんな使い方している場合、
urlpatterns = patterns('',
    (r'^lang/$', views.lang_index),

(以下略)
汎用ビューを使う方がいい。 下のように書くだけでいい。lang_indexが必要なくなる。
urlpatterns += patterns(
    "django.views.generic.list_detail",
    (r'^lang/$', 'object_list', dict(queryset=models.Lang.objects.all())),

(以下略)
テンプレートの名前をlang_listに変える。別にテンプレートの名前を指定することもできるけど、 今のファイル名にこだわりがあるわけではないのでデフォルトの名前に。 そして中身はこんなのだったので、langをobject_listに変える。
{% extends 'base.html' %}
{% load i18n %}

{% block content %}
<h2>{% trans 'List of languages' %}</h2>
<ul>
{% for lang in langs %} 
<li><a href="{{ lang.absolute_url }}">{{ lang.caption }}</a></li>
{% endfor %}
</ul>
{% endblock %}

これで6行縮む。しかも、表示する対象が増えてきて1ページに全部じゃ多すぎるな、と思ったときも pagenate_by=20と書き足して、URLでpageをキャプチャするようにすればいいだけ。簡単。


= 詳細表示ページもobject_detailを使える。 今までchallengeって変数に入れていたオブジェクトがobjectという抽象的な名前の変数にはいるけども、 テンプレートが長くてchallengeが何度も出てくるとか、 objectという名前ではよくわからなくなりそうだと思うのであれば {% with object as challenge %}~{% endwith %}で囲ってやるだけ。
= 珍しい、今日はまだ8時になっていないのにみんな帰っちゃった。 amachangがお休みなのもあるのかな。
= テンプレートに「一番根っこのURL」を渡すために、 いちいちrender_to_responseに RequestContext(request)を渡すのが面倒だ(DRYじゃない)って思ったので別の方法を模索して、 テンプレートのincludeで文字列を埋め込むという方法を使ったけども、 結局その方法ではテンプレートで静的な文字列を埋め込んでいるだけなので、 request.userのような変化するものは埋め込めないし、 モデルがabsolute_urlを返すときの文字列も変わるように setting.pyに重複した設定が必要になるし、 ダメダメだった。 素直に全てのrender_to_responseでRequestContextを渡す方がよっぽどスマートだと思った。

そういうわけでこんな感じの関数をviews.pyの冒頭に定義して、 render_to_responseが常にRequestContextを渡すようにしてみた。

def render_to_response(*args, **kw):
    from django.shortcuts import render_to_response
    request = sys._getframe(1).f_locals.get("request")
    kw["context_instance"] = RequestContext(request)
    return render_to_response(*args, **kw)
(_getframeは黒魔術なのでよいこは真似しないでね!)
= あわせて読みたい

URL入れるところに自分のブログのURLを入れて 「※このような画像が表示されます(横160x縦140)」「※画像は数時間ごとに自動的に更新されます。」 「ただいま集計中」 と表示されたので、そのページをブックマークして「いつになったら集計終わるかなー」 と何度か見に行ったけども、1週間経っても集計が終わらずorz。

っていうかブログに貼らなきゃダメだったのね?今気がついたよ!


= 実装は持ち運べるノートパソコンで。 でも簡単にサーバに入れたい。 ファイルをコピーするたびに設定を書き換えるのは面倒だけど、 環境によって分けないといけないファイルを覚えておくのも面倒。

とりあえずURLに関してはこうやってみた。

import sys
is_local = "nt" in sys.builtin_module_names
if is_local:
    URL_BASE = "http://localhost:8000/doukaku"
else:
    URL_BASE = "http://xxxxx.com/doukaku"
そうか、サーバに載せるなら当然DEBUGもFalseにしなきゃいけないな。
import sys
is_local = "nt" in sys.builtin_module_names
DEBUG = is_local
TEMPLATE_DEBUG = DEBUG
is_localなんか作らなくてもDEBUGでよかったんじゃないかという話。

追記:ローカルでは動くのにサーバでは動かない問題を解決するためにDEBUGをONにした。


= 9日目

あんまり長い文章をblocktransで囲むのもどうかと思ったので、 {% include 'welcome.txt' %}みたいな感じでウェルカムメッセージをインクルードすることにした。 さて。 訪問者のロケールによってインクルードするテンプレートを変更するにはどうしたらいいだろうか。


= models.pyのモデル定義にget_absolute_urlを書くのはおかしい気がしてきた。 urls.pyとまとめられないか。
# models.py
    def get_absolute_url(self):
        return '%s/lang/%s' % (settings.URL_BASE, self.slug)
# urls.py
    (r'^lang/(?P<lang_slug>.*)/$',
     views.lang_detail),
うーん。 まぁいいや。
= uniqueなのに
class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
どうしてuserprofile_setになってるんだ?
In [141]: c.author.userprofile_set.all()
Out[141]: [<UserProfile: nishio>]
よくわからないけど、このままでは不便なのでUserクラスにメソッドをつっこんでみた。
def profile(self):
    ups = self.userprofile_set.all()
    if len(ups) > 0:
        return ups[0]
    return None
User.profile = profile
Userクラスは自分が定義したものではないクラスだけど、 中身を気にせずにユーティリティメソッドをつっこむことができて便利。
= models.BooleanField()で宣言されているフィールドに、 フォームでチェックボックスをONにしたときに送られてくる値"on"を入れても、 Trueにはならないのか。

あー、そうか、チェックを外したときには値が送られないから、 そもそも入っている値ではなくてhas_keyでテストした結果を見るべきなのか。


= いつの間にかひらがな入力モードになっているに気づかずに「むむ」と打ったら 「もなもな」って出てなごんだ。
= 「あわせて読みたい」の集計が終わって、表示されるようになっていた。 やっぱり画像を貼らないといけなかったんだね。

あわせて読みたい - あわせて読みたいについて

「あわせて読みたい」では、AMN(あわせて読みたいネットワーク = 「あわせて読みたい」または「フィードメーター」)参加ブロガーの中から、直近約24時間で共通の読者率が高いサイトを、独自の計算により算出した順位で表示しています。

画像パーツを貼り付けた直後は「集計中」と表示されていますが、数時間後から情報の更新がはじまり、約1~2日ほどで安定した集計値が表示されるようになります。

表示は数時間~1日間隔で更新されます。 記事の中で他のブログを紹介するなどすることでも、「あわせて読みたいサイト」は毎日変化すると思います。

なお、順位付けのアルゴリズムは秘密ですが、基本的には共通の読者率が高い(相関性の高い)サイトが紹介されます。(※ Cookie は使っていません)

「Cookie は使っていません」 だそうな。 ということはやっぱり Kazuho Oku's Weblog : 固定化するIPアドレス、そしてブログパーツとプライバシー で書かれているようにIPアドレスで追跡しているのだろうか。
= orz。なんかうまく行かないと思ったらsubmitがsumbitになってた。
= コメントの投稿にプレビュー画面を挟みたい。 コメントオブジェクトを作って表示して、 OKを押したらsaveするってのでいけるかと思ったけども、 プレビューでコメントオブジェクトを作るたびにどんどん書き込まれてしまっている。

プレビューの段階でnew_comment=Comment(a=..., b=...)ってやらないで、 new_comment=dict(a=...,b=...)にしておく。 OKを押した段階でnew_comment=Comment(**new_comment)する。 これが一番エレガントかなぁ?


= モデルに指定する選択肢を、ビューでも使いたい(フォームに)ので こんな風にしてみた。
# models.py

RATING_LIST = [
  ("quality", [_('awful'), _('bad'), _('not bad'), _('good'), _('excellent')]),
  ("speed", [_('slower'), _('slow'), _('normal'), _('quick'), _('quicker')]),
  ("elegance", [_('cruder'), _('crude'), _('normal'), _('polished'), _('elegant')]),
  ("craziness", [_('nuisance'), _('crazy(in bad meaning)'),
                 _('ordinal'), _('crazy(in good meaning)'), _('genius')])
]
class Rating(models.Model):
    comment = models.ForeignKey(Comment)
    user = models.ForeignKey(User)
    date = models.DateTimeField(editable=False)
    for name, choices in RATING_LIST:
        locals()[name] = models.IntegerField(
            choices=zip(range(-2, 3), choices))
# views.py

from doukaku_proj.doukaku.models import RATING_LIST
class RatingForm(forms.Form):
    for name, choices in RATING_LIST:
        locals()[name] = forms.ChoiceField(
            widget=forms.RadioSelect(),
            choices=zip(range(-2, 3), choices),
            label=_(name))

= むむ。コンソールの青い字だけ浮いて見える…。
= ふう。レーティング機能が付いた。 -2~2だと、なんかラジオボタンで値をやりとりするときにおかしくなるようなので 0~4に変えた。
= テンプレートからincludeで取り込んでいる、拡張子がtxtのテンプレートは、 transでマークしてあってもgettextが拾ってくれない気配。

AnonymousUserをstrすると、えらる気配。

トラックバック(Trackback)

Trackback URL: http://www.nishiohirokazu.org/mt/mt-tb.cgi/615

フィードバック

by ささだ | 2007年06月25日 18:49

参照カウントだったらそれでいいんじゃね?

がーん。いいのか。

ご意見・ご感想をお送りください(フィードバック)

(フィードバックはメールで送信され、基本的に表示されませんが、内容によっては公開させていただくこともございます。ご了承ください。Your comment doesn't appear the page immediately. If the comment has value to other people, it will be put on the page or subsequent entries. Thank you.)

上の情報は、いずれも未記入でかまいません。 All of above questions are optional.