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..)ではまた少し事情が変わってくるかもしれませんが。
巳年じゃないけど 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します。
画面遷移が発生していないことが確認できます。
巳年じゃないけど 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
$ 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 ごとのインストール方法については、記事を改めて解説させていただきます。
巳年じゃないけど Python やろうぜ(その 17)
国際化と翻訳
多くのフレームワークがそうであるように、Django にも国際化の仕組みが備わっています。
下準備
あらかじめ settings.py
の USE_I18N
が True
になっていることを確認します(デフォルトでそうなっていると思います)。
今回は 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
や各種 CSS は GitHub のリポジトリを参考にしてください。
巳年じゃないけど Python やろうぜ(その 15)
初期データの一括投入
今回から、当ブログではすっかりお馴染みの「政令指定都市一覧」を使って、Django の様々な機能を見ていきます。
$ ./manage.py startapp major_city
例によってアプリを作成するところからスタート。
Model を作ります。
from django.db import models # Create your models here. class District(models.Model): name = models.CharField(max_length=30) class Prefecture(models.Model): name = models.CharField(max_length=30) district = models.ForeignKey(District, on_delete=models.CASCADE) class City(models.Model): name = models.CharField(max_length=30) prefecture = models.ForeignKey(Prefecture, on_delete=models.CASCADE) designated = models.DateField() area = models.DecimalField(max_digits=7, decimal_places=2) population = models.IntegerField()
ForeignKey
が初登場しました。これは参照する親モデルのクラス名と、親クラスを削除した時の挙動(ON DELETE ~)を指定します。ちなみに on_delete
は Django 2.0 以降で必須になるそうです*1ので、デフォルトに甘えずに指定しておくと移行が楽になるでしょう。
マイグレーションを作成します。
$ ./manage.py makemigrations major_city (0.001) SELECT name, type FROM sqlite_master WHERE type in ('table', 'view') AND NOT name='sqlite_sequence' ORDER BY name; args=None (0.000) SELECT "django_migrations"."app", "django_migrations"."name" FROM "django_migrations"; args=() Migrations for 'major_city': major_city/migrations/0001_initial.py: - Create model City - Create model District - Create model Prefecture - Add field prefecture to city
SQL を確認してみましょう。
$ ./manage.py sqlmigrate major_city 0001 (中略) BEGIN; -- -- Create model City -- CREATE TABLE "major_city_city" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(30) NOT NULL, "designated" date NOT NULL, "area" decimal NOT NULL, "population" integer NOT NULL ); -- -- Create model District -- CREATE TABLE "major_city_district" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(30) NOT NULL ); -- -- Create model Prefecture -- CREATE TABLE "major_city_prefecture" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(30) NOT NULL, "district_id" integer NOT NULL REFERENCES "major_city_district" ("id") ); -- -- Add field prefecture to city -- ALTER TABLE "major_city_city" RENAME TO "major_city_city__old"; CREATE TABLE "major_city_city" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(30) NOT NULL, "designated" date NOT NULL, "area" decimal NOT NULL, "population" integer NOT NULL, "prefecture_id" integer NOT NULL REFERENCES "major_city_prefecture" ("id") ); INSERT INTO "major_city_city" ( "id", "name", "designated", "area", "population", "prefecture_id" ) SELECT "id", "name", "designated", "area", "population", NULL FROM "major_city_city__old"; DROP TABLE "major_city_city__old"; CREATE INDEX "major_city_prefecture_a34a99d3" ON "major_city_prefecture" ("district_id"); CREATE INDEX "major_city_city_71a71d54" ON "major_city_city" ("prefecture_id"); COMMIT;
若干回りくどい処理をしていますが、これはモデル名のアルファベット順に処理をしようとしているからでしょうか。
最後はマイグレーションファイルを適用するのを忘れずに。
$ ./manage.py migrate