きっかけ
ページ遷移せずに、必要なデータだけを取得し表示する方がよりスマートだと思ったため。
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アプリを設定ファイルに登録
...
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モデルを作成
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モデルを管理サイトに登録
from django.contrib import admin
from .models import Tag
admin.site.register(Tag)
2.Viewを追加
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
<!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.ルーティンの追加
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ファイルを新規作成
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アプリを設定ファイルに登録
...
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モデルを作成
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
管理サイトに登録
from django.contrib import admin
from .models import Post
admin.site.register(Post)
3.Serializersを追加
ここから、通常のDjangoアプリ開発とは少し変わってきます。
このSerializersというファイルは、DjangoのモデルオブジェクトをJson形式に変換してくれるものです。
最初から用意されていないので、Postsアプリ内にserializes.pyを新規作成しましょう。
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('body', ) # Json形式に変換させたいフィールドを追加
4.Viewを追加
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.ルーティンの追加
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ファイルを新規作成
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.ルーティンの編集
from django.urls import path
from . import views
urlpatterns = [
path('<int:tag>/', views.PostAPIView.as_view()), # TagモデルのIDをパラメータで受け取るように変更
]
2.Viewの編集
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の編集
<!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>