Tag/Python
モデルのsaveが変わったよ。
Django の trunk に queryset-refactor がマージされたタイミングで、 モデルの save メソッドにも改良が加えられてたのでメモメモ。
マージ前(r7476) は save メソッド自体に実際の処理が書かれてた。
# django/db/models/base.py@213行目あたり
def save(self, raw=False):
dispatcher.send(signal=signals.pre_save, sender=self.__class__,
instance=self, raw=raw)
# 中略...
dispatcher.send(signal=signals.post_save, sender=self.__class__,
instance=self, created=(not record_exists), raw=raw)
ので、データ登録の際に何か処理を挟みたい場合、 オーバーライドして処理を追加してあげて、 良いところで親の save をコールしてあげる事で実装してた(と思う)。
class Hige(models.Model):
"""
HIGEなモデル
"""
def save(self, raw=False):
# なにか追加したい処理が入る。
super(Hige, self).save(raw=raw)
でも、これだとダイレクトにデータを保存したい場合に、 適当な名前でもうひとつ save メソッドを作ってやる必要があった。
class Hige(models.Model):
"""
HIGEなモデル
"""
def save(self, raw=False):
"""
adminとかでは処理を追加したいので、
オーバーライドせざるを得ないんだ。
"""
# なにか追加したい処理が入る。
super(Hige, self).save(raw=raw)
def direct_save(self, raw=False):
"""
たまーに処理を追加しないのも必要。
"""
super(Hige, self).save(raw=raw)
こういう状況になると、ものっすごい残念な気持ちになってた訳なのですが、 これが マージ後(r7477) でちょっと素敵に改良されてるよ。
# django/db/models/base.py@293行目あたり
def save(self):
"""
Save the current instance. Override this in a subclass if you want to
control the saving process.
"""
self.save_base()
def save_base(self, raw=False, cls=None):
"""
Does the heavy-lifting involved in saving. Subclasses shouldn't need to
override this method. It's separate from save() in order to hide the
need for overrides of save() to pass around internal-only parameters
('raw' and 'cls').
"""
# 省略...
実際のデータ登録の処理は全部 save_base メソッドで実装しておいて、 save メソッドはそのフックになってるよ。 これだと、ダイレクトにデータを登録する必要が出て来た場合でも、 save_base をコールすれば良いだけ。 なので、特にモデルに変更を加える必要は無い。
class Hige(models.Model):
"""
HIGEなモデル
"""
def save(self):
"""
処理を追加したいのでオーバーライド。
raw=Falseは無くなってるよ。
"""
# なにか追加したい処理が入る。
# self.save_base()ってしてないのはなんとなく。
super(Hige, self).save()
これでもう残念な気持ちにならなくて良いね。
また一つ、 Django が好きになりましたとさ ![]()
- Posted at:
- 2008/05/02 00:29:35
- 2 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/05/02/changed-models-save-on-django/trackback/
Pythonでスクリプトからインタプリタを起動する
Google App Engine を弄ってるついでに覚えたメモ。 Python なスクリプトからインタプリタを起動するには、
import code
code.interact()
って、したら良いらしいよ。 IPython がインストールされてるのであれば、
import IPython
IPython.Shell.IPShell().mainloop()
で起動出来るよ。 なので、こんな感じにすると良いのかも。
try:
import IPython
IPython.Shell.IPShell().mainloop()
except ImportError:
import code
code.interact()
これで IPython がインストールされてれば IPython が使えるし、 されて無ければデフォルトでインタプリタが起動出来るね。
- Posted at:
- 2008/04/15 02:49:03
- 0 Comments
- 1 TrackBack
- Tags:
- Python
- Trackback:
- http://humming.via-kitchen.com/2008/04/15/execute-interpreter-on-pythonscript/trackback/
DjangoのPaginatorが変わってるよ。
Django の r7306 で ObjectPaginator がdeprecated扱いになったので、 どんな風に変わったのか調べてみたよ。
テストを走らせてみると、こんな感じのWarningを思いっきり吐かれた。
DeprecationWarning: The ObjectPaginator is deprecated. Use django.core.paginator.Paginator instead.
なるほど、 Paginator に変わったらしい。 実際にどう変わったのかな?って確認しようと、 django/core/paginator.py をちょっと覗いてみると、 QuerySetPaginator って派生クラスもある。 どこが違うかと言うと、 _get_count なメソッドだけがオーバーライドされてる。
まずは Paginator の _get_count を見てみるとこうなってる。
def _get_count(self):
"Returns the total number of objects, across all pages."
if self._count is None:
self._count = len(self.object_list) # <-- lenでカウントを取得
return self._count
count = property(_get_count)
_countなプロパティがセットされて無ければ、 lenを使ってカウントを取得してる。
今度は QuerySetPaginator の _get_count を見てみる。
def _get_count(self):
if self._count is None:
self._count = self.object_list.count() # <-- QuerySetのcountをコール
return self._count
count = property(_get_count)
_countなプロパティがセットされて無ければ、 QuerySet のcountをコールして、COUNTなSQLを発行してカウントを取得してる。なるほどー。 って事は、 Paginator を使うよりも、 QuerySetPaginator を使う方が良いね。 今までの ObjectPaginator が QuerySet しか扱えなかったんだから、 WarningでもQuerySetPaginator insteadって出して欲しいなぁ。ってか出すべきな気がする。
念のために汎用ビューを見てみると、 やっぱり QuerySetPaginator を使ってるよ。
もう一つ、今回の変更で加わったのが Page なクラス。 今まで ObjectPaginator の get_page をコールすると、 返り値は QuerySet だった。
try:
# 返り値のobject_listはQuerySet
object_list = paginator.get_page(page - 1)
except InvalidPage:
raise Http404
それが Paginator の page をコールした時の返り値は Page になる。
try:
# 返り値のpage_objはPage
# マイナス1しなくて良くなった!
page_obj = paginator.page(page)
except InvalidPage:
raise Http404
# Pageの持つobject_listプロパティにQuerySetが入ってる
object_list = page_obj.object_list
この Page なクラスはページネイション関連のデータを保持する実装になっていて、
汎用ビューで今まで使えてたページネイション関連の変数は、
全部この Page がメソッドとして実装してる。
結構な変更がかかってるけども、
明らかに使いやすくなってると思う。
今までのがデータの引き回しとかが多くて、微妙に使いづらかったってのもあるけども ![]()
ObjectPaginator を QuerySetPaginator にリプレイスする時は、
django/views/generic/list_detail.py を見れば分かりやすいと思うよ ![]()
- Posted at:
- 2008/03/26 03:31:44
- 2 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/03/26/change-paginator-on-django/trackback/
Pythonの__metaclass__を少し理解したよ。
Pythonクックブックのオブジェクト指向プログラミングを読んでて、 下の2つは等価だと知った。(今更か?)
example1:
class Someclass(Somebase):
__metaclass__ = type
x = 23
example2:
Someclass = type('Someclass', (Somebase,), {'x': 23})
って事は、 example2 の方でも type.__new__ がコールされてるって事なのかな? ちょっと試しにやってみる。
>>> class HogeMetaclass(type):
... def __new__(cls, name, bases, attrs):
... """
... printするだけの拡張
... """
... print "__new__ has been called."
... return super(HogeMetaclass, cls).__new__(cls, name, bases, attrs)
...
>>> Hoge = HogeMetaclass('Hoge', (object,), {})
__new__ has been called. # <- ちゃんと出た。
>>> Hoge
<class '__main__.Hoge'>
ちゃんとコールされた!なるほど。 って言うより、実際の動きと自分の捉え方が全く逆だった事を、 Python リファレンスマニュアル の「 3.3.3 クラス生成をカスタマイズする 」を読み直してみてようやく理解したよ。 __metaclass__.__new__ がダイレクトに呼び出されるのでは無くて、 __metaclass__ がコールされた結果として __new__ が呼び出されるのね。
これが理解出来て、やっとこさ Django の newforms.form_for_model や newforms.form_for_instance で何が行われてるかがちゃんと理解出来たよ ![]()
- Posted at:
- 2008/03/17 00:33:31
- 0 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/03/17/little-learning-metaclass-on-python/trackback/
MacのPythonを野良Portsからデフォルトに
Mac の Python を 野良Ports からデフォルトにしたよ。 色々と easy_install で入れたのでとりあえずメモ。
- readline-2.4.2
- yolk-0.3.0
- ipython-0.8.2
- docutils-0.4
- Pygments-0.9
- MySQL_python-1.2.2
- pysqlite-2.4.1
- SQLAlchemy-0.4.4
- lxml-2.0.2
- httplib2-0.4.0
- pytc-0.3
- simplejson-1.7.4
- python-twitter-0.5
- Paste-1.6
- PasteScript-1.6.2
- PasteDeploy-1.3.1
Django はsvn-trunkを使ってるので、 シンボリックリンク貼り直しておしまい。
デフォルトの Python のreadline問題は知ってたので、 Yの砂箱 さんの「 LeopardにバンドルされてるPythonでreadlineを有効にする方法を見つけた。 」を参考に回避。 無事に日本語とかヒストリーとか、いつもの使い勝手が復活。
MySQL_python も、そのまま easy_install するとエラるのを知ってたので、 パッチ当ててから叩く。肝心のパッチファイルは MacPorts の中に入ってるのを拝借してくる。
$ cd /usr/local/src
# パッチファイル2枚をコピってくる
$ cp /opt/local/var/macports/sources/rsync.macports.org/release/ports/python/py25-mysql/files/* ./
$ curl -O http://osdn.dl.sourceforge.net/sourceforge/mysql-python/MySQL-python-1.2.2.tar.gz
$ tar zxf ./MySQL-python-1.2.2.tar.gz
$ cd MySQL-python-1.2.2
# パッチ2枚を当てる
$ patch -p0 < ../patch-_mysql.c.diff
$ patch -p0 < ../patch-setup_posix.py.diff
$ cd ../
$ sudo easy_install -UZ ./MySQL-python-1.2.2
ちゃんと MySQL_python も入ったよ。めでたしめでたし ![]()
後は pysvn と戦うだけ。 ホントにコイツとは相性悪いんだよなぁ。
- Posted at:
- 2008/03/16 19:32:52
- 0 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/03/16/change-python-on-mac/trackback/
Pasteとかeasy_installとか
easy_install の勉強しようと思ってた時に、 Pylons 触ってた時に使ってた Paste ってどうなんだろ?って思って、 分からないままにちょっと試してみたよ。
とりあえず Paste のインストール。 今回は PasteScript も必要なので、一緒にインストールする。
$ sudo easy_install Paste
$ sudo easy_install PasteScript
PasteScript をインストールした時に、 一緒に PasteDeploy も入ったけれども気にしない。 インストールが終ると paster なスクリプトが一緒にインストールされるので、 これを使って作業する。
まずは適当なディレクトリに移動して、 プロジェクトを作る。 paster に create オプションを付けて実行すると、 新規作成の為に色々聞いてくるので、 流れにそって答えていく。
$ paster create
Selected and implied templates:
PasteScript#basic_package A basic setuptools-enabled package
Enter project name: myproject
Variables:
egg: myproject
package: myproject
project: myproject
Enter version (Version (like 0.1)) ['']: 0.1
Enter description (One-line description of the package) ['']: my first project
Enter long_description (Multi-line description (in reST)) ['']: my first project
Enter keywords (Space-separated keywords/tags) ['']: test
(省略)
出来たディレクトリの中を見てみると、 必要なものが一通り出来てるよ。 なので、このまま easy_install で叩いてもちゃんとインストール出来る。 中身を確認するために、オプションを指定してインストールしてみる。
$ sudo easy_install -UZ ./myproject
Processing myproject
Running setup.py -q bdist_egg --dist-dir /Users/nobu/tmp/myproject/egg-dist-tmp-GzZuQc
Adding myproject 0.1dev to easy-install.pth file
Installed /opt/local/lib/python2.5/site-packages/myproject-0.1dev-py2.5.egg
Processing dependencies for myproject==0.1dev
Finished processing dependencies for myproject==0.1dev
ちゃんとインストールされたか確認してみる。
>>> import myproject
>>> dir(myproject)
['__builtins__', '__doc__', '__file__', '__name__', '__path__']
ちゃんとインストールされてるよ!素晴し過ぎる! 何もしてないから、コレだけだと何も出来ないけども。
で、これだけだと全然面白くないので、 何か実際作ってみたいなぁ。なんて。 そこで思い付いたのが、 pateo さんが中心になってオープンソースで開発されている monologista のクライアントAPIの野良パッケージ! これを勝手に easy_install 出来るようにしてみる。
まずはプロジェクト作成から。 twitter のパッケージに合わせて、 python-monologista な名前にして paster で作成する。
$ paster create
で、中身を編集していく。 中を見てみるとこんな感じになってる。
$ cd python-monologista
$ ls
python_monologista.egg-info/ pythonmonologista/ setup.cfg setup.py
デフォルトで作られるソースの中身がディレクトリなんだけども、 今回の monologista のクライアントはファイル1枚なので、 ディレクトリは削除してしまって、ファイルに置き換えてしまう。
$ rm -rf ./pythonmonologista
肝心のファイルはと言うと、 monologista – Trac で公開されているので、 そこからありがたく拝借する。 が、ソースの管理に mercurial が使われていて、 ファイル1枚だけを落してくる方法が分らなかったので、 地味にコピペでファイルを作成。
そのあと、 python_monologista.egg-info の中にある SOURCES.txt を編集してつじつまを合わせる。
setup.cfg
setup.py
python_monologista.egg-info/PKG-INFO
python_monologista.egg-info/SOURCES.txt
python_monologista.egg-info/dependency_links.txt
python_monologista.egg-info/entry_points.txt
python_monologista.egg-info/not-zip-safe
python_monologista.egg-info/top_level.txt
monologista.py # <-- コレを足す
pythonmonologista/__init__.py # <-- コレを消す
次に、同じく python_monologista.egg-info の中にある top_level.txt を編集。
monologista.py # <-- コレを足す
pythonmonologista # <-- コレを消す
ここまでで1度インストールしてみる。 ちょっとだけやる気をみせて、tarで固めてからのテスト。
$ sudo easy_install -UZ python-monologista.tgz
Processing python-monologista.tgz
Running python-monologista/setup.py -q bdist_egg --dist-dir /tmp/easy_install-XXgsgg/python-monologista/egg-dist-tmp-FKUCd7
warning: install_lib: 'build/lib' does not exist -- no Python modules to install
Adding python-monologista 0.1dev to easy-install.pth file
Installed /opt/local/lib/python2.5/site-packages/python_monologista-0.1dev-py2.5.egg
Processing dependencies for python-monologista==0.1dev
Finished processing dependencies for python-monologista==0.1dev
おお?なんかwarning吐いてるなぁ。 インストールするものが無いとか言われてる。 たしかにインストール先のディレクトリを見ても、 monologista.py が入ってない。
色々探してみると、 setup.py の中の setup に、 py_modules で指定して渡してやると良いらしい。
from setuptools import setup, find_packages
import sys, os
version = '0.1'
setup(name='python-monologista',
version=version,
py_modules=['monologista'], # <-- コレを足す
# (省略)
)
これでもう1度インストールしてみる。
$ sudo easy_install -UZ pytthon-monologista.tgz
今度はwarning吐かずにインストール出来たよ! インストール先を見ても、ちゃんと monologista.py が入ってる! なので、早速シェルでインポートしてみる。
>>> from monologista import Api
>>> dir(Api)
['SHOW_URI',
'TIMELINE_URI',
'TODO_URI',
'UPDATE_URI',
'__class__',
'__delattr__',
'__dict__',
'__doc__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__str__',
'__weakref__',
'get_url',
'public_timeline',
'update',
'user_timeline',
'validator']
おおお!出来たよ!
かなり嬉し過ぎます!!
思わず声に出してガッツポーズ
(現在 5:00 AM)
これで monologista にAPIが実装されればスクリプトからゴニョゴニョ出来るね。 monologista の1.0がリリースが待ち遠しいね。
Paste とか PasteScript 以前に setuptools の事を良く理解出来てないので、 これを機会に真面目に勉強しようと思ったよ。 Paste と PasteScript を使う事で簡単にパッケージ化する方法を覚えて、 少しでも多くアウトプットしていけたら良いっすなぁ。
使ってるものを理解する事って大切だなぁ。って再確認出来た良い機会になりましたとさ。
- Posted at:
- 2008/03/09 05:17:45
- 4 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/03/09/paste-and-easy_install/trackback/
OptionParserメモ
Python 使ってコマンドラインからゴニョゴニョしようと思って、 調べてみたら OptionParser なんてものがあったので試してみたメモ。
6.21 optparse -- より強力なコマンドラインオプション解析器 を見ながらhoge.pyとかにして試してみる。
#!/usr/bin/env python
# vim: encoding=utf=8
from optparse import OptionParser
parser = OptionParser()
def main():
options, args = parser.parse_args()
print options
print args
if __name__ == '__main__':
main()
これだけでヘルプの表示までは出来るようになってるらしい。 -h か --help でヘルプが表示出来るらしく、 実際にやってみるとこんな感じになったよ。
$ ./hoge.py -h
Usage: hoge.py [options]
Options:
-h, --help show this help message and exit
ここからオプションを add_option なメソッドを使って、 必要なオプションを詰め込んでいけば良いらしい。 リファレンス見ながらやってみる。
parser = OptionParser()
parser.add_option("-f", "--file", dest="filename",
help="write report to FILE", metabar="FILE")
parser.add_option("-q", "--quiet", action="store_false",
dest="verbose", default=True,
help="don't print status messages to stdout")
これでもう1回ヘルプを表示してみると、
$ ./hoge.py -h
Usage: hoge.py [options]
Options:
-h, --help show this help message and exit
-f FILE, --file=FILE write report to FILE
-q, --quiet don't print status messages to stdout
ちゃんと増えてるよ。 ここまで簡単に出来ると、 今までシェルで書いてたのがバカらしくなってくるなぁ。
実際に適当に実行してみて、 どんなふうに値が取れるのか見てみると、 キーワード指定したものはoptionsに、普通に指定したものはargsに入ってくるみたい。
$ ./hoge.py -f hoge.txt
{'verbose': True, 'filename': 'hoge.txt'}
[]
$ ./hoge.py -f hige.txt -q
{'verbose': False, 'filename': 'hige.txt'}
[]
$ ./hoge.py hoge -f hige.txt -q
{'verbose': False, 'filename': 'hige.txt'}
['hoge']
add_option の引数のキーワードの種類とか説明は、 リファレンスを見るのが一番良いって覚えておく。
- Posted at:
- 2008/03/09 00:26:16
- 0 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/03/09/optionparser-memo/trackback/
Djangoのpre_saveとpost_saveがちょっと便利に
Django のちょっとしたコネタ。
モデルの save メソッドには、 raw って引数があるのですが、 r7054 から pre_save 、 post_save にもコレが入ってくるようになっております。
なので、 pre_save や post_save でコレを見てやる事で、 処理を分岐させる事が出来るようになりました。
raw が True の場合、 余計な作業は省いてね。って事なので、 このブログではフックの最初で見てあげるようにしております。
def post_save_entry(instance, created, raw):
"""
エントリーのセーブ後のフック
"""
if raw or not created:
# raw=Trueか新規登録じゃ無い場合はココでおしまい。
return
# 実際やりたい処理がつづく。
loaddata してデータを取り込む時なんかも raw が True で入ってくるので、 こんな感じにしておくと余計な処理を省けるので便利。
- Posted at:
- 2008/02/17 00:39:07
- 2 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/02/17/more-useful-save-hook-on-django/trackback/
Djangoでパーマネントリダイレクト
前のブログからの変更でURLが結構変わったので、 どうしようかな?って思ってたんですが、 Django の汎用ビューにある redirect_to を使ってみる事に。
中を見てみると、必須な引数は url のみで、 残りの引数で置換してリダイレクトする。って仕組み。 また url を None にすると、 ステータスコード410なレスポンスを返してくれる。
今回は /weblog/ はリダイレクトしてあげて、 /bookmark/ に来たのは410を返すようにしたかったので、 こんな感じにしてみたよ。
# urls.py
urpatterns = patterns('django.views.generic.simple',
# weblogに来たリクエストをリダイレクト。
(r'^weblog/(?P<path>.*)$', 'redirect_to', {'url': '/%(path)s'}),
# bookmarkに来たリクエストは410を返す。
(r'^bookmark/(?P<path>.*)$', 'redirect_to', {'url: None'}),
)
410の場合、ブラウザで見ると何も表示されないのが残念だけども、 気になるようならコピペして弄ってあげれば良いと思う。
こういうちょっとしたのが既に実装されてるってのは、 地味に嬉しかったりする。
- Posted at:
- 2008/02/10 01:35:20
- 0 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/02/10/permanent-redirect-on-django/trackback/
ブログ乗り換えました!
新しく作り直したブログに乗り換えました! て言っても、まだまだ安定してないと思われます。 ココからじゃんじゃんバグを直して行くよ!
Django のソースを眺めながら作った(読めてるかどうかは別)ので、 また少し Django と仲良くなれたような気がする。
URLが変わってしまったトコロはリダイレクトさせてる(つもり)なのですが、 リンク切れを発見された方は、教えて頂けるとありがたいです。 無くなっちゃったURLに関しては...。 まぁ、それはご愛嬌と言う事で。
ソースは ここらへん に転がっております。 ので、ツッコミとかも頂けると両手をあげて喜びます!
Django の r7054 以前で動かしてしまって、
ちょっとしたハプニングに見舞われたのは秘密です ![]()
- Posted at:
- 2008/02/09 03:30:08
- 6 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/02/09/change-blog/trackback/