見出し画像

DjangoとVue.jsでシンプルなTodoアプリを作る 06.VueでTodoデータを取得する

前回の記事では、django と HTML で、task を作成し、作成したものを一覧で表示することができるようになりました。

ですが、まだ 1 クリックで task を完了にさせたり、削除したりする機能はできていません。

ここからは、django と HTML のみで実装するのではなく、Vue.js を使用して、task の一覧を表示、作成、更新、削除を画面の遷移なしでできるようにしたいと思います。

static/main.js を作成し、Vue を使う準備をするために、下記を追加してください。

static/main.js

const App = {
    data() {
        return {
            tasks: [],
        }
    },
    compilerOptions: {
        delimiters: ['[[', ']]'],
    },
    methods: {
    },
    created() {
    },
}

Vue.createApp(App).mount('#app')

const App で定義された data は、今回のアプリで扱う task のデータを保存する箱のようなものです。

compilerOptions delimiters は、Vue で保存したデータを html に表示する時に使用する形式を指定するためのものです。

通常、Vue は{{}}を使って、データを表示するのですが
今回 django で{{}}を使ってしまっているので、Vue のデータは[[]]を使って表示します。

methods は、Vue で使用するメソッドを書く部分です。
Django の views.py で書いていたメソッドを変わりにここに記述していきます。

created()には Vue のオブジェクトが起動したときに実行される処理を書きます。
今回の例でいうと、task を削除したり更新した時に、必ず task の一覧を更新する必要があります。
そのため、created()の中に、task の一覧を更新する処理を書く必要があります。

最後の Vue.createApp(App).mount('#app')で、html の id=app の要素に、
この Vue のオブジェクトが作られるようになります。

具体的にどのように Vue を使って HTML にデータを表示するかイメージそ作るために、
試しに data に文字を入れてみます。

static/main.js

const App = {
    data() {
        return {
            tasks: ['test'],
        }
    },
    compilerOptions: {
        delimiters: ['[[', ']]'],
    },
    methods: {
    },
    created() {
    },
}

Vue.createApp(App).mount('#app')

Vue を有効にするために、div 要素に id="app"を追加してください。
また、[[tasks]]を記述してください。

task/templates/task_list.html

{% extends 'base.html' %}
{% block content %}
{# 追加 #}
<div id="app">
  [[ tasks ]]
  <form method="POST">
    {% csrf_token %}
    <div class="form-group row">
        <div class="col-9">
            {{ form.title }}
        </div>
        <div class="col-3">
            <button type="submit" class="btn btn-primary mt-auto">タスクを追加</button>
        </div>
    </div>
  </form>

  <div class="mt-5">
    <div class="card mb-2" v-for="task, index in tasks">

写真のように test が表示されたと思います。

このように、<div id="app">で囲まれた中に、Vue で定義した data を[[]]でくくって記述すると、値が表示されることが確認できました。

なんとなく Vue の使い方がわかったところで、全 task を表示するために、Vue HTML Django を使って、task 一覧を取得するコードを書いていきたいと思います。

流れはこんな感じです。

  1. Vue で task 一覧を取得する getTasks メソッドを定義

  2. django の views.py にリクエストを送る

  3. レスポンスを Vue に返す

  4. html で結果を表示します。

まず、1 の、Vue で task の一覧を受け取る、getTasks メソッドを定義していきます。

static/main.js

    methods: {
        getTasks(){
            fetch(URL, {
                method: 'get',
                headers: {
                    'Content-Type':  'application/json',
                },
            })
            .then((response) => {
                return response.json();
            })
            .then((tasks_list) => {
                this.tasks = tasks_list;
            })
            .catch(error => {
                console.error('There has been a problem with your fetch operation:', error);
            });
        },
    },
    created() {
        this.getTasks();
    },

書いたコードについて説明します。
今回は、Fetch API の fetch()メソッド使って、Django の views.py から task の情報を持ってきています。

Fetch API とは?

JavaScript を使って、インターネット上でデータを取得できる機能です。

基本の構文はこのような形で、

fetch(url)
    .then(response => response.json)
    .then(data => console.log(data));

一行目に取得したい情報を提供している url を書き、
二行目に返ってきたレスポンスを 何形式で受け取るか指定し、
三行目で、レスポンスのデータの処理方法を定義します。

例えば、クジラ web api というものを使って、簡単に情報を取得してみます。

下記をコンソールに書き、実行すると

fetch('https://api.aoikujira.com/tenki/week.php?fmt=json&city=319')
  .then(response => response.json())
  .then(data => console.log(data));

一週間の天気の情報が、クジラ web api から取得できます!

0: {date: '22日(土)', forecast: '晴後曇', mintemp: '10', maxtemp: '10', poptimes: '-/-/-', …}
1: {date: '23日(日)', forecast: '晴時々曇', mintemp: '10', maxtemp: '10', poptimes: '-/-/-', …}
2: {date: '24日(月)', forecast: '晴時々曇', mintemp: '2', maxtemp: '11', poptimes: '-/-/-', …}
3: {date: '25日(火)', forecast: '曇', mintemp: '2', maxtemp: '8', poptimes: '-/-/-', …}
4: {date: '26日(水)', forecast: '曇', mintemp: '3', maxtemp: '11', poptimes: '-/-/-', …}
5: {date: '27日(木)', forecast: '晴時々曇', mintemp: '3', maxtemp: '11', poptimes: '-/-/-', …}
6: {date: '28日(金)', forecast: '晴時々曇', mintemp: '2', maxtemp: '9', poptimes: '-/-/-', …}
7: {date: '29日(土)', forecast: '晴時々曇', mintemp: '1', maxtemp: '8', popti


このように、Fetch api の構文に沿って、コードを書くとデータを取得することができます。


脱線してしまいましたが、Django の views.py からデータを取得するにはどうすればいいでしょうか。
まず、取得元の url ですが、html 上に、script タグを定義し、そこから URL を指定するようにします。

task/templates/task_list.html

  </div>
</div>\{# 追加 #}
<script>
  const URL = '{% url "task_list" %}';
</script>
{% endblock %}
    methods: {
        getTasks(){
            fetch(URL, {
                method: 'get',
                headers: {
                    'Content-Type':  'application/json',
                },
            })
            .then((response) => {
                return response.json();
            })
            .then((tasks_list) => {
                this.tasks = tasks_list;
            })
            .catch(error => {
                console.error('There has been a problem with your fetch operation:', error);
            });
        },
    },
    created() {
        this.getTasks();
    },

先程のクジラ web api ではしていしませんでしたが、後々必要になるので、method と、headers も指定します。
まず method ですが、今回は一覧を取得するので get と指定します。

続いて headers に'Content-Type': 'application/json'と指定します。
views.py の get メソッドは、html 形式で情報を返す場合と、今回の json 形式で返す場合があります。
views.py でそれぞれの場合を切り分けたいので、このように Content-Type を指定します。

続いて、2 の django の views.py にリクエストを送る処理をしていきます。
views.py を下記のように書き換えてください。

class TaskView(View):
    def get(self, request):
        # リクエストがjson形式のとき
        if request.headers.get("Content-Type") == "application/json":
            # すべてのtaskを辞書型で受け取る
            tasks = Task.objects.values()
            tasks_list = list(tasks)
            # json形式でレスポンスを返す
            return JsonResponse(tasks_list, safe=False, status=200)
        return render(request, "task_list.html")

コメントで書いたように、リクエストが json 形式の場合、すべての task を辞書型にした値をリストにし、それを json 形式でリクエストもとに返す処理をしています。
実際に js に返すデータは下記のような感じです。

[{'id': 4, 'title': 'ついか', 'created_at': datetime.date(2022, 1, 8), 'completed': False}, {'id': 3, 'title': 'a', 'created_at': datetime.date(2021, 12, 30), 'completed': False}, {'id': 1, 'title': '卵を買う', 'created_at': datetime.date(2021, 12, 29), 'completed': False}, {'id': 2, 'title': '新年の目標を立てる', 'created_at': datetime.date(2021, 12, 29), 'completed': False}]

最後に vue で受け取ったデータを html で表示していきます。
下記のように、html を書き換えてください。

{% extends 'base.html' %}
{% block content %}
<div id="app">
  {# 一旦コメントアウト #}
  {% comment %} <form method="POST">
    {% csrf_token %}
    <div class="form-group row">
        <div class="col-9">
            {{ form.title }}
        </div>
        <div class="col-3">
            <button type="submit" class="btn btn-primary mt-auto">タスクを追加</button>
        </div>
    </div>
  </form> {% endcomment %}

  <div class="mt-5">
    {# 追加 #}
    <div class="card mb-2" v-for="task, index in tasks">
      <div class="card-body">
        <div class="form-check">
          <input class="form-check-input" type="checkbox" />
          <label class="form-check-label">
          {# 追加 #}
            [[ task.title ]]
          </label>
          <button type="button" class="close float-right">
            <span>&times;</span>
          </button>
        </div>
      </div>
    </div>
  </div>
</div>
<script>
  const URL = '{% url "task_list" %}';
</script>
{% endblock %}

div 要素に、v-for を追加します。
v-for は、Vue でいう django の for 文と似たような役割で、複数ある task を順番に並べてくれます。

task.title で、task のタイトルを表示します。
写真のように、一覧が表示されれば ok です!

今回の記事では、VueでTodoデータを取得する方法を紹介しました。
次回の記事ではVueでTodo作成機能を実装する方法を紹介します。

おまけ記事

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


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


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