似非プログラマのうんちく

「似非プログラマの覚え書き」出張版

巳年じゃないけど Python やろうぜ(その 21)

PostgreSQL の利用

Django はデフォルトで SQLite3 を利用しますが、他のデータベースを活用することもできます。今回はその中から PostgreSQL を利用してみようと思います。

PostgreSQL のインストールと設定

Windows の場合は GUI インストーラもあって、比較的楽にインストール・設定ができると思います。

Linux についてはいろいろと設定を変更しないといけないっぽくて、とりあえず Fedora に関するものについては Qiita にまとめました。
Fedora に PostgreSQL をインストールして使えるようにするまで - Qiita

Django から利用するためのデータベースを作成しておきます。Linux の場合は postgres というユーザーが作成されているはずですので、以下のコマンドを実行します。

su - postgres
createdb django

Django の設定

PostgreSQLDjango から利用する場合、あらかじめ psycopg2 をインストールする必要があります。

pip install psycopg2

settings.py の中のデータベースに関する部分を以下のように書き換えます。

# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'django',
        'USER': 'postgres',
        'PASSWORD': '(snip)',
        'HOST': 'localhost',
        'PORT': 5432,
    }
}

マイグレーションと初期データの投入を再実行します。

./manage.py migrate
./manage.py loaddata initial_data

管理者ユーザーの情報もデータベースに格納されていますので、切り替えに伴って再度作成しておく必要があります。

django-admin createsuperuser

次回以降、WSGI による Apache HTTPD サーバとの連携についてお話をして、今回のシリーズはひとまず終了としたいと思います。

Memcached をインストールする

Python シリーズ番外編で Memcached のインストール方法を紹介します。

Windows の場合

こちらが参考になります。

Windows で Memcached を使う - make world

memcached-win64-1.4.4-14.zip をダウンロードしてきて適当なフォルダ(例えば C:\memcached) に解凍します。

あとはコマンドプロンプト

C:\memcached\memcached -d install
C:\memcached\memcached -d start

いささか古いバージョンになってしまいますが、最新のものを Windows でビルドする方法が良く分からないのでパス。

これを利用するとビルドできる*1っぽい ?
GitHub - LukeCarrier/windows-memcached: Abandoned attempt at getting memcached built on Windows

Fedora の場合

sudo dnf install memcached
sudo systemctl enable memcached
sudo systemctl start memcached

こちらも若干古いバージョンですが、最新版より少し遅れているだけなので大きな問題はなさそうです。あとは適宜 sudo dnf upgrade しておけば、更新が来た段階でアップデートされますし。

一応自前でビルドも試しましたがいまいちでした。他のディストリビューション(CentOS, Ubuntu, etc..)ではまた少し事情が変わってくるかもしれませんが。

*1:しかし作者は「Windows はひどいサーバープラットフォームだからこれは使うな」と言っている

巳年じゃないけど Python やろうぜ(その 20)

Ajax の活用(続き)

バリデーションエラーの出力

フォームのバリデーションエラーに関する情報は form.errors に辞書形式で格納されているので、それを利用すれば行けることがわかりました。

views.py

from django.shortcuts import render
from django.http.response import JsonResponse
from django.utils.translation import ugettext as _
from .forms import CustomerForm
from .models import Customer

# Create your views here.


def index(request):
    form = CustomerForm(label_suffix='')
    return render(request, 'customer_search/search.html', {
        'form': form,
    })


def search(request):
    form = CustomerForm(request.POST)
    if form.is_valid():
        customer_id = request.POST['customer_id']
        try:
            customer = Customer.objects.get(pk=customer_id)
            customer_name = customer.name
            customer_phone = customer.phone
        except Customer.DoesNotExist:
            customer_name = _('No data!')
            customer_phone = _('No data!')
    else:
        customer_name = _('Error!')
        customer_phone = _('Error!')
    return JsonResponse({
        'customer_name': customer_name,
        'customer_phone': customer_phone,
        'errors': form.errors,
    })

メソッド名が微妙に変わってるので、対応して urls.py も修正しておきます。

from django.conf.urls import url
from . import views

app_name = 'customer_search'
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^search/$', views.search, name='search'),
]

templates/customer_search/search.html

{% load static i18n %}
{% trans 'customer search' as page_title %}
<!doctype html>
<html>
  <head>
    <title>{{ page_title | title }}</title>
    <link rel="stylesheet" href="{% static 'customer_search/css/search.css' %}">
    <script src="//code.jquery.com/jquery-3.2.0.min.js"></script>
    <script src="{% static 'customer_search/js/js.cookie.js' %}"></script>
    <script src="{% static 'customer_search/js/csrf_token.js' %}"></script>
  </head>
  <body>
    <h1>{{ page_title | title }}</h1>
    <form method="post" action="{% url 'customer_search:search' %}">
      {% csrf_token %}
      <table>
        {% for field in form %}
          <tr>
            <th>{{ field.label_tag }}</th>
            <td>
              {{ field }}
              <span class="error" id="{{ field.html_name }}_errors"></span>
            </td>
          </tr>
        {% endfor %}
      </table>
      <input type="submit" value="{% trans 'Search' %}">
    </form>
    <script src="{% static 'customer_search/js/search.js' %}"></script>
  </body>
</html>

span 要素は DOM で追加する方がスマートかなと思ったけど、やってみると意外に苦戦した*1ので、とりあえずこうしておきます。

static/customer_search/js/search.js

$('form').submit(function (event) {
  event.preventDefault();
  $.post(
    $('form').attr('action'),
    { 'customer_id': $('#id_customer_id').val() },
    function (res) {
      $('#id_customer_name').val(res.customer_name);
      $('#id_customer_phone').val(res.customer_phone);
      var errors = res.errors;
      var messages = [];
      if(!$.isEmptyObject(errors)) {
        messages = errors.customer_id;
      }
      $('#customer_id_errors').text(messages.join(''));
    }
  );
});

エラーメッセージはフォームのフィールド名ごとに配列で格納されているので、join で連結*2します。

画面遷移が発生していないことが確認できます。

*1:自分が下手な書き方をしたせいでエラーメッセージが消えずにどんどん増えていくという間抜けなことにw

*2:今回のケースでは複数のエラーメッセージが格納されるケースは確認できていないので、必要ないと言えば必要ないのですがw

巳年じゃないけど Python やろうぜ(その 19)

Ajax の活用

以前作成した customer_search アプリケーションを Ajax を活用して作り変えてみましょう。

Ajax 活用のための準備

Ajax 活用のために、いくつかの準備が必要です。

1. js.cookie.js をダウンロードする

GitHub - js-cookie/js-cookie: A simple, lightweight JavaScript API for handling browser cookies

上記サイトから js.cookie.js をダウンロードします。間違っても script 要素の src 属性に URL を直接打ち込まないように(GitHub は CDN ではありません !)。

持って来た js.cookie.js は django_test/customer_search/static/customer_search/js/ に保存します。

2. csrf_token.js の作成

以下の内容で django_test/customer_search/static/customer_search/js/ 内に csrf_token.js を作成します。ほぼ公式ドキュメントからのコピペです。

var csrftoken = Cookies.get('csrftoken');

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
  beforeSend: function(xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader('X-CSRFToken', csrftoken);
    }
  }
});
続きを読む

巳年じゃないけど Python やろうぜ(その 18)

キャッシュサーバーの活用

これまではセッションの情報をデータベース(SQLite3)で管理してきましたが、別途キャッシュサーバーを用意して、そちらで管理することもできます。

キャッシュサーバーとしてよく使われるのが Memcached です。Memcachedインストールは済んでいて、サービスが起動しているものと仮定します。*1

python3-memcached をインストールします。

$ pip install python3-memcached

django_test/settings.py に以下を追記します。

# Session engine
# https://docs.djangoproject.com/en/1.10/ref/settings/#session-engine
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'


# Cache
# https://docs.djangoproject.com/en/1.10/ref/settings/#caches
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
    }
}

コメントは参照すべきドキュメントの URL を明示するためにつけただけなので、実際には無くても大丈夫です。

Memcached が正しくインストールされていて、かつサービスが開始されていれば、他は何も変更する必要はありません。

Memcached の各 OS ごとのインストール方法については、記事を改めて解説させていただきます。

*1:WindowsLinux 系 OS とで手順が異なります。

巳年じゃないけど Python やろうぜ(その 17)

国際化と翻訳

多くのフレームワークがそうであるように、Django にも国際化の仕組みが備わっています。

下準備

あらかじめ settings.pyUSE_I18NTrue になっていることを確認します(デフォルトでそうなっていると思います)。

今回は customer_search に適用してみます。customer_search フォルダの直下に locale フォルダをあらかじめ作成しておきます。

翻訳するフレーズをマークする

customer_search/forms.py

from django import forms
from .models import Customer
from django.utils.translation import ugettext as _


class CustomerForm(forms.Form):
    customer_id = forms.IntegerField(
        label=_('Customer ID'),
        widget=forms.TextInput(attrs={'size': 5}),
        min_value=1,
    )
    customer_name = forms.CharField(
        required=False,
        label=_('Customer name'),
        widget=forms.TextInput(attrs={'size': 20, 'readonly': True}),
    )
    customer_phone = forms.CharField(
        required=False,
        label=_('Phone'),
        widget=forms.TextInput(attrs={'size': 20, 'readonly': True}),
    )

customer_search/templates/customer_search/search.html

{% load static i18n %}
{% trans 'customer search' as page_title %}
<!doctype html>
<html>
  <head>
    <title>{{ page_title | title }}</title>
    <link rel="stylesheet" href="{% static 'customer_search/css/search.css' %}">
  </head>
  <body>
    <h1>{{ page_title | title }}</h1>
    <form method="post" action=".">
      {% csrf_token %}
      <table>
        {% for field in form %}
          <tr>
            <th>{{ field.label_tag }}</th>
            <td>
              {{ field }}
              {% if field.errors %}
                <span class="error">
                  {% trans 'Error!' %}
                  {% for error in field.errors %}
                    {{ error }}
                  {% endfor %}
                </span>
              {% endif %}
            </td>
          </tr>
        {% endfor %}
      </table>
      <input type="submit" value="{% trans 'Search' %}">
    </form>
  </body>
</html>

テンプレート内では i18n を読み込むことにより trans という組み込みタグが使えるようになります。

続きを読む

巳年じゃないけど Python やろうぜ(その 16)

汎用ビューの活用

Django には、用途に応じて使える汎用ビューが多数用意されています。

django_test/urls.py

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^hello/', include('hello.urls')),
    url(r'^customer_search/', include('customer_search.urls')),
    url(r'^cities/', include('major_city.urls')),  # 追加
]

major_city/urls.py

from django.conf.urls import url
from django.views.generic import ListView, DetailView
from .models import City

app_name = 'major_city'
urlpatterns = [
    url(
        r'^$',
        ListView.as_view(
            queryset=City.objects.all(),
            context_object_name='cities',
            template_name='major_city/list.html'
        ),
        name='index'
    ),
    url(
        r'^(?P<pk>\d+)/$',
        DetailView.as_view(
            model=City,
            context_object_name='city',
            template_name='major_city/detail.html'
        ),
        name='show'
    ),
]

今回は一覧表示系のビューに使用する ListView と、詳細表示系のビューに使用する DetailView を使うことにしました。

name を使ってそれぞれ名前を付けています。app_name は「名前空間のようなもの」で、逆引きの際に 'major_city:index' のようにすることで、他のアプリの index と区別できるようになります。

URL の逆引き

major_city/templates/major_city/list.html

{% load static %}
<!doctype html>
<html>
  <head>
    <title>日本の政令指定都市</title>
    <link rel="stylesheet" href="{% static 'major_city/css/list.css' %}">
  </head>
  <body>
    <table border="1">
      <caption>日本の政令指定都市</caption>
      <thead>
        <tr>
          <th>No.</th>
          <th>都道府県</th>
          <th>都市名</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        {% for city in cities %}
        <tr>
          <td class="no">{{ city.id }}</td>
          <td class="pref">{{ city.prefecture.name }}</td>
          <td class="name">{{ city.name }}</td>
          <td class="button">
            <form method="get" action="{% url 'major_city:show' city.id %}">
              <input type="submit" value="詳細">
            </form>
          </td>
        </tr>
        {% endfor %}
      </tbody>
    </table>
  </body>
</html>

テンプレート内では url タグを使って URL の逆引きができます。

major_city/templates/major_city/detail.html や各種 CSSGitHubリポジトリを参考にしてください。

GitHub - akaneko3/PracticeDjango: Django のお勉強

続きを読む