開発していた予定定期報知アプリが完成したので、djangoとpythonanywhereで躓いた細々としたことのまとめ


開発していたアプリが完成したので、djangoで躓いた細々としたことをまとめていきます。詳しい理由はわからないことがほとんどなので、質問にはおそらく答えられないことが多いです。参考にしたurlをそれぞれつけておくのでそちらをご覧ください。

記念日やイベントなどの予定を事前に何度もメールで報せるwebアプリです。
予定表との違いは、報せる間隔、期間に制限が無いため、一月の範囲に収まらない、普段は意識しないで埋もれてしまうような長期的な予定や周期的な予定に特にむいています。通知だと見逃すことが多かったので作りました。メールアドレスを登録するだけで始められて、料金は無料です。

たとえば記念日の50日前、30日前、15日前、5日前を毎年、のように設定できます。
個人的には果物や魚を旬の時期にあまり食べられなかったりすることがあるので、旬の終わりを予定日に、それから何日前を旬の始まりに設定して使うつもりです。あとは園芸の植え付けや肥料の管理にも使う予定です。
ほかにも、行ったことのない場所の定休日や営業日を毎週で登録し、その前日にメールを送るようにすると行くきっかけになります。
このような予定を何回でも繰り返し続けられます。

また、メールは同日の予定は一本にまとめて送ります。そして、デフォルトのurlを設定でき、天気予報などの予定を実行する際には頻繁に見るサイトなども登録できます。ほかにも、日付やデフォルトのタイトルや詳細を登録することで、予定の入力時にページの文章から抽出することも可能です。

もしこういう使い方もしたいというご意見があればお待ちしています
ただ、いまはサーバーなどの無料の範囲内で行っているため、容量やメール数に限りがあります。なので対応は人気が出てからになる場合があります。

例えば画像ファイルは大きいので現在では登録できません。かわりにdropboxなどの共有リンクか、その端末だけになりますが、画像ファイルのパスを登録すればブラウザから確認できるでしょう。

よろしくお願いします。

以下からdjangoで躓いた細々としたことのまとめです。

開発はローカルでやる人が多いでしょうが、そのときlocal_setting.pyを作るとデバッグに便利です。ただし、セキュリティ関連の設定はlocal_setting.pyで明確に打ち消さないと面倒なことが起こります。たとえばhttpsを強制する設定があるのですが、それをローカルで適用してしまうとアクセスできなくなり、キャッシュなどを消す必要が出てきます。
https://qiita.com/jp_ibis/items/0ac1ba8aba1398f2a492
もしくは以下のように完全に分けるのもいいでしょう。
https://qiita.com/okoppe8/items/e60d35f55188c0ab9ecc

axesとかrulesも使う場合

login(request, user)

axesとかrulesも使う場合
login(request, user,backend='django.contrib.auth.backends.ModelBackend')

このときloginにはbackend='django.contrib.auth.backends.ModelBackend'を渡さないと、
multiple authentication backends configured and therefore must provide the backend argument or set the backend attribute on the user
が発生します。AUTHENTICATION_BACKENDSに問題が無いときはこれを確認してみてください。

formsetにkwargsをわたす

#古いやり方
modelformset_factory(FirmPartner, form=FirmPartnerForm, can_delete=True, extra=1,
                     form_kwargs={'request': request, 'application': application})

#2020年
ConceptFormSet = modelformset_factory(
    model = Concept,
    form = ConceptForm,
    fields = show_fields
)
#分けて書く
formset = ConceptFormSet(form_kwargs={'action': 'detail'})

書き方に新旧があるようです。現在は下の書き方を使います。

json_scriptフィルタ

view
def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context.update({
            "update":"update"
        })

html
{{ update |json_script:"update-data" }}

document.getElementById('update-data').innerText=='"update"'
true

document.getElementById('update-data').innerText=="update"
false

viewからjson_scriptで値を渡すとき、上のようにしないと正誤判定ができませんでした。
真偽値は"true"でだけで大丈夫です。

raise ValidationError

こういうのを追加するとき、object has no attribute 'object'が起こるときがあります。
もしそれがget_context_dataでなら、以下が必要です。


def post(self, request, *args, **kwargs):
        #ValidationErrorのため
        self.object = self.get_object()

idやpkを伴うリンク

<a class="box" href="{% url 'app:form_detail' form.id %}">detail</a>    
<a class="box" href="{% url 'app:form_detail' form.id.value %}">detail</a>
<a class="box" href="{% url 'app:form_detail' 1 %}">idが1のdetail</a>

上のような感じで書いてあるページは多いのですが、value をつけないと、NoReverseMatch がおきる場合があります。一度idをadminでしらべて、上の三番目のように直接form.idの位置に書いても動きはするので、エラーの原因が分からなかったら試してみたり、アドレスバーに直接入力してみるのもいいかもしれません。

updateviewでのidやpk

app_form = AppForm(request.POST)
if app_form.is_valid():
    form = app_form.save(commit=False)
    form.pk = self.kwargs['pk']

こういう書き方をするときに最後のを忘れると新規作成になってしまう。

inputをtextareaに

metaに追加する。keyは変更したいフィールド名。ただしレイアウト的にはcolsは指定しないか、widthを使ったほうがいいです。

from django.forms import ModelForm, Textarea

class PostModelForm(ModelForm):
    class Meta:
        model = Post
        widgets = {
            'content': Textarea(attrs={'cols': 80, 'rows': 20}),
        }

datetime.datetimeとdatetime.date

date=datetime.datetime.strptime("2000-02-02", '%Y-%m-%d')
#これで作られるのはdatetime.datetime datetime.dateがほしいときは
date=datetime.datetime.strptime("2000-02-02", '%Y-%m-%d').date()

この二つは一見同じように見えるときがありますが別物なので、開発中日付がどうしても==にならないときはtyoe()などで確認してみてください。

cssでtransformとanimationを併用するとき

@keyframes slideIn {
  0%, 100% {
    transform: translate(10px) rotate(0deg);
    color: red;
  }
  25% {
    transform: translate(125px) rotate(360deg);
    color: green;
  }
}

transformを併用するときはkeyframes にまとめて書きます。

pythonanywhereのScheduled Task

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    def handle(self, *args, **options):
        command...

どんなbashの命令でも実行できそうですが、djangoを利用する場合はpython manage.py Command_file_name にしたほうが無難です。そうでないと例えばDJANGO_SETTINGS_MODULEかsettings.configure()してほしいというエラーがでます。
https://qiita.com/checkpoint/items/b6947501774b4008e077
https://help.pythonanywhere.com/pages/environment-variables-for-web-apps/
https://stackoverflow.com/questions/7598793/setting-django-settings-module-under-virtualenv
また、仮想環境を使用している場合は

ファイルパスに

/home/myusername/myproject/mytask.py

こうではなく

/home/myusername/.virtualenvs/myvenv/bin/python /home/myusername/myproject/mytask.py

と入力する。

pythonanywhereのtaskでmodelを使う場合

modelにはapp名のlabelをつける必要があります。ないと
doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

class TopicsTr(models.Model):

   class Meta:
      abstract = True  # specify this model as an Abstract Model    
      app_label = 'blogs'