1.この記事について
概要
DjangoのWebアプリケーション開発を勉強しているときに引っ掛かった部分を忘備録的にメモしました。
内容
こちらのチュートリアルに従って勉強しました。
https://eiry.bitbucket.io/
※※
内容が「初級者が~」ということのなので、ある程度Djangoやその他Webアプリになれた方にとっては響かない記事かもしれません。
2.使用したツール・環境
環境
・Windows10
・Python3.8.0
・Webフレームワーク:django2.2.5
・DB:sqlite3.30.1
ツール
・Pycharm
3.トラブルシューティング
(A)__HTMLのinputタグとtextareaタグ内の文字色が白色になってしまう
(1)現象
設定内容
forms.pyに次のようにフォームを指定
from django import forms
from .models import Posting
class PostingForm(forms.ModelForm):
class Meta:
model = Posting
fields = ('name', 'message')
widgets = {
'name': forms.TextInput(attrs={'size': 40, 'required': True}),
'message': forms.Textarea(attrs={'cols': 80, 'rows': 20, 'required': False})
}
views.pyで次のようにindex.htmlにフォーム情報を渡す。
def index(request):
"""表示・投稿を処理する"""
# ModelFormもFormもインスタンスを作るタイミングでの使い方は同じ
form = PostingForm(request.POST or None)
if request.method == 'POST':
# NoneならばFalse
if form.is_valid():
# save()メソッドを呼ぶだけでModelを使ってDBに登録される。
form.save()
# メッセージフレームワークを使い、処理が成功したことをユーザーに通知する
messages.success(request, '投稿を受付しました。')
return redirect('guestboard:index')
else:
# メッセージフレームワークを使い、処理が失敗したことをユーザーに通知する
messages.error(request, '入力内容に誤りがあります。')
page = _get_page(
Posting.objects.order_by('-id'), # 投稿を新しい順に並び替えて取得する
request.GET.get('page'), # GETクエリからページ番号を取得する
count=3
)
contexts = {
'form': form,
'page': page,
}
return render(request, 'guestboard/index.html', contexts)
index.htmlの内容はこのよう。
{{ field.label_tag }}と{{ field }}でそれぞれフォーム上のデータを展開する。
<form action="{% url 'guestboard:index' %}" method="post">
<div class="row">
{% for field in form %}
<div class="form-group">
{% if field.errors %}
<div class="col-sm-10">
<span class="col-sm-10">{{ field.errors }}</span>
</div>
{% endif %}
<div class="col-sm-10 ">
<label class="col-sm-3 control-label ">{{ field.label_tag }}</label>
<label class="label col-sm-7 ">{{ field }}</label>
</div>
</div>
{% endfor %}
<div class="col-sm-10">
<div class="form-group">
<label class="col-sm-2 control-label"><input type="submit" class="btn btn-primary"
value="登録"></label>
{% csrf_token %}
</div>
</div>
</div>
</form>
cssは、bootstrap 3.3.5と自作シートを用いている。
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="{% static 'css/common.css' %}">
トラブル
テストサーバを立ててみる。
すると、次のように入力ボックスの文字が白色になって見えない状態になる。
(2)結論
結論から先に言うと、次の二点。
・bootstrap 3.3.5のデフォルト設定が悪さ(?)をしていた。
・自作CSSの変更後、「スーパーリロード」がひつようだった。
(3)トラブルシューティング
3-1
まずchromeの開発者ツールを開いて調査してみた。
スタイルシート欄を注視。
すると、bootstrapのtextareaに対するデフォルト設定が適用されていることを確認できた。
検証のためにこれをオフにすると文字が出現。
なので、自作のCSSファイルで上書きしてやろうという方針にした。
3-2
Djangoのstaticファイル格納フォルダに次のように定義。
text_change_colorセレクタを定義し、子要素にも適用できるようにした。
.text_change_color {
color: black !important;
}
.text_change_color * {
color: black !important;
}
index.htmlを改変。
<div class="col-sm-10 text_change_color">
<label class="col-sm-3 control-label ">{{ field.label_tag }}</label>
<label class="label col-sm-7 ">{{ field }}</label>
</div>
これでWebアプリを立ち上げたが、
まだ文字が白色のままだった。
3-3
怪しく思って開発者ツールでcssの中身を見てみると、
変更が反映されていない。
アプリをリブートしても、状況は変わらない。
色々調べたところ、キャッシュを初期化のために「スーパーリロード」をしなければいけないらしいとわかった。
参考:https://qiita.com/shati-taro/items/3946d3962071a26ebcb6
スーパーリロードをしたところ、無事反映し、入力ボックスの文字がちゃんと黒色になったことを確認できた。
(B)__auto_now_addで登録したDBの時刻値がTIMEZONEを反映していない
###(1)現象
####設定内容
次のようにPostingモデルを定義。
登録日時の指定にはDateTimeFieldの「auto_now_add」引数をTrueにして
現在のシステム日時が登録されるように設定する。
~~~~
from django.db import models
class Posting(models.Model):
name = models.CharField(
max_length=64,
verbose_name='名前',
help_text='<b>あなたの名前を入力してください</b>',
)
message = models.CharField(
max_length=255,
verbose_name='メッセージ',
help_text='<u>メッセージを入力してください</u>',
)
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name='登録日時',
)
また、setting,pyでTIMEZONEをAsia/Tokyo(日本標準時)に指定。
USE_TZ = TrueとしてTIMEZONEを有効化済み。
~~~~
TIME_ZONE = 'Asia/Tokyo'
USE_I18N = True
USE_L10N = True
USE_TZ = True
indexページで登録ボタンを押下するとDBにモデルの各データがレコードとして登録される。
~~~~
####トラブル
上記の条件で、テストサーバを立て
自分のPC時間で【2020-01-18 17:35ごろ】に
「767675467」とメッセージを打って
画面上の登録ボタンを押す。
~~~~
データが登録されたことを確かめるために、
Pycharm上でデータベースのツールウィンドウを開いて
内容を確認。
~~~~
すると、UTC時刻で登録されてしまっている。
~~~~
~~~~
TIMEZONEが有効化されているはずなのに、なぜ??
(カラム名が謎の文字化けを起こしているのもかなり気になるけど)
~~~~
###(2)結論
結論から言うと、
auto_now_add指定時のモデルデータ生成に関するDjangoの仕様だった。
タイムゾーン云々はDjango 側で勝手にやってくれる。
~~~~
###(3)トラブルシューティング
####3-1
とりあえず画面上のデータもデータベースの登録日時と整合性があっているか確認した。
すると、画面上では日本標準時で表示できている。
~~~~
~~~~
どうやらDateTimeField内のコードを読む必要がありそう。
~~~~
####3-2
DateField(DateTimeFieldの親クラス)のコンストラクタはこのよう。
~~~~
class DateField(DateTimeCheckMixin, Field):
empty_strings_allowed = False
default_error_messages = {
'invalid': _("'%(value)s' value has an invalid date format. It must be "
"in YYYY-MM-DD format."),
'invalid_date': _("'%(value)s' value has the correct format (YYYY-MM-DD) "
"but it is an invalid date."),
}
description = _("Date (without time)")
def __init__(self, verbose_name=None, name=None, auto_now=False,
auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
if auto_now or auto_now_add:
kwargs['editable'] = False
kwargs['blank'] = True
super().__init__(verbose_name, name, **kwargs)
auto_now_addがTrueだとeditableをFalseとしてさらに親クラスに渡すらしい。
ここを調べても有用な情報は手に入らなさそうだったので、
pre_saveメソッド(モデルのsave時に実行されるメソッド)を見てみた。
~~~~
~~~~
def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = timezone.now()
setattr(model_instance, self.attname, value)
return value
else:
return super().pre_save(model_instance, add)
auto_now_addがTrueだったときはtimezone.now()を呼んでいる。
その中身は......
~~~~
~~~~
def now():
"""
Return an aware or naive datetime.datetime, depending on settings.USE_TZ.
"""
if settings.USE_TZ:
# timeit shows that datetime.now(tz=utc) is 24% slower
return datetime.utcnow().replace(tzinfo=utc)
else:
return datetime.now()
settings.pyの指定タイムゾーン利用が有効であろうとなかろうと、
returnする値はUTCのものになっているように見える。
~~~~
ん?
でも画面上では日本標準時が取得できている。
なぜだろう。
~~~~
####3-3
手に負えなかったので調べたら、
次のような記事を見つけた。
~~~~
https://djangobrothers.com/blogs/django_datetime_localtime/
~~~~
>Djangoのdjango.utils.timezoneを利用する際の注意点として、コード内でdatetimeを利用する時にはまだローカライズされていないという点が挙げられます。
少し分かりづらいですが、例えば、テンプレートで表示されるまではUTCを基準にしたawareなdatetimeオブジェクトを扱っており、それが表示されるタイミングで適切なローカルタイムに変換されているということです。
~~~~
この仕様に準拠すると、上記の挙動も理解できる。
~~~~
画面にアウトプットする際にDjango側でTIMEZONEの自動変換をしているようだ。
これがソースコードのどこの部分の処理なのかは以前不明だけど。
公式ドキュメントにUTCでDBに登録と書いてありますね
先にそちらを読めばよかったです
https://docs.djangoproject.com/ja/2.1/topics/i18n/timezones/
5.終わりに
以上、初級者のトラブル解決までの道筋でした。
なにか補足がありましたらコメントください。
あと、なにか個人情報もれてるよってところがあったら、
コソッと教えていただけると助かります。(mailto:1044adad@gmail.com)