LoginSignup
39
44

More than 3 years have passed since last update.

DjangoとGoogle Maps JavaScript APIを使用してWebアプリを作る

Last updated at Posted at 2019-02-10

やりたいこと

Djangoを勉強中なのでDjango REST frameworkを導入し、Google Maps JavaScript APIを使用してWebアプリを作ってみた。

環境

Windows 10
Python 3.6.5
Django 2.1.4

プロジェクトの作成

コマンドプロンプトでプロジェクトの作成とアプリケーションの作成を行う。

$ django-admin startproject mymapsite
$ cd mymapsite(作成したプロジェクトディレクトリに移動)
$ python manage.py startapp gmap

一度サーバーを立ち上げて確認を行う。

$ python manage.py runserver
Django version 2.1.4, using settings 'mymapsite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

になったら http://127.0.0.1:8000/ をブラウザで開いて確認する。ロケットが出てくればOK。

Modelの作成

Modelとして顧客モデルを作る。顧客モデルは名前、住所、緯度、経度を持つものとする。

gmap/models.py
from django.db import models


class Customer(models.Model):
    name = models.CharField('名前', max_length=20)
    address = models.CharField('住所', max_length=50)
    lat = models.DecimalField('緯度', max_digits=8, decimal_places=6)
    lng = models.DecimalField('経度', max_digits=9, decimal_places=6)

    def __str__(self):
        return str(self.name)
    class Meta:
        verbose_name = '顧客'
        verbose_name_plural = '顧客'

settings.pyにアプリケーションを含める。

mymapsite/settings.py
INSTALLED_APPS = [
    ...
    'gmap.apps.GmapConfig', #追加
]

migrationコマンドを実行。

$ python manage.py makemigrations gmap
Migrations for 'gmap':
  gmap\migrations\0001_initial.py
    - Create model Customer

こうなったらmigrateも実行する。

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, gmap, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying gmap.0001_initial... OK
  Applying sessions.0001_initial... OK

URLconfの設定

Djangoのプロジェクトルートのurls.pyにURL設定をする。

mymapsite/urls.py
from django.contrib import admin
from django.urls import include, path #include追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('gmap/', include('gmap.urls')), #追加
]

gmap/urls.pyの設定

gmapアプリケーションのurls.pyを新規に作成する。

gmap/urls.py
from django.urls import path

from . import views

app_name = 'gmap'
urlpatterns = [
    path('', views.index, name='index'),
]

gmap/views.pyにindexビューを追加する

gmap/views.py
from django.shortcuts import render
from django.http import HttpResponse
from django.template import loader


def index(request):
    template = loader.get_template('gmap/index.html')
    context = {}
    return HttpResponse(template.render(context, request))

gmap/templates/gmap/index.htmlを作成する

地図ページとなるindex.htmlを作成する。gmap/templates, gmap/templates/gmapの必要なディレクトリは作成する。
(index.htmlはGoogle Maps JavaScript APIのページから借りてきたものを少し修正した物です。YOUR_API_KEYの部分は書き換えてください。)

2019/10/22 追記
JavaScriptのforループに誤りがあったため修正しました。

gmap/templates/gmap/index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Simple Map</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta charset="utf-8">
    <style>
      /* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
      #map {
        height: 100%;
      }
      /* Optional: Makes the sample page fill the window. */
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script>
      var map;

      /**
       * The CenterControl adds a control to the map that recenters the map on
       * Chicago.
       * This constructor takes the control DIV as an argument.
       * @constructor
       */
      function CenterControl(controlDiv, map) {

        // Set CSS for the control border.
        var controlUI = document.createElement('div');
        controlUI.style.backgroundColor = '#fff';
        controlUI.style.border = '2px solid #fff';
        controlUI.style.borderRadius = '3px';
        controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
        controlUI.style.cursor = 'pointer';
        controlUI.style.marginBottom = '22px';
        controlUI.style.textAlign = 'center';
        controlUI.title = 'Click to recenter the map';
        controlDiv.appendChild(controlUI);

        // Set CSS for the control interior.
        var controlText = document.createElement('div');
        controlText.style.color = 'rgb(25,25,25)';
        controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
        controlText.style.fontSize = '16px';
        controlText.style.lineHeight = '38px';
        controlText.style.paddingLeft = '5px';
        controlText.style.paddingRight = '5px';
        controlText.innerHTML = 'Customer Search';
        controlUI.appendChild(controlText);

        controlUI.addEventListener('click', function() {

          var xhttp = new XMLHttpRequest();
          xhttp.onreadystatechange = function() {
               if (this.readyState == 4 && this.status == 200) {
                 setMarker(JSON.parse(this.responseText));
               }
          };
          xhttp.open("GET", "api/customer/?format=json", true);
          xhttp.setRequestHeader("Content-type", "application/json");
          xhttp.send();

        });

      }

      function initMap() {
        map = new google.maps.Map(document.getElementById('map'), {
          center: {lat: 35.681236, lng: 139.767125},
          zoom: 8
        });

        var centerControlDiv = document.createElement('div');
        var centerControl = new CenterControl(centerControlDiv, map);

        centerControlDiv.index = 1;
        map.controls[google.maps.ControlPosition.TOP_CENTER].push(centerControlDiv);

      }

      function setMarker(customerJson) {

        for(var i = 0; i < customerJson.length; i++){

          var infowindow = new google.maps.InfoWindow({
            content: customerJson[i].name + ' ' + customerJson[i].address
          });

          var marker = new google.maps.Marker({
            position: {lat: Number(customerJson[i].lat), lng: Number(customerJson[i].lng)},
            map: map
          });

          bindInfoWindow(marker, map, infowindow);
        }
      }

      function bindInfoWindow(marker, map, infowindow) {
        marker.addListener('click', function() {
          infowindow.open(map, this);
        });
      }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
    async defer></script>
  </body>
</html>

動作確認

ここまでの状態で一度動作確認しておく。python manage.py runserverして、http://127.0.0.1:8000/gmap/ にアクセスする。(画面上部のボタンは後で使用します。現在は動作しません。)
動作確認1.png

Django REST frameworkのインストールと使用

index.htmlでjsonデータを受け取りたいのでDjango REST frameworkを使用する。

公式ページ
https://www.django-rest-framework.org/

インストール

コマンドプロンプトでインストールする。
pip install djangorestframework
pip install markdown
pip install django-filter

settings.pyに追記

settings.pyのINSTALLED_APPSに追記する。

mymapsite/settings.py
INSTALLED_APPS = (
    ...
    'rest_framework', #追記
)

また、どこでも良いのでsettings.pyにREST_FRAMEWORKを追記する。

mymapsite/settings.py
REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ]
}

また、このあと使用するログイン後のリダイレクトを設定する。

mymapsite/settings.py
LOGIN_REDIRECT_URL = '/gmap/api/' #追記

URLconfの修正

プロジェクトルートのURLconfにRESTの設定など追記する。(丸ごと載せます)

mymapsite/urls.py
from django.contrib import admin
from gmap.models import Customer
from django.urls import path
from django.conf.urls import url, include
from rest_framework import routers, serializers, viewsets

class CustomerSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Customer
        fields = ('name', 'address', 'lat', 'lng')

class CustomerViewSet(viewsets.ModelViewSet):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer

router = routers.DefaultRouter()
router.register(r'customer', CustomerViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('gmap/', include('gmap.urls')),
    path('accounts/', include('django.contrib.auth.urls')),
    path('gmap/api/', include(router.urls)),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

superuserの作成

ログインしてモデル登録するためcreatesuperuserコマンドでユーザを作成する。(Usernameはadminにしました)

$ python manage.py createsuperuser
Username (leave blank to use 'あなたの名前'): admin
Email address: あなたのメールアドレス
Password:
Password (again):
Superuser created successfully.

ログインする

http://127.0.0.1:8000/api-auth/login/ にアクセスしてcreatesuperuserで作成したユーザでログインする。
動作確認2.png

データの登録

http://127.0.0.1:8000/gmap/api/customer/ にアクセスしてデータの登録を行う。画面右下のPOSTで登録する。(緯度経度は適当に入手してください)
動作確認3.png

最後の動作確認

再度http://127.0.0.1:8000/gmap/ を開いて動作を確認する。画面上部の"Customer Search"をクリックして、ピンが表示される。ピンをクリックするとInfoWindowが表示される。
動作確認4.png

ここまで見ていただきありがとうございました。
以上です。

39
44
2

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
39
44