見出し画像

DjangoとVue.jsでシンプルなTodoアプリを作る 07.VueでTodo作成機能を実装する

前回の記事ではVueでタスクを取得する方法について書きました。

続いて、タスクを作成する機能を Vue で作っていこうと思います。
一覧を取得するのとほとんど同じ流れで、下記の流れでコードを書いていきます。

  1. html→Vue に task のタイトルを送れるようにする

  2. Vue で task を作成する createTask メソッドを定義

  3. html→Vue で送られてきた新しい task の内容を django の views.py に送る

  4. 送られてきた内容を元に、task を作成し、db に保存する。

  5. 保存できたら、その結果(レスポンス)を Vue に返す。

  6. html に新しい task が表示されていれば ok!

1.html から vue に task のタイトルを送れるように、準備します。
先程コメントアウトした、タスク追加フォームのコメントアウトを外し、以下のように修正してください。

task/templates/task_list.html

{% extends 'base.html' %}
{% block content %}
<div id="app">
  {# 追加 #}
  <form @submit.prevent="createTask">
    <div class="form-group row">
        <div class="col-9">
            {# 追加 #}
            <input type="text" class="form-control" v-model="task.title" placeholder="卵を買う">
        </div>
        <div class="col-3">
            <button type="submit" class="btn btn-primary mt-auto">タスクを追加</button>
        </div>
    </div>
  </form>

@submit.prevent="createTask"とは、form 内で submit したときに、createTask メソッドを呼び出すという意味です。
今回の場合は、タスクを追加ボタンを押すと、createTask メソッドが実行されます。

v-model="task.title"とは、vue の main.js でこれから定義する、data 変数の task.title に入力した値を代入するという意味です。

例えば、html のテキストフォームにタイトルを「あ」と入力すると、vue の data 変数の task.title の値が html で入力した「あ」という文字に置き換わります。

2.Vue で task を作成する createTask メソッドを getTasks()のあとに定義します。
data()の中に先程 html で、v-model="task.title"と書いた、task.title を保存する変数も定義します。

const App = {
    data() {
        return {
            // 追加
            task: {title: ''},
            tasks: [],
        }
    },
    compilerOptions: {
        delimiters: ['[[', ']]'],
    },
    methods: {
        getTasks(){
            fetch(URL, {
                method: 'get',
            //略
        },
        // 追加
        createTask(){
            // csrfトークンを定義
            const csrftoken = Cookies.get('csrftoken');
            // taskリストを取得する
            this.getTasks();
            fetch(URL, {
                method: 'post',
                headers: {
                    'Content-Type':  'application/json',
                    'X-CSRFToken': csrftoken,
                },
                // htmlから入力されたtaskの情報をviews.pyに送信
                body:JSON.stringify(this.task),
            })
            .then((response) => {
                return response.json();
            })
            .then((task) => {
                console.log(task)
                // タイトルを空にする
                this.task.title = ''
                this.getTasks();
            })
            .catch(error => {
                console.error('There has been a problem with your fetch operation:', error);
            });
        },
    },
    created() {
        this.getTasks();
    },

書いたコードの説明をします。

はじめに、getTask メソッドと違い、createTask メソッドは、データを django の views.py に送る際に、csrf トークンを付与しなければいけません。

csrf_token とは、CSRF(クロスサイトリクエストフォージェリ)を検証する仕組みで、データを変更するときには、必ず記述する必要があります。

html からデータを送信する際は、django のテンプレートタグである、{% csrf_token %}を記述すれば大丈夫でした。
ですが、今回は Vue からデータを送信する必要があります。その場合はどうすればいいでしょうか。

Django のドキュメントをみると、ajax を使用した際に、どうやって、csrf トークンを付与すればいいかが書いてあります。
今回は、下記ドキュメントにかいてある、JavaScript Cookie library を使用して、csrf トークンを付与します。

設定は簡単で、base.html に、JavaScript Cookie library の cdn を追加します。

       </div>
      </div>
    </div>
    <script src="https://unpkg.com/vue@next"></script>
    {# 追加 #}
    <script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.0/dist/js.cookie.min.js"></script>
    <script src="{% static 'main.js' %}"></script>
  </body>
</html>

main.js にconst csrftoken = Cookies.get('csrftoken');と定義し、
下記のように headers に追加すれば問題ありません。

createTask(){
    const csrftoken = Cookies.get('csrftoken');
    this.getTasks();
    fetch(URL, {
        method: 'post',
        headers: {
            'Content-Type':  'application/json',
            'X-CSRFToken': csrftoken,
        },

続いて、fetch する内容について、解説します。
html から vue に新たに作りたい task のタイトルを受け渡したいと思います。
その場合は、新たに body を定義し、そこに html で送信したデータを定義します。
JSON.stringify とは、json のオブジェクトを json 文字列に変換し、vue から django に送信できる形に変更するメソッドです。

            fetch(URL, {
                method: 'post',
                headers: {
                    'Content-Type':  'application/json',
                    'X-CSRFToken': csrftoken,
                },
                // htmlから入力されたtaskの情報をviews.pyに送信
                body:JSON.stringify(this.task),
            })

送信したあと、Vue の data にあるtask: {title: ''}を空にし、task のリストを再取得する処理を入れてください。

            .then((response) => {
                return response.json();
            })
            .then((task) => {
                console.log(task)
                // タイトルを空にする
                this.task.title = ''
                this.getTasks();
            })

これをしないと、html に何を入力しても前送信した task の title が残って、同じ task を追加し続けてしまいます。

また、リストを再取得しないと、task を追加したはずなのに、反映されないということが起こるので注意してください。

vue の方のコードが書き終わったので、django の views.py も書いていきます。
post メソッドを下記の様に書き直してください。

    def post(self, request):
        # json文字列を辞書型にし、pythonで扱えるようにする。
        task = json.loads(request.body)
        form = TaskForm(task)

        # データが正しければ保存する。
        if form.is_valid():
            new_task = form.save()
            return JsonResponse({"task": model_to_dict(new_task)}, status=200)
        return redirect("task_list_url")

はじめに、vue から送信した json データを python で扱えるように辞書型に変更します。
json.loads というメソッドを使うと変更することが可能です。
次に、html→django にデータを送信したときと同じように、TaskForm を使用して、task を保存します。
保存できたら、保存したデータを vue に返します。

ここで出てきた model_to_dict は、下記のように、django の model のデータを辞書型に変更してくれるメソッドです。

{'id': 8, 'title': 'e', 'completed': False}

こうすることで、django→vue にデータを送信することができます。
完了したら、タスクを追加からタスクを追加できるか確認しましょう。

追加できたら今回の機能は完成です。お疲れさまでした!

今回の記事ではVueでTodo作成機能を実装しました。次回の記事では、VueでTodoデータを更新できるようにします!

おまけ記事

⏩webサービスの作り方が気になったら


⏩Djanogを学ぶメリットについてはこちら

この記事が気に入ったらサポートをしてみませんか?