7月 29
以前のエントリーで集計クエリを利用する方法を説明しましたが、それだとDatetimeのフィールドを「年月」ごとに、っていう集計はできませんでした。購買履歴データを月別に件数集計したいときなんかは非常に困るんですよね。ので、それを解決する方法です。
select_column = {“d_column”: ”DATE_FORMAT(pub_date, ‘%%Y%%m’)”}
_list = PurchaseRecord.objects.extra(select=select_column)
_list = _list.values(“d_column”).annotate(cnt=Count(‘id’))
上記はMySQLを利用しているケースの書き方です。
あ、横長になると見難いから2行に分けただけで、1行で大丈夫ですからね。
「ORマッパー使ってるのにデータベースに依存ってなによ」
な感じではありますが、extraを使っている部分は直接クエリが流れるみたいですね。
つまり、「DATE_FORMAT(pub_date, ‘%%Y%%m’)」の部分は
select *,DATE_FORMAT(pub_date, ‘%Y%m’) as d_column
from app_purchaserecord
といった風に、追加されるのです。で、追加されたカラムを使って集計、という流れですね。うっかりミスりやすいのが「%」を二つにする点でしょうか。
あと、データベースを変えたら「select_column~」の部分に修正が必要です。使用するデータベースに依存した書き方になります。ここは要注意ですね。
7月 28
例えば組織図なんかのmodelを構築する際に、「属する部門」を表現するには再帰的なリレーションを使うことになると思います。「部門が属する部門」ですね。
class Div(models.Model):
name = models.CharField(max_length=100)
upper = models.ForeignKey(Div)
だと、syncdbの時に「Divなんてありません」と怒られてしまいます。そりゃそうだ。
ので、
class Div(models.Model):
name = models.CharField(max_length=100)
upper = models.ForeignKey(’self’)
と書きます。
Python2.5、Django1.1.1でのお話でした。
7月 27
DjangoでMySQLを利用する場合、初期状態だとMyISAMでテーブルが作成されます。まぁ後からalterすればよい話ではあるのですが、なんかそれだと違うかなぁと。
一昔前はInnoDBはMyISAMほどパフォーマンスが優れず、更新系のテーブルと参照系のテーブルでストレージエンジンを使い分けたものですが、今はストレージエンジンによるパフォーマンスの差も少ないため、行レベルロックができるInnoDBを積極的に採用するケースが増えてるみたいですね。まぁ数千万行をレコードから数ミリ秒でレスポンスを返さないといけないケースとかではどうなるのかわかりませんが。
DATABASE_OPTIONS = {
“init_command”: “SET storage_engine=INNODB”,
}
settings.pyに上記を記載するだけでsyncdbするときにInnoDBでテーブル作成してくれます。が、この記述はテーブル作成が終わって運用にはいったら削除するように、とのことです。
MySQLとの接続が確立された後にわざわざストレージエンジンを指定するコマンドを送っているっぽいので、普段は無駄な処理が発生しちゃうっていうことなんですかね。
5月 19
Djangoでテンプレートを出力するとき、あわせてcookieの書き込みもしたいケースがあります。よくあるサンプルだと
return render_to_response(‘index.html’)
っていう書き方をしているのですが、「コレ、cookieの書き出しどうすんのよ」って悩んでしまいました。その結果、
def index(request):
_max_age = 45*24*60*60
_expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=_max_age), “%a, %d-%b-%Y %H:%M:%S GMT”)
_response = render_to_response(‘index.html’)
_response.set_cookie(‘hoge’, value=’hogehoge’, max_age=_max_age, expires=_expires, path=’/')
return _response
こんな書き方をしたらできているようでした。上記の例だと、現在時刻から45日間有効なcookieを書き出しています。
単純なことなんですが、意外と躓いてしまった。。。
Python2.5、Django1.1.1です。
そういえばDjangoの1.2がリリースされてますね。検証したいな。
4月 27
ハセテツは複数の言語を状況に応じて使い分けてきましたが、最近のWeb系開発はPython(+Django)がメインになってきています。
ちょっと前までRuby(+Rails)だったのですが、いろいろと考えるところもあり、乗り換えました。その乗り換えた理由をまとめてみようと思います。
まとめてみたら「やっぱりRailsじゃね?」となるかもしれません。w
Ruby(+Rails)
- Rails便利すぎる。
- コントローラとモデルが別々のファイルになってくれるのはソースが追いやすい。
- Railsを通さない画像やCSSはpublicフォルダに置けばよいのはわかりやすい。
- urlディスパッチャーがいまいち使いにくい。(知らないだけかも)
- APサーバはmongrel一択?
- VirtualHost使おうとするとApache+Passenger(mod_rails)だが、これが重い。(チューニングで速くなるのかも)
- というか、Railsがそもそも重い。
- ちゅーか、Rubyが重い、遅い。
- PassengerはWindowsじゃ動かない。
- mongrelもPassengerより激速軽快かというと、そうでもない。
- gem便利すぎ。
- ワンライナで書く人が多くて、Perlとおなじ匂いがする。
ハセテツが使っていたRubyは1.8で、速くなったといわれる1.9には触れていないのでもしかすると古いのかもしれません。でも、Railsって1.9には対応してないですよね?(本日現在)
Python(+Django)
- Django便利すぎる。
- 日本語の書籍、情報が少なすぎる。(ハマるとキツい)
- modelが一枚のファイルなので、大量のmodelがあると可読性が激しくダウン。
- viewファイルをフォルダで分けたくても、アプリケーションフォルダ直下以外にviewファイル置くなら「sys.path.append」しないといけない。(違う?)
- urlディスパッチャーのカスタマイズが超便利。
- mod_pythonが使えるので、Windows環境だろうがLinux環境だろうが気にせずVirtualHostが使える。
- 速い。
- 日本語大変。Shift_JIS怖い。
- easy_installでパッケージのインストールは楽だが、管理が悪夢。
メリットとデメリットが入り乱れました。見難くてすいません。
結局フルスタックのフレームワークということで、DjangoとRailsに関しては一長一短だと思います。個人的にはRailsの方が良くできてたかなぁと。
最終的に、「RubyonRailsは重い」というオチになってしまうのです。Rubyも、Railsも、です。ただ、これはRuby1.9が実は劇的に速くなっていて、1.9に対応したRailsがリリースされたらすべて解決されるのかもしれません。
それでも、いまのところPythonの軽快さ、シンプルさに満足しています。あと、GAEでPythonが動くのもいいですよね。
「Googleが導入した言語」というのがハセテツ的に琴線に触れたのかもしれません。
ミーハーですいません。w
4月 21
隠蔽するっていうか、viewにごりごり書くのではなくてModelのメソッドで処理してしまおうという話です。このエントリの応用ですね。
import hashlib
class Member(models.Model):
name = models.CharField(max_length=32)
passwd = models.CharField(max_length=256)
def save(self, force_insert=False, force_update=False):
self.passwd = hashlib.md5(self.passwd).hexdigest()
super(Member, self).save(force_insert, force_insert)
Modelのsave()メソッドをオーバーライドしています。これで、登録フォームなどからPOSTされたデータをそのままsave()してもデータベース上には暗号化された状態で保存されます。
adminサイトで作成したユーザ情報なんかのパスワードとかも、暗号化して保存してくれます。便利便利。
4月 20
コンボボックスの<option>~</option>の値は動的に指定したいものです。が、Djangoでどうしたらよいものか、数時間悩みました。
これが意外と簡単で、こんなことで数時間悩んだ自分が情けなく、それでも発見した自分をほめてやりたく。w
class Form_hoge(forms.Form):
month = forms.ChoiceField()
_form = Form_hoge()
_form.fields['month'].choices = [(1,1), (2,2), (3,3)]
フォームを呼んだ後に指定するだけ。
なんですぐ気づかなかったんだろ。。。
Django1.1.1、Python2.5で動作検証。
4月 20
Djangoのモデルで集計クエリというと、1.0以降でcount()が使えますが、1.1になってさらに多機能になっていました。
from django.db.models import Sum
_result = Detail.objects.all().values(‘category’).annotate(price=Sum(‘price’))
for item in _result:
print item
Detailというモデルがあって、それをcategoryというフィールドでグループ化し、priceというフィールドのsum(合計)を求めています。
values(~)は必須ではありません。グループ化しないと全体のsumですね。
annotate(得られる結果の名前=Sum(集計対象フィールド))
です。サンプルの「price」をsum_priceにしても結果は得られます。
また、annotateで利用できるのはsumだけでなく、Avg、Max、Min、Countも使用できます。サンプルコードだとSumしかインポートしていませんが、「*」でまとめてインポートするか、「Avg, Max, Min, Sum, Count」をそれぞれインポートしてあげれば使用できます。
ちなみに、サンプルコードで得られるitemは、Detailモデルのインスタンスではありません。辞書です。最初ここに気づかず、「?」となっていました。w
4月 06
Djangoのフォームでclassを指定する方法です。Widgetクラスを使うのですが、情報が少ないような気がします。探し方ヘタ?
class Form_login(forms.Form):
mailadd = forms.EmailField(max_length=32,widget=forms.TextInput(attrs={‘class’:'form_entry’}))
passwd = forms.CharField(min_length=6, max_length=12,widget=forms.PasswordInput(render_value=False))
これで出力されるフォームには「class=”form_entry”」が追加されます。
パスワード側もWidgetクラスを使用していますが、Widgetクラスを活用するとDjangoのフォームってものすごく便利に活用できるような気がします。
Django 1.1.1、Python2.5で検証しました。
3月 26
PHPでいうところの$_SERVERですね。
request.META.get(‘HTTP_HOST’)
ホスト名はこんな感じです。リファとかユーザーエージェント(ブラウザ)もここで。モバイルの場合端末識別番号も取得できますね。