Djangoで同じモデルのフィールド同士によるフィルタを実行する方法
例えば
class Person(models.Model):
name = models.CharField(max_length=100)
nickname= models.CharField(max_length=100)
みたいなモデルがあって、「名前とニックネームがおんなじ人を抽出したい」っていうケースがあるとします。
_person = Person.objects.filter(name=nickname)
はNGですね、エラーがでます。「nicknameは宣言されてません」となります。そこで、
from django.db.models import F
_person = Person.objects.filter(name=F(‘nickname’))
と書きます。フィールドを値として展開してくれるみたいですね。site-packages\django\db\models\expressions.pyにソースもあるので、きちんと調べればいい感じに使えそうです。
Python2.5、Django1.1.1でのお話でした。
Pythonで来月1日と月末の日付を取得する方法
簡単かと思いきや、これが意外とめんどくさいんです。
import datetime
_today = datetime.date.today()
_next_month = datetime.date(_today.year, _today.month+1, 1)
でいいかと思いきや、今日が12月だとしたらエラーが出ます。monthの値は1から12の間に限定されていて、自動的に「月が13なら翌年」にはならないんですね。
ので、
from dateutil.relativedelta import relativedelta
import calendar_date = datetime.date.today() + relativedelta(months=1)
_next_first = datetime.date(_date.year, _date.month, 1)
_next_last = datetime.date(_date.year, _date.month, calendar.monthrange(_date.year, _date.month)[1])
timedeltaだと「日の加減算」しかできないのですが、relativedeltaだと年、月、日の加減算が可能になります。「years」「months」「days」でそれぞれ指定できます。(months=-1)で前月です。
月末日を求めるには「calendar.monthrange」を利用します。
print calendar.monthrange(2010, 3)
で、(0,31)と表示されます。これは2010年3月が月曜から始まり、31日までありますよ、ということです。曜日は月曜の0から始まり日曜の6で終わります。ので、
calendar.monthrange(2010, 3)[1]
で月末日付が取得できるというわけです。
Python2.5でのソースコードです。他のバージョンでは検証していません。
Djangoの開発用サーバを他のPCから参照する方法
普通に
manage.py runserver
すると127.0.0.1:8000で起動しますので、開発用サーバは他のPCから参照できません。「ちょっとこの画面見て欲しいな」とかの時には席まで来てもらう必要があります。
まぁそんなに広いオフィスでもなければ別に問題ないのでしょうが、いちいち面倒なんですよね。
manage.py runserver 192.168.0.1:8000(開発用マシンのIPが192.168.0.1の場合)
で開発用サーバを起動すると、他のPCから「http://192.168.0.1:8000」で参照できます。
検証用サーバとか別に用意してあるんですけど、わざわざデプロイするほどでもない時ってありますよね。そういう時にちょっと便利です。
Pythonでフォルダ内のファイル一覧を取得する
用途に合わせて2パターンあります。
#!/usr/bin/python
# -*- coding: utf-8 -*-import os
list = os.listdir(‘/path/to/dir/’)
for file in list:
print file
この例だと、対象フォルダ内のすべてを列挙します。つまり、サブフォルダも抽出対象ということです。これでいい場合もあるし、いやいやファイルだけ欲しいんだよ、という場合もあるでしょう。
#!/usr/bin/python
# -*- coding: utf-8 -*-import glob
list= glob.glob(‘/path/to/dir/*.*’)
for file in list:
print file
上記の例であれば、対象フォルダのファイルだけを抽出します。まぁファイルだけ、とっても「拡張子が付いているもの」、というか名前にドットが付いているものを抽出しています。よって、ドットが付いた名前のフォルダも引っかかりますね。
ふたつめの例で、さらにos.path.isfile(対象)を使ってファイルか否かをチェックすればよいと思います。
qmail+vpopmailの環境でDjangoからメールをSMTP認証で送信する
vpopmailを使ってqmailでバーチャルドメイン運用している場合、困った問題がひとつあります。vpopmailがCRAM-MD5に未対応なんですね。
まぁMTAとして運用するにはたいして問題ないんですが、Djangoでメール送信しようとすると大問題、DjangoはSMTP認証のときにパスワードをMD5で暗号化してqmailに送っているようです。よって、qmailから「SMTP認証にコケました。」と叱られてしまうわけです。
ので、PLAINでSMTP認証をする方法です。
PythonインストールディレクトリのLibフォルダに「smtplib.py」があるはずです。その562行目付近、
preferred_auths = [AUTH_CRAM_MD5, AUTH_PLAIN, AUTH_LOGIN]
を
preferred_auths = [AUTH_PLAIN, AUTH_LOGIN,AUTH_CRAM_MD5]
に変更します。認証の優先順位ですね。優先順位を入れ替えているだけなのでCRAM_MD5が使えなくなるわけでもなく、vpopmailの困った仕様にも対応可能です。
MTA変えただけでメールが送信できなくなり、しかもMUAからは無事に送信で来ちゃっていたのでまったくわからず、一晩悩みました。
Pythonで文字列エンコーディング
例えばWindowsクライアントからアップロードされたCSVとかはたいていShift_JISなわけです。Pythonは基本的にUTF-8(Django使ってるから、というのも大きな理由ですな)なので、そのままだとえらいこっちゃになるわけです。
u”ほげほげ”.encode(‘cp932′,’utf-8′)
cp932(Shift_JIS)からutf-8にエンコードしますよ、という指定です。「文字列.encode(‘utf-8′)」でもいいのですが、それだとエンコード前の文字列エンコーディングは自動判定みたいですね。しょっちゅう「こんな文字列エンコード知らない」ってエラーが出ました。
ので、「~から~にエンコードしてね」ってやさしく説明してあげたほうが確実というわけです。
PythonでDjangoを使ったバッチ処理
たいていのアプリにバッチは付き物なのですが、DjangoにはRailsでいうところのscript/runnerがありません。
まぁPythonでバッチ書いてそれを叩けばいいのですが、settingsの値とか、モデルデータとか、やっぱり共有したいわけですよ。
# -*- coding: utf-8 -*-
import sys
import ossys.path.append(“C:/works/Python”)
os.environ['DJANGO_SETTINGS_MODULE'] = ‘hogepj.settings’from hogepj.app.models import *
_hoge = Hoge.objects.all()
for item in _hoge:
print item.name
sys.path.appendに書くのはプロジェクトがあるフォルダです。プロジェクトフォルダは含みません。Pythonというフォルダのなかにhogepjというプロジェクトフォルダがある、という前提です。
Railsだと「Railsアプリのバッチを実行する」な感じだったのに対して、Djangoだと「バッチをDjangoアプリの一部として実行する」って感じでしょうか。
環境はPython2.5、Django1.1.1です。
PythonでURLエンコードする方法
GETでクエリを引きずり回す時などに使いますね。後は日本語名称のファイルをクライアントに出力するときでしょうか。2バイト文字の世界の人々には必須の知識ですよね。
import urllib
print urllib.quote_plus(‘ほげほげ’)
print urllib.unquote_plus(‘%82%D9%82%B0%82%D9%82%B0′)
上がエンコード、下がデコード。
ソースコード中に2バイト文字書くときは
u’ほげほげ’
といった感じでuをつけないといけなかったのですが、ここでは不要です。
Djangoのformでカスタムバリデーションをする方法
例えば会員登録とかでパスワードを確認のために2回入力してもらうときとか、両方の値があっているかチェックする必要があります。
Djangoの標準バリデーションだとそこまではやってくれないんですね。
でも、バリデーションのカスタマイズはとっても簡単です。簡単なハズなのに、とってもてこずりました。w
# -*- coding: utf-8 -*-
from django import forms
class RegistForm(forms.Form):
name = forms.CharField(max_length=100)
mailadd = forms.EmailField(max_length=100)
passwd = forms.CharField(min_length=6, max_length=12, widget=forms.PasswordInput(render_value=False))
passwd_confirm = forms.CharField(min_length=6, max_length=12, widget=forms.PasswordInput(render_value=False))def clean_passwd_confirm(self):
_pswd = self.cleaned_data['passwd']
_pswd_confirm = self.cleaned_data['passwd_confirm']
if _pswd!=_pswd_confirm:
raise forms.ValidationError(u’確認用パスワードが異なります。’)
else:
return _pswd_confirm
clean_フィールド名のメソッドが、対象フィールドのバリデーション用メソッドです。self.cleand_dataで値にアクセス、検証してNGならraiseする。OKなら値をそのまま返す。
これだけ!w
インターネット上でビジネスをするなら必読だと思う本
特にFREEがお気に入りです。
両方ともクリス・アンダーソンの著作。「ロングテール」というキーワードの生みの親(厳密には違いますが)で、Wiredの編集長ですね。
読んでなにか答えが得られる本ではないと思いますが、インターネット上でビジネスをする、インターネットにかかわる仕事をする人たちにとっては必読の本ではないかと思います。