Djangoやってて忘れっぽいところとかをメモ
manage.pyのコマンドを別のプログラムから実行
from django.core.management import call_command
call_command('hoge')
何か作業した後に自動でcollectstaticしたいとかそんなこともこれで出来る
フォームのフィールドをグループ化
yieldを使うと良い感じだった。
group1 = ["hoge1","fuga1"]
group2 = ["hoge2","fuga2"]
class HogeForm(ModelForm):
...
def group1(self):
for name in group1:
yield self[name]
def group2(self):
for name in group2:
yield self[name]
<h3>group1</h3>
{% for field in object.group1 %}
<li>{{ field.data}}</li>
{% endfor %}
<h3>group2</h3>
{% for field in object.group2 %}
<li>{{ field.data}}</li>
{% endfor %}
こんな感じ
django-formtoolsの確認画面でoptionタグのテキストを表示する
テンプレートフィルタを作ると、そこらへんを自由にできる
アプリ\/templatetags\/data_verbose.py
from django import template
register = template.Library()
@register.filter
def data_verbose(boundField):
"""
Returns field's data or it's verbose version
for a field with choices defined.
Usage::
{% load data_verbose %}
{{form.some_field|data_verbose}}
"""
data = boundField.data
field = boundField.field
if hasattr(field, 'choices'):↲
return {str(k):str(v) for k,v in field.choices}.get(data, '')↲
return data↲
{% for field in object %}
<li>{{ field|data_verbose}}</li>
{% endfor %}
参考にしたHPそのままだとモデルのフィールドがテキスト以外だとエラーが出るのでちょい変更した。
save前後にモデルやフォームのデータを変更する方法
- How can I change a Django form field value before saving?
- Django - How can i modify text before save them to database?
方法は何種類か
- clean_[フィールド名] メソッドをフォームクラス内に作る
- saveメソッドをオーバーライド
- views.pyでrequestをいじる
テンプレート内でモデルのフィールドについてループ
Iterate over model instance field names and values in template
{% for field in object %}
<li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}
て感じ。fieldがBoundFieldクラスで、その中のdataメソッドでデータを取得している。
認証済みチェック
Django1.10以降
views.pyなどで
request.user.is_authenticated
Django1.9以前
request.user.is_authenticated()
テンプレート内
{% if user.is_authenticated %}
参考:How to check if a user is logged in (how to properly use user.is_authenticated)?
GETパラメーターを入れてそのままリダイレクト
response = redirect('namespace:viewname')
get_params = request.GET.urlencode()
response['location'] += '?'+get_params
return response
forms.DateFieldでISO-8601フォーマットの入力を受入れる
html5でdatetime-localとか扱おうとすると、入力値がISO-8601形式になって、そのままだとDateFieldが入力をdatetimeの正しい形式として認識してくれない。
で、input_formatsを設定するといけるらしいんだけど、あまりマニュアルでちゃんと説明してない。Stack Overflowの情報とかをググると設定方法が間違ってるコードとかあって、正しいのが良く分からない。で、結局色々調べたら
# models.py
date = forms.DateField(input_formats=('%Y-%m-%dT%H:%M:%S',))
とタプルで渡す必要があるらしい
参考:うえちょこ@ぼろぐ» [Django]forms.DateFieldのinputs_formatではまった
ModelForm使ってフォーム出力する時にrequeied属性をテンプレートで参照するとか
# models.py
hoge = forms.CharField(max_length=100,widget=TextInput(attrs={"required":"true"}))↲
こんな感じで定義したフィールドの属性取ってきたい場合、
\{\% for field in form \%\}
\{\% if field.field.required \%\}<p>必須</p>\{\% endif \%\}
\{\% endfor \%\}
適当だけど、こんな感じでアクセスできる。
ModelFormでcleanを使ってデータの検証する時に、updateしようとしてるかcreateしようとしてるか判断する
createの時だけチェックしたかったので、色々調べてみたらclean(self)内で、self.instanceでチェックすると良いみたい。例えばプライマリーキーでidがある場合こんな感じ
# models.py
class Hoge(forms.ModelForm):
def clean(self):
cleaned_data = self.cleaned_data
if self.instance.id is None:
#create用の処理
raise ValidationError('例外出すのはこんな感じ')
else:
#update用の処理
return cleaned_data
instanceの型チェックする方が正攻法かも。例外のメッセージは
# views.py
def test(request):
form = Hoge(request.POST)
if form.is_valid():
#例外が無かった場合
else:
for k,v in form.errors.item():
#kが例外のフィールド。cleanの場合は__all__
#vが例外のメッセージ
みたいな感じで取り出せる。
runserver実行時にコンソールにスタックトレースを出力する
# settings.py
LOGGING={
'version':1,
'handlers':{
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers':{
'django.request':{
'handlers':['console'],
'propagate':True,
'level':'DEBUG',
}
},
}
これを書いておけばコンソールにスタックトレースを出力してくれる。apiのコード書いてる時とかajaxで呼び出してる場合とかに便利。
参考:python - Print a stack trace to stdout on errors in Django while using manage.py runserver - Stack Overflowというかここのコード丸コピー
Gmail送信
# settings.py
EMAIL_USE_TLS=True
EMAIL_HOST='smpt.gmail.com'
EMAIL_HOST_USER='[email protected]'
EMAIL_HOST_PASSWORD='mypassword'
EMAIL_PORT=587
# views.py
from django.core.mail import send_mail
def mail(request):
send_mail('Subject','message','mymail.gmail.com',['[email protected]'],fail_silently=False)
こんだけ
参考:DjangoでGmailを使ってメール送信 - brainstorm、
django+uwsgi+nginxで502エラーが出た原因の1つ
djangoはあまり関係無いけどuwsgiの設定の問題だったので、こちらに。django+uwsgi+nginxで502エラーが出て、runserverでは普通に動いたので、しばらくググって解答が得られたのでメモ
DataTablesを使って動的にテーブルデータをロードしようとしたら、runserverでは上手く行くのにuwsgiだと502エラーが出てロード出来なかった。最初にnginxのログを見てみると、uwsgiのとこでエラーと書いてあったので、uwsgiのログをチェックした。すると
invalid request block size: xxxxx (max 4096)...
みたいなエラーが出ていた。リクエストが大きすぎるのかなと思い、リクエストの一部を削ってみたらエラーが出なかった。んで、対策をググってみたら書いてあったので、そのまま下のコードを実装してみた。
# uwsgi.ini
buffer-size=65535
みたいにバッファーサイズを大きく(例では64k)した。これでエラーが消えたのでめでたしめでたし。
運用環境と開発環境で設定を切り分ける
開発環境用にlocalアプリを作って、local/settings.pyを作成し、開発環境での設定を書いて、プロジェクトのsettings.pyの最後に以下を追加すると良い
try:
from local.settings import *
except ImportError:
pass
参考:パーフェクトな Django の設定ファイル -- Kosei Kitahara's Blog
テンプレートの読み込まれ順
- TEMPLATE_DIRSで指定されたディレクトリの順番で、そこを起点にした全てのテンプレート
- INSTALLED_APPSに指定した順にアプリケーション直下のtemplatesディレクトリを調べる
hogeアプリでテンプレートを指定した場合に、hogeアプリ直下のtemplatesディレクトリを調べてくれる訳ではないので注意が必要。
例えば
TEMPLATE_DIRS = ['/home/hoge/templates',]
INSTALLED_APPS = (
'hoge',
'fuga',
)
と書いてあり、プロジェクトが/www/a_project/に置いてある場合、
- /home/hoge/templates
- /www/a_project/hoge/templates
- /www/a_project/fuga/templates
の順でテンプレートの存在を調べる
参考:DjangoのTemplateローダ | tsuyuki.makoto
url末尾に必ずスラッシュを入れる
url末尾にスラッシュが無い場合、スラッシュ有りにリダイレクトする設定
# settings.py
APPEND_SLASH = True
Django rest apiで日時としてタイムスタンプを出力する
参考:django rest framework - serialize a datetime as an integer timestamp - Stack Overflow
どうもDjango rest frameworkのDateTimeFieldで指定するformatパラメータだとタイムスタンプが指定出来ないみたいなのでこんな感じにすると出来る。
# serialize.py
from rest_framework import serializers
import time
class HogeSerializer(serializers.Serializer):
date = serializers.DateTimeField()
timestamp = serializers.SerializerMethodField()
def get_timestamp(self, obj):
return time.mktime(obj.date.timetuple())
javascriptのタイムスタンプに対応するためにはget_timestampの返り値を1000倍すればおk
Paginatorでcount文を実行させずに指定件数分のページングを行う
分量が多くて、先頭1000件を100件ずつページング表示させるので十分かなという時に、いちいちcount文を発行させたくない時
paginator = Paginator(query, 100)
paginator._count = 1000
queryは適当なQuerySet
参考:django - How to implement a paginator that doesn't call count(*) - Stack Overflow
メソッドの呼び出し元オブジェクトを取得
inspectモジュールってのが使えるらしい
import inspect
...
(frame, filename, line_number, function_name, lines, index) = inspect.getouterframes(inspect.currentframe())[1]
print(frame, filename, line_number, function_name, lines, index)
参考:How to use inspect to get the caller's info from callee in Python? - Stack Overflow
プロファイラ使った時に、socket.pyが呼ばれてるのは分かったけど、その呼び出し元がイマイチわからなかった時に使った。
urlsでviewにパラメータを送る
# urls.py
url('^$',views.hoge, {'fuga':'hage'}, name='hoge'),
...
# views.py
def hoge(request,fuga):
...
パラメーターに指定する名前(上の場合はfuga)がviewsとurlsで同じ名前になっていることが必要(でないとエラーが出る)
参考:URL dispatcher | Django documentation | Django
Ajaxでpostしたい時
csrfフィルタが初期状態で設定されてるし、csrfフィルタはあった方が良いので、次のようになったら良いかも(Jqueryを使う場合)
$.post(url,{csrfmiddlewaretoken: '{{csrf_token}}'}...);
参考:Django CSRF check failing with an Ajax POST request - Stack Overflow
AjaxのPOSTで配列を受け取りたい時
var arr = ['hoge','fuga'];
$.post(url, {arr:arr}, function(){alert('success');});
該当するビューでこんな風に書く
def hoge(request):
arr = request.POST.getlist('arr'[]')
ちなみにgetだと[]は要らないらしい。
参考:[http://stackoverflow.com/questions/12101658/how-to-get-an-array-in-django-posted-via-ajax] 該当するビューでこんな風に書く
def hoge(request):
arr = request.POST.getlist('arr'[]')
ちなみにgetだと[]は要らないらしい。
参考:How to get an array in Django posted via Ajax - Stack Overflow
intcommaフィルタが効かない(django 1.8)
django 1.4からL10Nを見るので、ロケールjaの場合はNUMBER_GROUPINGを明示しないといけないらしい。
# settings.py
NUMBER_GROUPING = 3
これでおk
参考:やまよし@Google大好き: Django1.4 で intcommaが効かない
リダイレクト時にパラメータを渡す
response = redirect('name', param)
response['Location'] += '?hoge=fuga'
return response
でいける
参考:add request.GET variable using django.shortcuts.redirect - Stack Overflow
テンプレートのforループで何個おきで表示
{% for fuga in hoge %}
{% if forloop.counter0|divisibleby:4%}4個おき{%endif%}
{{fuga.hage}}
{% endfor %}