nrRouteの日記

勉強において虚無精進をするブログです

Djangoでフォームの入力欄の追加をする方法(メモ)

DjangoのFormsetを使うときにフォームの追加をする方法をまとめておきます。メモなのでだいぶ雑です。

バージョンは3.2.20です。

Djangoのみでやる

こちらのサイトを参考にやっていたのですが、グローバル変数を使っており実用向きではありません。グローバル変数を使わずに書いてみます。

en-junior.com

models.py

from django.db import models

class Group(models.Model):
    name = models.CharField(max_length=100)

class Member(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    email = models.EmailField(max_length=255)

views.py

from django.views import generic
from django import forms
from django.shortcuts import render
from .models import Group, Member

class MemberView(generic.FormView):
    template_name = 'app/member.html'

    def get_success_url(self):
        return reverse_lazy('app:member', kwargs={'pk': self.kwargs['pk']})

    def get_form(self):
        group = Group.objects.get(pk=self.kwargs['pk'])
        MemberFormset = forms.modelformset_factory(
            Member,
            fields=["name", "email",],
            extra=3, max_num=100,
            can_delete=True,
        )
        queryset = Member.objects.filter(group=group)

        if self.request.method == 'POST':
            formset = MemberFormset(**self.get_form_kwargs())
        else:
            formset = MemberFormset(queryset=queryset)
        return formset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context

    def get_form_kwargs(self):
        kwargs = super.get_form_kwargs()
        if self.request.POST and 'add' in self.request.POST:
            kwargs['data'] = self.request.POST.copy()
            kwargs['data']['form-TOTAL_FORMS'] = int(kwargs['data']['form-TOTAL_FORMS']) + 1
        return kwargs

    def post(self, request, *args, **kwargs):
        if 'add' in request.POST:
            context = self.get_context_data(**kwargs)
            return render(request, self.template_name, context)
        return super().post(request, *args, **kwargs)

    def form_valid(self, form):
        if 'submit' in self.request.POST:
            data = form.cleaned_data
            group = Group.objects.get(pk=self.kwargs['pk'])

            for member_parameter in data:
                if member_parameter:
                    try:
                        member = member_parameter['id']
                        member.name = member_parameter['name']
                        member.email = member_parameter['email']
                        member.save()
                    except:
                        Member.objects.create(
                            group=group,
                            name=member_parameter['name'],
                            email=member_parameter['email'],
                        )
        return super().form_valid(form)

テンプレートには {{ form }} で描画できます。

JavaScriptを使ってやる

こちらのサイトを参考にやりました。JavaScriptはこちらのものを一部変更して、更新処理などをできるようにしました。

qiita.com

models.py(①と同じです)

from django.db import models

class Group(models.Model):
    name = models.CharField(max_length=100)

class Member(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    email = models.EmailField(max_length=255)

forms.py

from .models import Group, Member
from django import forms

MemberFormset = forms.inlineformset_factory(
    Group, Member, fields=('name', 'email'),
    extra=0, can_delete=True
)

views.py

from .forms import MemberFormset
from .models import Group, Member
from django.shortcuts import redirect, render, get_object_or_404

def member(request, pk):
    group = get_object_or_404(Group, pk=pk)
    formset = MemberFormset(request.POST or None, instance=group)

    if request.method == 'POST' and formset.is_valid():
        formset.save()
        return redirect('app:member', pk=group.pk)

    context = {
        'formset': formset,
        'group': group,
    }

    return render(request, 'app/member.html', context)

JavaScript

$(function() {

    // フォームの追加をする部分は同じなので省略します

    $('#form').submit(function() {

        const text = $('.text');
        $('[name=form-TOTAL_FORMS]').val(text.length);

        // それぞれの入力欄の__prefix__をindexで置換する
        text.each(function(index, element){
            originalHtml = $(element).html();
            replacedHtml = $(element).html().replace(/__prefix__/g, index);
            // empty_formのみ置換の処理を行う
            if (originalHtml !== replacedHtml) {
                // valueが消えるのですべての項目に対して保存
                value_name = $(element).find("#id_form-__prefix__-name").val();
                value_email = $(element).find("#id_form-__prefix__-email").val();
                is_delete = $(element).find("#id_form-__prefix__-DELETE").prop('checked');
                $(element).html(replacedHtml);
                $(element).find("#id_form-" + index + "-name").val(value_name);
                $(element).find("#id_form-" + index + "-email").val(value_email);
                $(element).find("#id_form-" + index + "-DELETE").prop('checked', is_delete);
            }
        });
    });
});

テンプレートにはすでに入力されている(されていない場合は空)formsetと、empty_formを1つ書いておきます。

ビューの実装はこちらも参考にしました。

blog.narito.ninja

2023/10/26追記: JavaScriptも一部変える必要があったので更新しました。

11月のこんにちは

2023/02/06 11月の記事の下書きが残っていたので公開しておきます。

10月前半

なんやかんやしていたら授業に全然出られませんでした。記憶が全くありません。

10月後半

なんやかんやしていたら授業に全然出られませんでした。記憶があまりありません。

残っている記憶を紹介します。

誕生日でした。ついに20歳になってしまいました。できるだけ濃い20代を過ごしたいものです。

できたばかりのラーメン屋に行きました。見た目のインパクトがすごいです。もはやチャーシューが器になっています(いいえ)。

良かった曲、アルバム等

Spotifyと復縁しました。

 

値段は忘れました。多分900円ぐらい

FPMの2ndを中古CD屋で購入しました。*********recordsのアルバムはだいたいサブスクにないので中古CD屋を渡り歩いて見つけるしかない……わけではないですが、僕はそうしています。

2月のこんにちは

11月の終わりごろまで壊滅的な生活を送っており、1度も授業に出ない週もありました。12月以降は健常者の生活リズムを取り戻そうと努力はしたものの、完全には取り戻せないままテストを迎えて壊滅的な結果を残してしまうことに……来期はどうなるのでしょうか。そもそも希望通りのコースに配属されるかどうかが怪しいです。

音楽

すごくいいです。ミュージックマガジン誌の2022年のベストアルバムにも選出されていました。

だんだん音楽を探すために張っているアンテナが歪になってきました。

よかったもの2022

更新をサボりすぎて年末になっていました。すいません。今年聴いてナイスだと思った曲とアルバム(リリース年は限定しません)を書き残しておきます。順番は適当です。カッコ内はリリース年のつもりですが、間違っているかもしれません。余裕があればサブスクかYouTubeのリンクも載せます。

アルバム
  • tofubeats - lost decade (2013)
  • Cornelius - Mellow Waves (2017)
  • CAPSULE - phony phonic (2003)
  • betcover!! - 時間 (2021)
  • TEMPLIME & 星宮とと - skycave (2022)
  • 大貫妙子 - SUNSHOWER (1977)
  • 佐野元春 - 月と専制君主 (2011)
  • Plus-Tech Squeeze Box - catooom! (2004)
  • Crackazat - Evergreen (2022)
  • Limre - ライマー (2022)
  • DOMi & JD BECK - NOT TiGHT (2022)
  • in the blue shirt - Park with a Pond (2022)
  • 和泉宏隆 & フレンズ - Unforgotten Saga (2022)
  • ラブリーサマーちゃん - THE THIRD SUMMER OF LOVE (2020)
  • FPM - THE FANTASTIC PLASTIC MACHINE (1997)
  • The 1975 - Being Funny In A Foreign Language (2022)
  • SOLEIL - LOLIPOP SIXTEEN (2019)
  • 藤井隆 - Music Restaurant Royal Host (2022)
  • KIRINJI - ペイパードライヴァーズミュージック (1998)
  • RYUTist - (エン)(2022)
  • Louis Cole - Quality Over Opinion (2022)
  • Ink Waruntorn - INK (2022)
  • betcover!! - 卵 (2022)
終わりに

今年もたくさん音楽を聴けました。来年も聴きます。

10月のこんにちは(上)

10月です。授業が始まってしまいました。夏休み後半で生活リズムが完全に崩壊してしまい、出席へのモチベーションが皆無です。実際2コマ出られませんでした。

9月後半 (夏休み終了まで)

引き続き実家でスプラトゥーン3やっていました。全然勝てませんでした。京都に帰ってからは、Webアプリ開発らしきことをしていたりしました。開発は結構楽しいです。あと、響け!ユーフォニアム(1期、2期)とリズと青い鳥を見ました。このシリーズは以前から気になっていたけど見るのが怖くて見ていなかったのですが、ある日突然「今見るしかない!!!」という気持ちになったので見ました。感想としては、本当に見てよかったです。できれば高校入学時ぐらいに見ておきたかったです。また、猫物語 (黒、白)と傾物語を見ました。忍野忍さんが可愛いです。

後期の授業開始日前日には日本橋のe-イヤホンに行きました。いろいろなイヤホンを試聴しましたが、モニターイヤホンのバランスがすごく良い感じでした。今回は何も買いませんでしたが、今度行った時にもう一度試聴し直して良いものがあれば買おうと思います。

履修登録についてですが、抽選科目は9個申し込んで4個当たっていました。

良かった曲、アルバム等

FPMの1stが日本橋の中古CDショップにあったので買いました。ハウスとボサノヴァを感じられて良いです。

1,320円でした

 

アニメを見たので入れました。90年代ポップス成分を摂取できてよいです。

 

こちらもアニメを見たので。苦い記憶が蘇ってきます。アニメを見たことで少しだけ好きになれました。

まとめ

ちゃんと授業に出れるのでしょうか。出られないかもしれません。

9月のこんにちは(下)

遅刻です。もう19日になってしまいました。

9月前半

京都にいました。時間があったので進撃の巨人を勢いに任せて2日で全巻読みました。全然わかりませんでした。もう一回読もうと思います。ただ一気に読んでしまうぐらいに面白い作品でした。そもそも漫画を読むのはかなり久しぶりでしたが、楽しく読めたと思います。

競プロはモチベーションがなさすぎて全然やっていません。逆に開発に対するやる気が結構あったのでそういう感じのことをしていたのですが、これを書いている今はスプラトゥーン3以外のモチベーションが消えています。スプラトゥーン3は全然勝てないですがリリース直後特有の勢いに乗ってやっています。楽しいです。

良かった曲、アルバム等

12日にSpotifyの無料トライアル期間が終わり、それ以降の支払い手段がなかったので解約してしまいました。だからといって音楽に出会う機会が今より少なくなるということはないのでここのコーナーにはさほど影響はない(はず)です。そもそもTwitterとかYouTubeでたまたま出会った曲を何度も聴きたいときとかにサブスクを使うという認識でやっているので…。サブスクはクレジットカードを作ってからもう一度入ろうと思います。

 

響き方がとてもよいです。

 

追悼アルバムです。最後のトラックにご本人が吹奏楽版の宝島の演奏に参加しているのが収録されているのですが、聞いていて不思議な感覚になりました。

 

まとめ

そろそろ履修登録ですね。今期は抽選全落ち回避を願っています。

9月のこんにちは(上)

9月です。夏休み折り返しですね。大学生は気楽です。

8月後半

化物語偽物語を見ました。どのキャラもかわいいですね。歌の歌詞の意味もわかってよかったです。すべての曲を実質神前暁さん一人で作っているのに、全然ワンパターンさがないというか記名性が薄いというかで、職人技だな……という気持ちになりました。下旬には香川に行ってうどんを食べたり現代アートを見たりしました。12年ぶりに直島に上陸してきました。

さて、1回前期の成績ですが、A×4、B×8、D×1、F×1で28単位、GPAは3でした。

計算機科学概論を落としたのは不本意でした。どうしてなのでしょうか。理解ができません。

良かった曲、アルバム等


下のツイートがTwitterのタイムラインに流れてきたので見てみたらハマりました。曲のタイトルがオランダ語なのでオランダ人のトラックメイカーなのでしょうか。ドラムンっぽい感じではありますが、Google翻訳の音声などインターネットの要素も盛り込まれていて良いです。


これもすごくよかったです。穏やかな気持ちになれました。

 


2003~2006年あたりのCAPSULEっぽい雰囲気がありながら、それに加えて少し湿り気もあって良いです。

 


アニメを見たので。サビ前のベースが最高です。神前暁さんの曲は一貫してメロディーに親しみやすさがある気がします。

最近メインストリームの邦楽ロックをもう一度聞きたくなってきましたが、なかなかきっかけになる一曲が無いです。

まとめ

そろそろ京都に戻ろうかなと思っています。