Welcome 

Point🛎️

Point Memo


Calendar整形

Calendarは以前にPHPやLaravel Inertia Vue3でも作成したが、DjangoのTemplateではどうやって整形するのか少し悩んだ。
1週間のBlockを作るには、
v-for="n in 7"
とかでLoopさせてDivタグで整形すれば済む話だけど、DjangoのTemplateではシンプルなFor Nextが使えない。
7回のLoopをさせるには、Views.pyで7個の要素を持つListを作成しContextでTemplateのHTMLに渡し、そのリスト内をLoopさせる。
(他にも方法はあるのかもしれないけど)
同様に1月分のBlockを作成するには、対象月の日数分の要素を持つListをViews.pyで作成し、それをTemplateに渡す。
また、その月の1日が何曜日かでカレンダーの開始位置が変わる。なのでその曜日までのブランクのBlockを作る必要がある。
(作りやすいという理由で月曜開始のスタイルにした。JRA方式🏇)
ブランクの個数のListもViews.pyで曜日を調べて作成しTemplateに渡す。
最終日のBlockであることを容易に確認するために、Listとは別にIntegerとして日数もTemplateに渡しておくと楽。
(Blockの罫線は基本的に左と下にのみ設定する。(重複すると濃くなったりするため)日曜日と最後のBlockは右も罫線を設定するから)
HTMLレベルだけど、カレンダーの各BLock内で縦位置が真ん中になったり高さの制御が難しかった。
align-items: flex-start; display: flex; を指定したdivで囲む必要がある。
カレンダーの各BLock内で改行されない。
white-space: normal; overflow-wrap: break-word; flex-wrap: wrap;を指定したdivで囲む必要がある。
というようなことで、意外と手間取った。 😮‍💨
レスポンシブなスマホ対応。一応 😞

Calendar登録

予定の登録先はシンプルにSQLiteを使用。
Modelにフィールド追加
     
  author = models.ForeignKey(
        settings.AUTH_USER_MODEL, 
        on_delete=models.CASCADE, 
        verbose_name='Author'
    )
     
  
登録はCreateViewを用いて簡単に行うが、投稿者はLoginUserとするので、auth_userとリレーションしたテーブルを対象にする。
そのため、登録時に、authorフィールドにLoginUserIDを登録出来る様に、form_validメソッドをオーバーライドする。
     
class TodoCreateView(LoginRequiredMixin,CreateView):
    model = Todo
    template_name = 'todo/todo_create.html'
    form_class=TodoForm
    success_url=reverse_lazy('todo:todolist')

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)
    
  

List表示

外部テーブルにあるauthorの名前を表示するには、なんの苦労もなく{{d.author.username}}で良かった。
( dはViewから渡されたオブジェクト )

Calendar表示

登録した予定は予定日のカレンダーにタイトルを表示する。
やり方がわからずヘンテコながらなんとかできたメモ。
views.pyでカレンダーを表示する年月でデータを抽出し、辞書としてTemplateに渡す。
    
  # 年月でフィルター(予定日順)
  results = tmodel.objects.filter(
      scddate__year=int(str_y),
      scddate__month=int(str_m)
  ).order_by('scddate')

  # 日付をキーとする辞書を作成する。
  # 日付順なので、日付が変わるとキーを変える。svd
  svd=0

  # 日付をKeyとしてタイトルのリストをValueとする辞書
  dicd={}

  # IDをKeyタイトルをValueとする辞書
  dicscd={}

  # データのない年月は対象にしない(any() は要素が一つでも真なら True、すべての要素が偽であるときFalse)
  if any(results):
      svd=0
      for result in results:
          if svd != result.scddate.day:

              if svd > 0 :
                  # 日付が変わって初回でなければリストを辞書に吐き出す
                  dicd[svd]=lstd

              # 日付Keyの更新
              svd=result.scddate.day

              # 日付Keyが変わったのでリストの初期化
              lstd=[]

              # リストにタイトルを追加
              lstd.append(str(result.id))
          else:

              # 日付keyが変わらない場合はそのままリストに追加
              lstd.append(str(result.id))

          #Loop毎にID、タイトルの辞書に追加
          dicscd[result.id]=result.title
          
      #Loop終了時にもリストを辞書に吐き出す
      dicd[svd]=lstd
      
  
Templateで辞書を読むためにフィルターを実装する。
    
@register.filter(name='getdic')
def get_item(dictionary, key):
  return dictionary.get(key,"")

@register.filter(name='getdic_int')
def get_item(dictionary, key):
  return dictionary.get(int(key),"")
  
  
Templateで、カレンダーをレンダリングするときに日付をキーとしてタイトルIDのリストを取得。
タイトルIDのリストをLoopしてIDをキーに順次タイトルを取得し、IDを利用して予定詳細へリンクを貼る。
    
{% if dicd|getdic:d != '' %}
  {% for x in dicd|getdic:d %}
    <div><a href="{% url 'todo:tododetail' x %}">{{dicscd|getdic_int:x}}</a></div>
  {% endfor %}
{% endif %}
  
  
Responsive
CSSで画面が狭い時の対応をするだけなんだけど、カレンダーはそれだと複雑になってしまう。
なので、PCでは表示するけどスマホでは表示しないBlockとその逆のBlockを用意して、その中でそれぞれ全く別に整形。
スマホ版はカレンダー形式ではなく、日付順にBlockが縦に並ぶようにするだけなので簡単。
しかしながら、個別に曜日を用意していないので曜日を表示できない。
やむなく、月の開始曜日の番号(月曜日=1)と日付を加えたものから1を引いたものを7で除した余りから曜日を取得するフィルターを用意した。

@register.filter(name='get_week')
def get_item4(value,arg):
    r=(value + arg -1) % 7
    match r:
        case 1:
            wname="月"
        case 2:
            wname="火"
        case 3:
            wname="水"
        case 4:
            wname="木"
        case 5:
            wname="金"
        case 6:
            wname="土"
        case 7:
            wname="日"
        case _:
            wname="日"
    return wname
    
  

HTMLソース

DjangoのTemplateでは {%if %} や {%for %}などの箇所が改行されて空行だらけになってしまう。
共通枠のbase.htmlのBodyを {% spaceless %} {% endspaceless %}で囲むと全ページの改行が省略された。
… 省略されすぎて 何もわからなくなった。😑