Tag/Django
モデルの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/
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/
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/
Djangoのflatpagesは凄いよ!
今作ってるブログ の静的ページをどうしようか迷ってる時に、 常山さん に刺激されて flatpages を使ってみたよ。
使い方はめちゃめちゃ簡単。 settings.py の INSTALLED_APPS と MIDDLEWARE_CLASSES に追記するだけ。
# 一番最後に追加する。
MIDDLEWARE_CLASSES = (
...
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
)
INSTALLED_APPS = (
...
'django.contrib.flatpages',
)
デフォルトだと flatpages/default.html なテンプレートを探しにくるので、 先に用意しておいてあげる。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>{{ flatpage.title }}</title>
</head>
<body>
{{ flatpage.content }}
</body>
</html>
後は管理画面からゴリゴリ登録しまくってあげるだけ。 urls.py の編集はしなくても大丈夫! 登録したURLにアクセスすると、 ちゃんと表示されるはず!
コレ凄いよ!めちゃめちゃ便利! ステータスコード404に反応して該当するページを探してくれるよ! 簡単なサイトなら、コレだけで出来ちゃうかもよ!(言い過ぎ)
今欲しい機能としてはちょっとオーバースペックだけど、
これはかなり良いなぁ ![]()
アイデアだけもらうかなぁ? それともコレ使っちゃうかなぁ? かなり迷うトコロでありますです。
flatpages の詳しい説明は flatpages アプリケーション : Django オンラインドキュメント和訳 でどうぞ。
- Posted at:
- 2008/02/07 05:42:55
- 2 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/02/07/django-flatpages-is-very-useful/trackback/
Djangoでモデルのsaveをフックする
Django の子ネタ。
Django のモデルの save メソッドには、 pre_save と post_save なフックポイントがあって、 これ使うとちょっとした処理を差し込めて便利。
# myapp/models.py
from django.db import models
# 必要なものをインポート。
from django.dispatch import dispatcher
from djanog.db.models import signals
class MyModel(models.Model):
# 省略
# セーブ前に呼ばれる関数
def pre_save_mymodel(instance, **kwargs):
# セーブ前にやりたい事を書く。
# セーブ後に呼ばれる関数
def post_save_mymodel(instance, **kwargs):
# セーブ後にやりたい事を書く。
# 関数を登録。
dispatcher.connect(receiver=pre_save_mymodel,
signal=signals.pre_save,
sender=MyModel)
dispatcher.connect(receiver=post_save_mymodel,
signal=signals.post_save,
sender=MyModel)
こうやっておくと、 MyModel の save が呼ばれたときにコールされる。 post_save な関数には created ってブール値が入ってきてるので、 新規登録なのか?更新なのか?も分かるようになってるよ。
今作ってるブログ では、 コレ使って更新Ping飛ばしたり、 twitterに投げたりしとります。
- Posted at:
- 2008/02/07 01:23:31
- 4 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2008/02/07/hook-models-save-on-django/trackback/
Djangoのレスポンスコード
Djangoのちょっとした子ネタ。
今まで HttpResponse から始まって、 HttpResponseForbidden とか HttpResponseNotFound とか、 もりもりインポートするか、
t = loader.get_template('sometemplate.html')
response = HTTPResponse(t.render(Context({})))
response.status_code = 404
return response
とかして、ステータスコードをごにょごにょしてたんですが、 実は HttpResponse のコンストラクタで、 ステータスコードを指定出来る事を発見。
return HttpResponse(t.render(Context({})), status=404)
とかすると、ステータスコード404でレスポンスを返せるよ。
これは地味に便利 ![]()
- Posted at:
- 2008/02/05 22:28:04
- 0 Comments
- 0 TrackBacks
- Trackback:
- http://humming.via-kitchen.com/2008/02/05/responsecode-on-django/trackback/
mod_wsgiでHTTP_AUTHORIZATIONを受け取る。
Django でDigest認証用のバックエンドを書いていたんだけれども、 Apache と mod_wsgi で動かすとなぜか認証を通過出来ない。
で、色々と泥臭いデバッグを繰り返したところ、 そもそも HTTP_AUTHORIZATION ヘッダが渡されていない様子。
request.META.get('HTTP_AUTHORIZATION') # <-- コレがNoneになる。
ちょこっとググってみる前に、 mod_wsgi のドキュメントを眺めてみると、 ちゃんと Configuration Guidelines や Configuration Directives に書いてありました。
WSGIPassAuthorization On
これを VertualHost ディレクティブに追加したら、 バッチリ HTTP_AUTHORIZATION を受け取れた! 認証も通りましたですよ!
セキュリティ面から、 WSGIPassAuthorization ディレクティブはデフォルトではOffらしい。 なるほどなるほど。
- Posted at:
- 2007/12/27 01:43:07
- 2 Comments
- 0 TrackBacks
- Trackback:
- http://humming.via-kitchen.com/2007/12/27/passed-httpauthorization-on-modwsgi/trackback/
newformsが変わってたよ。
いつの間にか、 newforms の form_for_model と form_for_instance がdeprecatedになってるよ。 変わりに ModelForm が出来ていて、 モデルに紐づかないフォームと同じ様に定義出来るようになったっぽい。
# vim: encoding=utf-8 :
from django import newforms as forms
from blog.models import Entry
class EntryForm(forms.ModelForm):
"""
エントリーに紐づくフォーム。
"""
class Meta:
model = Entry
基本はコレだけで良いみたい。 使い方は今までと変わらないのがありがたいっすなぁ。
>>> from blog.forms import EntryForm
>>> form = EntryForm()
>>> form.is_bound
False
>>> form.is_valid()
False
>>> del form
>>> data = {'title': u'test', 'slug': u'test', 'body': u'test'}
>>> form = EntryForm(data=data)
>>> form.is_bound
True
>>> form.is_valid()
True
>>> object = form.save()
>>> object
<Entry: test>
更新の時は、 __init__ に instance キーワードで渡してやると良いみたい。
>>> data = {'title': u'new title', 'slug': u'new-slug', 'body': u'new text'}
>>> # instanceキーワードで渡す。
>>> form = EntryForm(data=data, instance=object)
>>> form.is_bound
True
>>> form.is_valid()
True
>>> object = form.save()
>>> object
<Entry: new title>
バリデーションするフィールドを指定する fields は、 フォームの Meta クラスに移動したらしい。 それと、 exclude ってのも追加されたみたいで、 fields と逆の、バリデーションから省くフィールドを指定出来る。
# vim: encoding=utf-8 :
class EntryForm(forms.ModelForm):
"""
"""
class Meta:
# ターゲットとなるモデルを指定。
model = SomeModel
# バリデーションするフィールドを列挙。
fields = ('some', 'fields', 'here')
# バリデーションしないフィールドを列挙。
exclude = ('some', 'exclude', 'fields', 'here')
今までよりも少し拡張しやすくなったような。 フィールドのオーバーライドも出来るみたいなんだけども、 そこらへんはまだ試してないので、またおいおい。
- Posted at:
- 2007/12/23 17:26:02
- 2 Comments
- 1 TrackBack
- Trackback:
- http://humming.via-kitchen.com/2007/12/23/changed-newforms/trackback/