読者です 読者をやめる 読者になる 読者になる

GIG

赴くままに技術を。

SwaggerでWeb APIの設計

Swagger

Web APIの設計

Web APIの設計でExcelを使って定義書を作成していたが、仕様の変更等によって気がついたら設計書とシステムに乖離しているなんてことがあった。そのようなことがないようにWeb APIの定義情報を常に正とするようなアプリにしたい。

そこでSwaggerは、いくつかの企業によるコンソーシアムによって、Web APIの標準化を行うための規約とそのツール群を使う。

Swagger

Swaggerのサイトに行くとSwagger-editorやSwagger-UIなどツールがいくつかあるけど、swagger-nodeとbootprint-openapiで一通りできる。

GitHub - swagger-api/swagger-node: Swagger module for node.js

$ npm install -g swagger

GitHub - bootprint/bootprint-openapi: Bootprint-module to render OpenAPI specifications

$ npm install -g bootprint
$ npm install -g bootprint-openapi
$ npm -g install html-inline

手順としては、以下。

Swagger-editorでWeb API定義ファイルを作成する

はじめにプロジェクトのディレクトリを作る。 その際にNodeJSのwebフレームワークを選択させられる。これは後で作るモックサーバをカスタマイズするときに改造していくものである(Flaskが対応していたら便利だけど)。 expressを選択する。

$ swagger project create example

次に生成されたディレクトリに降りて、エディタを起動する。

$ cd example/
$ swagger project edit

Webブラウザが起動し、エディタが表示される。 GUIに対応できない環境(x windowを起動していないサーバなど)で利用するときは、-s | --silentを付ける。

今回はSwagger Editorのpetstores_simple.yamlを使う。

ドキュメント化する

bootprint-openapiでSwaggerファイルをHTML化する。

$ pwd
example
$ bootprint openapi api/swagger/swagger.yaml target

するとtargetディレクトリ配下にhtml, css, javascriptが書き出され、適当なwebサーバに配置することで閲覧できる。 さらにcss, javascriptをHTMLにまとめたいときは下記のようにし、dist.htmlを公開すれば良い。

$ html-inline target/index.html > target/dist.html

モックサーバを立てる

モックサーバはサーバ機能をまだ実装していない段階で、クライアント側の仕様検討、実装を待ちにすることなく行う目的で建てる。

$ pwd
example
$ swagger project start -m

クライアント側(同一端末だが)でAPIを叩く。

$ curl -i http://localhost:10010/pets
HTTP/1.1 404 Not Found
X-Powered-By: Express
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
Content-Length: 17
Date: Sat, 24 Sep 2016 01:17:18 GMT
Connection: keep-alive

Cannot GET /pets

おや? 同じ事象がRunning in mock modeチュートリアル例でも起きる。

調べてみると、下記のissueに遭遇。x-swagger-router-controllerを指定しないとルーティングしてくれないみたい。 これを付けるのは、pathかoperationの階層で定義すれば良いとこと。

github.com

pathに含まれるoperationで全て同じとする。

..(省略)..

  /pets:
    x-swagger-router-controller: pets

..(省略)..

  /pets/{id}:
    x-swagger-router-controller: pets

..(省略)..

改めてクライアント側で叩く。 そのまま使うと決められた値(numberなら1, stringなら"Sample text")が返ってくる。 レスポンスをカスタマイズしたい場合は、Implementing mock mode controllersを参照。

$ curl -X GET -i http://localhost:10010/api/pets
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Sat, 24 Sep 2016 13:08:50 GMT
Connection: keep-alive
Content-Length: 51

[{"id":1,"name":"Sample text","tag":"Sample text"}]
$ cat pet.json 
{"id":1,"name":"foo","tag":"dog"}
$ curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" -i http://localhost:10010/api/pets -d @pet.json
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Sat, 24 Sep 2016 13:29:25 GMT
Connection: keep-alive
Content-Length: 49

{"id":1,"name":"Sample text","tag":"Sample text"}
$ curl -X GET -i http://localhost:10010/api/pets/1
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Sat, 24 Sep 2016 13:36:02 GMT
Connection: keep-alive
Content-Length: 49

{"id":1,"name":"Sample text","tag":"Sample text"}
$ curl -X DELETE -i http://localhost:10010/api/pets/1
HTTP/1.1 500 Internal Server Error
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Sat, 24 Sep 2016 13:37:25 GMT
Connection: keep-alive
Content-Length: 196

{"message":"Response validation failed: void does not allow a value","code":"INVALID_TYPE","failedValidation":true,"path":["paths","/pets/{id}","delete","responses","204"],"originalResponse":"{}"}

最後だけこけた。どうやらレスポンスが空は許可されていないみたい。 対象療法だけど、レスポンスを修正した。

    delete:
      description: deletes a single pet based on the ID supplied
      operationId: deletePet
      parameters:
        - name: id
          in: path
          description: ID of pet to delete
          required: true
          type: integer
          format: int64
      responses:
        '200':
          description: pet deleted
          schema:
            $ref: '#/definitions/pet'
        default:
          description: unexpected error
          schema:
            $ref: '#/definitions/errorModel'

どうにか。

$ curl -X DELETE -i http://localhost:10010/api/pets/1
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Sat, 24 Sep 2016 13:51:11 GMT
Connection: keep-alive
Content-Length: 49

{"id":1,"name":"Sample text","tag":"Sample text"}

サーバ機能の雛形を生成する

最後にAPI仕様書からサーバ機能の雛形を作る

swagger-nodeの方ではないSwagger-Editorを用いると、サーバ機能のダウンロードができる(swagger-codegenでも良い)。 インストールしても良いが、Dockerイメージがあるので、コンテナにて行う。

$ docker pull swaggerapi/swagger-editor
$ docker run -p 80:8080 swaggerapi/swagger-editor

あとはhttp://(YOUR_CONTAINER_URI)にアクセスし、API仕様書の内容をコピー&ペーストして、[Generate Server]からお好みのフレームワークの雛形をダウンロードする。これからFlaskでサーバ機能を作るので、Flaskを選択する。

NvidiaのDeep Learning Quest(無料分)を受ける

DeepLearning

すでにひとしきり広まった感があるDeep Learning。 Nvidiaがかなり本腰入れていて、それ向けの教育プログラムまである。

f:id:hermesian:20160504130503p:plain

どういうものかは知っておきたいので、無料枠で受けれる「ディープラーニング入門」を受講してみよう。実際AWS上に構築された環境を触りながらできるハンズオン形式で、有料のプログラムもAWSの利用料金ぐらいの値段みたい(とNvidia Deep Learning Day 2016 Springで紹介されていた)。

私たちは、あなたがあなたのアプリケーションは、研究者や開発者として必要な最高のどのフレームワークスーツを決める手助けを目的とした深い学習> のための最も一般的なソフトウェアフレームワークを見学します。この見学では、あなたのアプリケーションに一番適したディープラーニングフレーム> ワークを決定する事をゴールにします。ディープラーニングの予備知識は必要ありません。

たしかにDLフレームワークは乱立している印象がある。Software links « Deep Learningを見ると38種もあるのか。

起動に4分程度かかるとのこと。Jupyter Notebook形式なのね。 始まるとなんとタイマーが起動して、55分の間だけ利用できるとのこと。

f:id:hermesian:20160504130510p:plain

各DLフレームワークの採用基準も記載があった。 NVDLDでも下記のような比較がされてたけど、TheanoとTorch7の比較(性能面、機能面)があまり理解できていない。 これはもう使ってみないとわからない世界なのかも。 Lua言語の習得コスト考えるとTheanoとかPythonサポートしているものになりそうな印象。

GPUリソースをどうやって調達しようかな...。

Djangoアプリケーションのデプロイ

Python Django Python Django

開発サーバでなく、製品版ではどうするかというと2通りの方法があるみたい。

  1. Apache Httpサーバ + mod_wsgi
  2. Nginx + Gunicorn

2.の方がパフォーマンスが優れているという話も見かけたけど、今回は1.を試してみる。 wsgiは"ウィスギィ"と読むのか。

検証環境として、Virtualbox上に立てたCentOS 7を使う。

 cat /etc/centos-release
CentOS Linux release 7.2.1511 (Core)

webブラウザから確認するため、VagrantfileにIPアドレスを固定するよう設定する。

  • Vagrantfile
...
  config.vm.network "private_network", ip: "192.168.33.10"
...
Python 3.5.1のインストール

デフォルトでインストールされているPythonのバージョンが2.7.5なので、3.5.1をインストールする。

# mkdir tmp
# cd tmp
# wget https://www.python.org/ftp/python/3.5.1/Python-3.5.1.tgz
# tar -xzf Python-3.5.1.tgz
# cd Python-3.5.1
# ./configure --enable-shared
# make
# make altinstall

インストールの確認をしたが、関連するライブラリのパスが通っていない...。 パスを通す。

# /usr/bin/python3.5 -V/usr/bin/python3.5: error while loading shared libraries: libpython3.5m.so.1.0: cannot open shared object file: No such file or directory
# echo "/usr/local/lib/python3.5" > /etc/ld.so.conf.d/python33.conf
# echo "/usr/local/lib" >> /etc/ld.so.conf.d/python33.conf
# ldconfig
Apache Httpサーバ およびmod_wsgiのインストール

tmpディレクトリのままでApache Httpサーバとmod_wsgiをインストールする。

# yum install httpd
# wget https://github.com/GrahamDumpleton/mod_wsgi/archive/4.5.2.tar.gz
# tar -xzf 4.5.2.tar.gz
# cd mod_wsgi-4.5.2/
# ./configure --with-python=/usr/local/bin/python3.5
# make
# make install
Djangoのインストール

pipのバージョンが古いかったので、アップグレードも実施。

# pip3.5 freeze
You are using pip version 7.1.2, however version 8.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
# /usr/local/bin/pip3.5 install --upgrade pip
# /usr/local/bin/pip3.5 install django
確認用プロジェクトで確認する

helloプロジェクトを作成。

# su - vagrant
$ cd ~
$ mkdir -p dev/python/django
$ cd dev/python/django/
$ django-admin startproject hello

マイグレーション

$ /usr/local/bin/python3.5 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, sessions, contenttypes
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... 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 sessions.0001_initial... OK

管理者アカウントを作っておいて、管理画面の表示も確認しよう。

$ /usr/local/bin/python3.5 manage.py createsuperuser
Username (leave blank to use 'vagrant'): admin
Email address: admin@email.com
Password:
Password (again):
Superuser created successfully.

前回はstaticフォルダにJavascriptやらCSSを入れて参照していたけど、管理画面のものはどこにあるのかなと思ったらそれをstaticフォルダに集めれとのこと。これをしないとあとで管理画面のスタイルが崩れて表示される。

collectstaticコマンド を実行するとSTATIC_ROOTで指定したディレクトリに格納される。

  • hello/settings.py
...
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
...

それでもって

$ /usr/local/bin/python3.5 manage.py collectstatic
mod_wsgiの設定
oadModule wsgi_module modules/mod_wsgi.so

WSGIDaemonProcess hello python-path=/home/vagrant/dev/python/django/hello
WSGIProcessGroup hello
WSGIScriptAlias / /home/vagrant/dev/python/django/hello/hello/wsgi.py process-group=hello

<Directory /home/vagrant/dev/python/django/hello/hello>
    <Files wsgi.py>
        Require all granted
    </Files>
</Directory>

Alias /static /home/vagrant/dev/python/django/hello/static
<Directory /home/vagrant/dev/python/django/hello/static>
    Require all granted
</Directory>

apacheユーザがhelloプロジェクトにアクセスできるようにする。

$ sudo usermod -a -G vagrant apache
$ sudo chmod 775 -R ~/dev/python/django/hello/
Apache Httpサーバの起動

あとはApache Httpサーバを起動して、http://192.168.33.10/http://192.168.33.10/adminで開発用サーバと同じ画面が見れるはず。

# systemctl start httpd
# systemctl enable httpd

ただ1点気になったのが、Apache Httpサーバのエラーログに/はないと言われるのはなんでだろう。。

[wsgi:error] [pid 4116] Not Found: /

Django1.9+Bootstrap3でログイン表示を作る

Python Django Python Django Django

認証・認可は、Djangoに限らず、Webフレームワークを使い出してまず外せない機能。

Django公式マニュアルを見てみると、機能としてデフォルトで持っていて、それを用途に応じて拡張していく方針とのこと。 Users, Groupモデル、パスワードをハッシュ化して保持して保持するといった提供されている。

1回やり方を確認しておけば何にでも応用できそうなのででBootstrap3を利用し、ログイン画面を表示してみる。

環境

環境としては、こちら。

項目 バージョン
Python 3.5.1
Django 1.9.5
jQuery v2.2.2
Bootstrap v3.3.6

Djangoの認証

Djangoで認証・認可の機能は、django.contrib.authで使用できる。これはプロジェクトを生成するときに元から含まれている。プロジェクト作成後、マイグレーション(python manage.py migrate)とすると認証・認可用のテーブルが作成される。

つくってみる

はじめにプロジェクトから作成する。 radishはただのプロジェクトの例なので、適宜変更してください。

$ django-admin startproject radish
$ python manage.py migrate

次にaccountsdashboardアプリを作成する。 accountsには認証・認可機能を含めで、dashboardには認証された後のリダイレクト先のアプリを含める想定。

$ python manage.py startapp accounts
$ python manage.py startapp dashboard

アプリを作ったら、忘れずsettings.pyINSTALLED_APPSに追記しておく。これを忘れると、後で出てくるテンンプレートが存在しないというエラーが発生するので、忘れずに。

  • radish/radish/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # local
    'accounts.apps.AccountsConfig',
    'dashboard.apps.DashboardConfig',
]
Bootstrapを配置する

JavascriptCSSのライブラリは、radish/staticフォルダ内に入れておく。するとsettings.pyの下記の記載に沿って、参照可能となる。

  • radish/radish/settings.py
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/

STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

jQueryやらBootstrapやらをインストールする。それぞれダウンロードするのが面倒であったので、Bowerで入れる。

  • radish/static/
$ bower init
...
(対話型でbower.jsonを作成)
...
$ bower install jquery -S
$ bower install bootstrap -S
トップ画面、ログイン画面、ログイン後画面(ダッシュボード画面)を作る

各画面共通で使うNavbarなどはbase.htmlに記述し、他の画面はそれを継承する。またそういう共通のHTMLはradish/templates配下に入れておく。しかし、このパスはDjangoが見つけてくれないので、またsettigs.pyに追記する。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],  # 追記箇所
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

それではbase.html{% load staticfiles %}と書くと、下のように{% static xxx%}でjsやcssを利用できる。{% if user.is_authenticated %}~{% else %}で条件分岐しているところは、認証されている場合のみ有効にするメニュー表示である。また {% block container %}~{% endblock %}は、base.htmlを継承した方のHTMLが埋め込まれる箇所となっている。

{% load staticfiles %}

<!DOCTYPE html>
  <html lang="ja">
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">

      <tile>{% block title %} Radish | {{ site_name }}{% endblock %}</tile>

      <!-- CSS -->
      <link href="{% static 'bootstrap/dist/css/bootstrap.min.css' %}" rel="stylesheet" />
      <link href="{% static 'bootstrap/dist/css/bootstrap-theme.min.css' %}" rel="stylesheet" />
      <link href="{% static 'font-awesome/css/font-awesome.css' %}" rel="stylesheet" />
  </head>
  <body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
          <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
              <span class="sr-only">Toggle navigation</span>
              <span class="icon-bar"></span>
              <sapn class="icon-bar"></sapn>
              <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Radish</a>
          </div><!-- /.navbar-header -->

          <div id="navbar" class="navbar-collapse collapse">
            {% if user.is_authenticated %}
              <ul class="nav navbar-nav navbar-right">
                  <li>
                      <a href="#">Dashboard</a>
                  </li>
                  <li>
                      <a href="#">Settings</a>
                  </li>
              </ul>
            {% else %}
              <form class="navbar-form navbar-right">
                  <button type="button" class="btn btn-primary"
                    onclick="location.href=&quot;{% url 'login' %}&quot;;">
                    Login</button>
              </form>
            {% endif %}
          </div><!-- /.navbar-collapse -->
        </div><!-- /.container -->
    </nav>

    {% block container %}
    {% endblock %}

    <hr/>
    <footer>
      <p>&copy; hermesian</p>
    </footer>

    <!-- JavaScript -->
    <script src="{% static 'jquery/dist/jquery.min.js' %}"></script>
    <script src="{% static 'bootstrap/dist/js/bootstrap.min.js' %}"></script>
  </body>
</html>

次にトップ画面home.htmlは下記。 これはBootstrapのjumbotronの例をそのまま利用。

{% extends 'base.html' %}

{% load staticfiles %}

{% block container %}
<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
    <div class="container">
      <h1>Radish</h1>
      <p>
          "Radish is a dashboard for collecting visualizing report."
      </p>
    </div>
</div>
<div class="container">
      <!-- Example row of columns -->
      <div class="row">
        <div class="col-md-4">
          <h2>Heading</h2>
          <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
          <p><a class="btn btn-default" href="#" role="button">View details »</a></p>
        </div>
        <div class="col-md-4">
          <h2>Heading</h2>
          <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
          <p><a class="btn btn-default" href="#" role="button">View details »</a></p>
       </div>
        <div class="col-md-4">
          <h2>Heading</h2>
          <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
          <p><a class="btn btn-default" href="#" role="button">View details »</a></p>
        </div>
      </div>
  </div>
{% endblock %}

home.htmlのログインボタンで遷移する先は、accounts/tempalte/accounts/login.html<form>タグの下に{% csrf_token %}とあるのは、DjangoCSRF対策機能である。

{% extends 'base.html' %}

{% load staticfiles %}

{% block container %}
<div class="container">
  <div class="page-header">
      <h3>
        Login
      </h3>
  </div>
  <div class="row">
    <div class="col-md-4 col-md-offset-4">
        <div class="panel panel-default">
            <div class="panel-body">
                <form accept-charset="utf-8" role="form" action="{% url 'login' %}" method="post">
                    {% csrf_token %}
                    <fieldset>
                        <div class="form-group">
                            <input type="text" id="username" name="username" class="form-control"
                                   placeholder="Username" required autofocus>
                        </div>
                        <div class="form-group">
                            <input type="password" id="password" name="password" class="form-control"
                                   placeholder="Password" required>
                        </div>
                        {% if login_failed %}
                          <p class="text-danger">Sorry, that login was invalid.  Please try again.</p>
                        {% endif %}
                        <input class="btn btn-lg btn-success btn-block" type="submit" value="Log in">
                    </fieldset>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}

最後に認証後のdashboard画面dashboard/template/dashboard.html。といっても今回は空っぽ。

{% extends 'base.html' %}
ビューとルーティングを作る

accountsアプリ

  • radish/accounts/views.py

何かしらで認証されっぱなしの状態にならないように、logoutを最初に呼んで初期化してる。

import logging
from django.shortcuts import render_to_response, HttpResponseRedirect
from django.template import RequestContext
from django.contrib.auth import authenticate, login, logout

logger = logging.getLogger(__name__)


def login_view(request):

    #強制的にログアウト
    logout(request)
    username = password = ''

    login_failed = False

    if request.POST:
        username = request.POST['username'].replace(' ', '').lower()
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user is not None:
            if user.is_active:
                login(request, user)
                return HttpResponseRedirect('/dashboard')
        else:
            login_failed = True

    return render_to_response('accounts/login.html',
                              {'login_failed': login_failed},
                              context_instance=RequestContext(request))

上で作ったビューをwebブラウザから/accounts/loginでアクセスできるよう設定する。

  • radish/accounts/urls.py
from django.conf.urls import url
from .views import login_view

urlpatterns = [
    url(r'^login/$', login_view, name='login'),
]

dashboardアプリ

  • radish/dashboard/views.py

@login_requiredをつけて、認証後のみアクセス可能なようにしている。

import logging

from django.shortcuts import render
from django.contrib.auth.decorators import login_required


@login_required
def index(request):
    return render(request, 'dashboard/index.html')
  • radish/dashboard/urls.py
from django.conf.urls import url
from .views import index

urlpatterns = [
    url(r'^$', index, name='dashboard'),
]
動作確認

ユーザを作成する。今回はスーパーユーザを造り、そのままそのアカウントでログインしてみる。

$ python manage.py createsuperuser
...
(対話的に作成)
...

動作確認として、開発サーバを起動$ python manage.py runserver、webブラウザからhttp://localhost:8000/にアクセスして、先ほど登録したスーパーユーザでログインし、NavbarにDashboard, Settingsというメニューが出ていることを確認する。

Djangoのフロー

Python Django

DjangoフルスタックなWebフレームワークで、Ruby on RailsのようなDBマイグレーションができたり、管理GUIを簡単に作る機能があるなど、使えたら便利そう。

1) プロジェクトを作成する

$ django-admin startproject (プロジェクト名)

2) アプリケーションを作成する

$ python manage.py startpapp (アプリケーション名)

3) ビューを作成する

3)-1. (アプリケーション名)/views.pyを改修する
3)-2. (アプリケーション名)/urls.pyを改修する
3)-3. テンプレート(*.html)を作成する (js, cssは(プロジェクト名)/staticディレクトリ配下に入れる)

*テンプレート単体で確認できないのかな?

4) モデルを作成する

4)-1. (プロジェクト名)/(プロジェクト名)/settings.pyでデータベースを設定する
4)-2. (アプリケーション名)/models.pyを改修する
4)-3. (プロジェクト名)/(プロジェクト名)/settings.pyのINSTALLED_APPSを編集し、モデルを有効にする
4)-4. マイグレーションを実施する

$ python manage.py makemigrations (アプリケーション名)
$ python manage.py sqlmigrate (アプリケーション名) 0001  <= データベーススキーマを確認
$ python manage.py migrate

*ER図->DB--(リバース)-->モデル--(モデル修正)-->モデル'--(マイグレーション x n回)-->DB'の方がいきなりコードを書けないので、馴染める。

JacksonでLombokを使ったBeanにマッピングできない

Java

同じ轍を踏まないようにメモ。

事象

JSONファイルから設定を読み込むことをしたく、Jacksonを使い、アクセッサーメソッドの記述を省力化しようとLombokを使った。そのとき下記のように記載するとアクセッサーメソッドがスネークケースになって(実際スネークケースになる設定というわけではなく、頭文字を大文字にして繋げる仕様で、"_"だからそれに対応できなかっただけのような気がする気がする...)、アンマーシャルすることができないエラーが発生する。

f:id:hermesian:20160207222514p:plain

import lombok.Getter;
import lombok.Setter;

public class Bean {

    @Getter
    @Setter
    private Parent1 _parent1;

    public class Parent1 {

        @Getter
        @Setter
        private String _child1;

        @Getter
        @Setter
        private String _child2;

        @Getter
        @Setter
        private String _child3;
    }
}
対策

属性の接頭辞には、"_"を使用しないこと。

f:id:hermesian:20160207222521p:plain

import lombok.Getter;
import lombok.Setter;

public class Bean {

    @Getter
    @Setter
    private Parent1 parent1;

    public class Parent1 {

        @Getter
        @Setter
        private String child1;

        @Getter
        @Setter
        private String child2;

        @Getter
        @Setter
        private String child3;
    }
}
バージョン

pom.xmlの内容は下記。

...以上、省略....
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.6</version>
        </dependency>
...以下、省略....

初段になりました

初めて審査なるものを受けました。 あいにくの雨。そして傘を忘れた...

審査は、術科試験と学科試験の2種。 ウェイトは不明だが、まれに学科で落ちることがあるとのことなので、術科にウェイトがあるような印象。

術科試験

一手座射が内容です。体配(執弓の姿勢、歩き方、坐しての回り方)が何よりも重要。 明らかに練習不足...。揖してからのモタつき、極め付けは射位を行き過ぎてしまいました。

学科試験

今年から学科の問題が公表されました(http://osaka-kyudo.jp/mondai_chiren_rengou.pdf)。 「A群、B群からそれぞれ1問、計2問を出題する。」を読んでいなかったため、「45分でこの量を書くのか...」と思い、キーワードを抑えることに集中していました。 終わった後、周りの方はほぼ15行びっしり記載されており、短すぎたかと焦りました(緊張すると震えてスラスラ書けない...)が、結果的には通ったようです。次は小論文などある程度の文量は書くトレーニングをし、そして硬筆の練習して臨みたいところ。