目標
選択された都道府県に属する市区町村を選択候補とするプルダウンメニューの作成
開発環境
- 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.必要なライブラリのインストール
pip install django
pip install djangorestframework
2.プロジェクト作成
# プロジェクト用ディレクトリ作成
mkdir your_project_name
cd your_project_name
# プロジェクト作成
django-admin startproject config .
# 作成されるプロジェクトの中身
└── your_project_name
│
├── config
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
│
└── manage.py
3.言語&タイムゾーン設定変更
config/settings.py
...
LANGUAGE_CODE = 'ja' # 英語から日本語に変更
TIME_ZONE = 'Asia/Tokyo' # タイムゾーンを東京に変更
USE_I18N = True
USE_L10N = True
USE_TZ = True
...
今回は試験的開発のため、データベースはデフォルトのSQLiteのままでいきます。
さっそく開発開始(バック処理)
1.アプリを作成
python manage.py startapp addresses
config/settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'addresses.apps.AddressesConfig', # 作成したアプリを登録
'rest_framework', # 最初にインストールしたdjango-rest-frameworkも登録
]
...
2.モデルを作成
addresses/models.py
from django.db import models
class Pref(models.Model):
"""都道府県モデル"""
name = models.CharField(max_length=5)
def __str__(self):
return self.name
class City(models.Model):
"""市区町村モデル"""
pref = models.ForeignKey(Pref, on_delete=models.PROTECT)
name = models.CharField(max_length=15)
def __str__(self):
pref_name = self.pref.name
city_name = self.name
return pref_name + city_name
コマンドでDBに変更を反映
python manage.py makemigratios
python manage.py migrate
作成したモデルを管理サイトに登録
addresses/admin.py
from django.contrib import admin
from .models import Pref, City
admin.site.register(Pref)
admin.site.register(City)
3.シリアライザを作成
このシリアライザファイルが、モデルオブジェクトをJson形式に変換してくれます。
addresses/serializers.py
from rest_framework import serializers
from .models import City
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
fields = ('name', ) # Json形式に変換してほしいフィールドを追加
4.Viewを追加
addresses/views.py
from django.views import generic
from rest_framework import generics
from .models import Pref, City
from .serializers import CitySerializer
class PrefListView(generic.ListView): # 通常のListViewを指定
model = Pref
template_name = 'index.html'
class CityAPIView(generics.ListAPIView): # rest-frameworkのListViewを指定
serializer_class = CitySerializer
# パラメータから受け取った都道府県オブジェクトに属する
# 市区町村オブジェクトを返すよう設定
def get_queryset(self):
pref = self.kwargs['pk']
queryset = City.objects.filter(pref=pref)
return queryset
5.ルーティンを追加
config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# トップページアクセスでAddressesアプリに飛ぶよう設定
path('', include('addresses.urls')),
]
Addressesアプリにもurls.pyを作成
addresses/urls.py
from django.urls import path
from .views import PrefListView, CityAPIView
urlpatterns = [
# トップページアクセスをPrefListViewに指定
path('', PrefListView.as_view()),
# 選択された都道府県オブジェクトのパラメータを受け取るAPIルートを追加
path('city/<int:pk>/', CityAPIView.as_view()),
]
開発(フロント処理)
1.都道府県プルダウンメニューを追加
addresses/templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body>
<select>
<!-- for文で、都道府県オブジェクトをプルダウン表示 -->
{% for pref in pref_list %}
<option value="{{ pref.pk }}">{{ pref.name }}</option>
{% endfor %}
</select>
</body>
</html>
2.都道府県プルダウンメニューをVueと連携
addresses/templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Index</title>
<!-- CDNでVue.jsをインポート -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- v-modelで、選択された都道府県オブジェクトのpk値をprefに代入 -->
<select v-model="pref">
{% for pref in pref_list %}
<option value="{{ pref.pk }}">{{ pref.name }}</option>
{% endfor %}
</select>
</div>
<script>
new Vue({
el: '#app',
data: {
pref: null,
},
})
</script>
</body>
</html>
3.選択された都道府県に属する市区町村オブジェクトをAxiosで取得
addresses/templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Index</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- CDNでAxiosをインポート -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<select v-model="pref">
{% for pref in pref_list %}
<option value="{{ pref.pk }}">{{ pref.name }}</option>
{% endfor %}
</select>
</div>
<script>
new Vue({
el: '#app',
data: {
pref: null,
cityList: null,
},
watch: {
pref: function() {
// prefに値が代入された場合
if (this.pref) {
getUrl = 'city/' + this.pref + '/'
// それに属する市区町村オブジェクトを取得しcityListに代入
axios.get(getUrl).then(res => (this.cityList = res.data))
}
},
},
})
</script>
</body>
</html>
4.市区町村プルダウンメニューを追加
addresses/templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Index</title>
<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="pref">
{% for pref in pref_list %}
<option value="{{ pref.pk }}">{{ pref.name }}</option>
{% endfor %}
</select>
<!-- 都道府県が選択された場合のみ選択可能になるよう設定 -->
<select :disabled="!pref">
<!-- v-forで取得した市区町村オブジェクトをプルダウン表示 -->
<option v-for="city in cityList">[[ city.name ]]</option>
</select>
</div>
<script>
new Vue({
el: '#app',
data: {
pref: null,
cityList: null,
},
watch: {
pref: function() {
if (this.pref) {
getUrl = 'city/' + this.pref + '/'
axios.get(getUrl).then(res => (this.cityList = res.data))
}
},
},
// Vueデフォルトの "{{ }}" だと、Djangoのそれと被ってしまい
// うまく表示されないため "[[ ]]" に変更
delimiters: ['[[', ']]'],
})
</script>
</body>
</html>
実際に動かしてみる
1.管理者ユーザーを作成
python manage.py createsuperuser
ユーザー名:
メールアドレス:
Password:
Password (again):
Superuser created successfully.
2.管理サイトで都道府県&市区町村オブジェクトを新規作成
まずサーバーを起動
python manage.py runserver
無事起動したら http://127.0.0.1:8000/admin から管理サイトへログインし、オブジェクトを新規作成する。
完成!
トップページ http://127.0.0.1:8000 にアクセスすれば結果が確認できます。