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

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

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

巳年じゃないけど 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 のお勉強

続きを読む