LoginSignup
4
2

More than 3 years have passed since last update.

Django REST Framework + Vue で選択されたタグに関連する投稿を非同期的に取得し表示する

Last updated at Posted at 2020-04-09

きっかけ

ページ遷移せずに、必要なデータだけを取得し表示する方がよりスマートだと思ったため。
Githubでコードを見る

開発環境

  • Python 3.7.3
  • Django 3.0.5
  • Django REST Framework 3.11.0
  • Vue.js 2.6.11 (CDN)
  • Axios 0.19.2 (CDN)

※Djangoは、Class-Basedで開発していきます。

まずは準備から

1.適当な場所に作った仮想環境に入る。(環境毎にコマンドが変わるので割愛)
2.必要なライブラリのインストール

pip install django
pip install djangorestframework

3.Djangoプロジェクトの作成

# 適当な場所にプロジェクト用ディレクトリを作成
mkdir your_project_name
# 作成したディレクトリに移動し以下のコマンドを打つ
django-admin startproject config .

# 作成されるプロジェクトの中身
└── your_project_name
    │
    ├── config
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    │
    └── manage.py

下記のように打つことで、設定ディレクトリの名前をプロジェクト名と別にすることができます。
django-admin startproject config .

例)普通にdjango-admin startproject your_project_nameと打った場合

# 作成されるプロジェクトの中身
└── your_project_name  # ここと
    │
    ├── your_project_name  # ここが一緒の名前になるのでややこしい
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    │
    └── manage.py

4.Tagsアプリを作成

# manage.py があるディレクトリまで移動
# 移動したら以下のコマンドを打つ
python manage.py startapp tags

# プロジェクトの中身
└── your_project_name
    │
    ├── config
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    │
    ├── tags  # 新しく作成したTagsアプリ
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    │
    └── manage.py

5.Tagsアプリを設定ファイルに登録

config/settings.py
...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'tags.apps.TagsConfig',  # ここにTagsアプリを登録

    'rest_framework',  # 最初にインストールしたdjango-rest-frameworkも一緒に登録
]

...

いざ開発開始(普通のDjangoアプリ開発と変わりません)

1.Tagモデルを作成

tags/models.py
from django.db import models


class Tag(models.Model):
    """タグモデル"""

    name = models.CharField(max_length=10)  # 名前フィールドを追加(最大文字数10文字)

    def __str__(self):  # 管理画面上で各オブジェクトの識別をし易くするための設定
        return self.name  # オブジェクト自身の名前フィールドの値を返すように設定

次にコマンドでマイグレーションファイルを作成し、MigrateでDBに反映

python manage.py makemigrations
python manage.py migrate

最後に、作成したTagモデルを管理サイトに登録

tags/admin.py
from django.contrib import admin

from .models import Tag


admin.site.register(Tag)

2.Viewを追加

tags/views.py
from django.views import generic

from .models import Tag


class IndexView(generic.ListView):  # 一覧表示に特化したViewを作成
    model = Tag
    template_name = 'index.html'

3.Templateの作成

cd tags  # Tagアプリのディレクトリに移動
mkdir templates

# プロジェクトの中身
└── your_project_name
    │
    ├── config
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    │
    ├── tags
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── templates  # 新しくTemplate用のディレクトリを作成
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    │
    └── manage.py
tags/templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Index</title>
</head>
<body>
  {% for tag in tag_list %}
    <p>{{ tag.name }}</p>
  {% endfor %}
</body>
</html>

とりあえず、Tagオブジェクトの名前をFor分で一覧表示するようにしました。
早速、管理画面へログインしてタグを追加してみましょう。

4.管理画面からタグを追加する
管理画面へログインするために、管理者アカウントを作ります。

python manage.py createsuperuser
ユーザー名:
パスワード:

※公式ドキュメントでも、最初にカスタムユーザーモデルを作成するよう強く推奨されていますが、今回は試験的開発なので割愛します。

管理者アカウントを作成したら、コマンドを打ってサーバーを立ち上げます。

python manage.py runserver

サーバーが無事立ち上がったら、http://127.0.0.1:8000/admin から管理画面へアクセスし、タグを追加してみましょう。

5.ルーティンの追加

config/urls.py
from django.contrib import admin
from django.urls import path, include  # includeを追加でインポート


urlpatterns = [
    path('admin/', admin.site.urls),  # 管理サイトへのルーティン

    path('', include('tags.urls')),  # トップページアクセスでTagsアプリに飛ぶように設定
]

次に、Tagsアプリ内にもurls.pyファイルを新規作成

tags/urls.py
from django.urls import path

from . import views  # 同ディレクトリからViews.pyをインポート


app_name = 'tags'  # アプリ識別用の名前を設定(オプション)
urlpatterns = [
    path('', views.IndexView.as_view(), name="index"),  # 先ほど追加したIndexViewを指定
]

これで、http://127.0.0.1:8000 に接続すれば、先ほど管理サイトで追加したタグが一覧表示されているはずです。

いよいよ REST Framework を用いての開発

1.Postsアプリの作成

python manage.py startapp posts

# プロジェクトの中身
└── your_project_name
    │
    ├── config
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    │
    ├── posts  # 新しく作成したPostsアプリ
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    │
    ├── tags
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── templates
    │   │   └── index.html
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── models.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    │
    └── manage.py

新規作成したPostsアプリを設定ファイルに登録

config/settings.py
...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'tags.apps.TagsConfig',
    'posts.apps.PostsConfig'  # ここにPostsアプリを登録

    'rest_framework',
]

...

2.Postモデルを作成

posts/models.py
from django.db import models

from tags.models import Tag  # Tagモデルをインポート


class Post(models.Model):
    """投稿モデル"""

    body = models.TextField(max_length=150)
    tag = models.ForeignKey(Tag, on_delete=models.PROTECT)  # Tagモデルとのリレーション設定

    created_at = models.DateTimeField(auto_now_add=True)  # 作成日時を自動追加するよう設定
    updated_at = models.DateTimeField(auto_now=True)  # 更新されたら日時を更新するよう設定

コマンド打つ

python manage.py makemigrations
python manage.py migrate

管理サイトに登録

posts/admin.py
from django.contrib import admin

from .models import Post


admin.site.register(Post)

3.Serializersを追加

ここから、通常のDjangoアプリ開発とは少し変わってきます。

このSerializersというファイルは、DjangoのモデルオブジェクトをJson形式に変換してくれるものです。

最初から用意されていないので、Postsアプリ内にserializes.pyを新規作成しましょう。

posts/serializers.py
from rest_framework import serializers

from .models import Post


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ('body', )  # Json形式に変換させたいフィールドを追加

4.Viewを追加

posts/views.py
from rest_framework import generics

from .models import Post
from .serializers import PostSerializer  # 先ほど作成したシリアライザをインポート


class PostAPIView(generics.ListAPIView):
    serializer_class = PostSerializer  # シリアライザを指定
    queryset = Post.objects.all()  # Postモデルの全オブジェクトをVue.jsに渡すよう設定

5.ルーティンの追加

config/urls.py
from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),

    path('', include('tags.urls')),
    path('api/', include('posts.urls')),  # api/ アクセス時にPostsアプリに飛ぶように設定
]

次に、Postsアプリ内にもurls.pyファイルを新規作成

posts/urls.py
from django.urls import path

from . import views


urlpatterns = [
    path('', views.PostAPIView.as_view()),  # 先ほど作成したPostAPIViewを指定
]

この時点で、http://127.0.0.1:8000/api に接続すると、Django REST Framework に最初から用意されているテンプレートが、APIから受け取ったJson形式に変換されたPostモデルオブジェクトを一覧表示してくれます。

いざ、題名に書いてある機能を実装しましょう!

1.ルーティンの編集

posts/urls
from django.urls import path

from . import views


urlpatterns = [
    path('<int:tag>/', views.PostAPIView.as_view()),  # TagモデルのIDをパラメータで受け取るように変更
]

2.Viewの編集

posts/views.py
from rest_framework import generics

from .models import Post
from .serializers import PostSerializer


class PostAPIView(generics.ListAPIView):
    serializer_class = PostSerializer

    # queryset = Post.objects.all() を以下に変更
    def get_queryset(self):
        tag = self.kwargs['tag']  # 渡されたパラメータの値をtagに代入
        if tag == 0:  # tagで条件分岐
            # order_by('-created_at') で最新の投稿順に配列に格納されるように設定
            queryset = Post.objects.all().order_by('-created_at')
        else:
            queryset = Post.objects.filter(tag=tag).order_by('-created_at')

        return queryset

3.Templateの編集

tags/templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Index</title>
  <!-- CDNでVue.jsとAxiosをインポート -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>

<div id="app">
  <!-- タグをセレクトタグで表示 -->
  <select v-model="tag">
    <option value="0">全てのポスト</option>
    {% for tag in tag_list %}
      <option value="{{ tag.id }}">{{ tag.name }}に関するポスト</option>
    {% endfor %}
  </select>
  <!-- 選択したタグに関連する投稿を表示 -->
  <div v-if="posts">
    <p v-for="post in posts">[[ post.body ]]</p>
  </div>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      tag: null,
      posts: null,
    },
    watch: {
      // 選択されたタグのIDをパラメータに追加し、APIにリクエスト
      tag: function() {
        if (this.tag) {
          getUrl = 'api/' + this.tag + '/'
          // 返ってきたデータをthis.postsに代入
          axios.get(getUrl).then(res => (this.posts = res.data))
        }
      },
    },
    // Vueデフォルトの "{{ }}" ではDjangoのそれと被ってしまい
    // うまく識別されないので "[[ ]]" に変更
    delimiters: ['[[', ']]'],
  })
</script>

</body>
</html>

完成!

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2