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

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

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

管理画面を使う

管理者ユーザーの作成

管理画面を使うにはまず管理者ユーザーを作成しなければなりません。

$ ./manage.py createsuperuser
Username (leave blank to use '(snip)'): admin
Email address: admin@example.com
Password: 
Password (again): 
This password is too short. It must contain at least 8 characters.
This password is too common.
Password: 
Password (again): 
Superuser created successfully.

メールアドレスはでっち上げで公式のチュートリアルと同じものを入れてますが、実際に入力するときはちゃんと使えるアドレスを入れておいた方が良いかと思います(自分も実際にはそうしています)。

ちなみにパスワードは 8 文字以上にしないと怒られる、ということをお見せするために敢えてそのままコピー & ペーストしました。

続きを読む

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

だいぶシリーズが長期化してきました(苦笑)

Model で遊ぶ

今回使用するデータは「現場で使えるSQL 第2版」のものを使用することにします。

現場で使えるSQL 第2版 (DB Magazine SELECTION)

現場で使えるSQL 第2版 (DB Magazine SELECTION)

$ ./manage.py shell
Python 3.6.0 (default, Feb  4 2017, 00:11:30) 
[GCC 6.3.1 20161221 (Red Hat 6.3.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from customer_search.models import Customer
>>> Customer.objects.all()
<QuerySet []>
>>> c = Customer(name='(株)ワイキキソフト', phone='090-AAAA-AAAA')
>>> c.save()
>>> c.id
1
>>> c.name
'(株)ワイキキソフト'
>>> c.phone
'090-AAAA-AAAA'
>>> Customer.objects.all()
<QuerySet [<Customer: Customer object>]>

save はテーブルにデータを保存するメソッドです。実際に DB Browser for SQLite で確認した画面がこちら。

確かにデータは保存されています…が、チュートリアルでは objects.all() の出力を見て「これは困りました*1ね」というお話になります。これはオブジェクトを表示するときに文字列化する models.Model.__str__ メソッドをオーバーライドしていないので、デフォルトのものが使用されている*2ことによります。

このままだと、後程説明する管理画面での表示にも差し障るので、models.py を改造しましょう。

from django.db import models

# Create your models here.


class Customer(models.Model):
    name = models.CharField(max_length=20)
    phone = models.CharField(max_length=20)

    def __str__(self):
        return self.name

自身の name の内容を表示するように変更しました。もう一度 shell を起動して確認してみましょう。

$ ./manage.py shell
Python 3.6.0 (default, Feb  4 2017, 00:11:30) 
[GCC 6.3.1 20161221 (Red Hat 6.3.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from customer_search.models import Customer
>>> Customer.objects.all()
<QuerySet [<Customer: (株)ワイキキソフト>]>
>>> Customer.objects.filter(id=1)
<QuerySet [<Customer: (株)ワイキキソフト>]>
>>> Customer.objects.get(pk=1)
<Customer: (株)ワイキキソフト>

filter は条件に合致するデータを全て取得するもので、出力の型は QuerySet になっています。一方の get は主キーを指定してそれに該当する単一のデータを取得するものなので、出力の型は Customer になります。

表示に顧客名が表示されていて、いい感じですね。次回は管理画面を利用して Model を管理する方法について書きます。

*1:オブジェクトの内容が表示されていないということです。

*2:デフォルトの挙動は公式ドキュメントからソースコードを参照するとわかります。

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

Model を扱う

いよいよ今回から Model を扱います。

…の前に。

AtomDjango アプリの開発を快適に行うために

すっかり忘れていましたが、AtomDjango アプリの開発を行う際に入れておくと良いパッケージを二つ紹介しておきます。

新しいアプリの作成

端末からコマンドを入力して新しいアプリを作成します。

$ ./manage.py startapp customer_search

今回はデータベース内のレコードを ID を入力して顧客を検索する customer_search アプリを作ります。なお今回から Linux 上で作業していますが、基本となる手順はほぼ同じ*1です。

settings.py の編集を忘れずに。

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'hello',
    'customer_search',  # 追加
]

Model を作成する

Model の作成は、Model に対応する class を models.py に書くだけです。と言っても、Rails みたいにコマンド一つで自動生成、とは行かないのが難点ですが…。

from django.db import models

# Create your models here.


class Customer(models.Model):
    name = models.CharField(max_length=20)
    phone = models.CharField(max_length=20)

今回は Customer (顧客)という単一のモデルを扱うことにします。モデル同士の関連性についてはまた後程ということで。

CharFieldVARCHAR 型に対応するもので、max_lengthバイト数を指定できます。このコードは SQLVARCHAR(20) という型宣言と同等になります。なお、主キーは特に指定しなければデフォルトで id というフィールドが勝手に作成されます。

Model を有効にする

作った Model を有効にするための作業が必要です。まずは

$ ./manage.py makemigrations customer_search
Migrations for 'customer_search':
  search/migrations/0001_initial.py:
    - Create model Customer

sqlmigrate コマンドを使うと、migrate コマンドを実行した時に実際に発行される SQL が読めます*2

$ ./manage.py sqlmigrate search 0001
BEGIN;
--
-- Create model Customer
--
CREATE TABLE "customer_search_customer" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" varchar(20) NOT NULL,
    "phone" varchar(20) NOT NULL
);
COMMIT;

customer_search_customer という新たなテーブルが作成されるようですね。では migrate を実行しましょう。

$ ./manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, customer_search, sessions
Running migrations:
  Applying customer_search.0001_initial... OK

システムに必要なテーブルは既に作成されているので、今回は新しく追加したものだけが実行されました。

次回は公式のチュートリアルに沿って、これを使ってコマンドラインで少し遊んでみます。

*1:Windows 上での作業について特段の補足が必要な場合のみ別途補足します。

*2:下記の出力例は読みやすさのためにフォーマットを変えています。

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

3 週間ほど空いてしまいました。この間こっそりと開発環境を Linux に移行させていたのは秘密です*1

hello ページを作る(続き)

静的ファイル(CSS, JavaScript, etc...)の配置

今回は CSS を配置してみます。django_test\hello\static\hello というフォルダを作り、その中に css\hello.css を作成します。

body {
    background-color: #f8f8f8;
    color: #0c3c26;
}

p {
    font-size: large;
}

label {
    font-weight: bold;
}

次に common.html を書き換えます。

{% load static %}
<!doctype html>
<html>
  <head>
    <title>{{ page_title | title }}</title>
    <link rel="stylesheet" href="{% static 'hello/css/hello.css' %}">
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>

サーバーを起動して確認してみましょう。

わかりにくいですが、文字色と背景色を Django の公式ドキュメントに合わせています。

JavaScript や画像ファイルなど、その他の静的ファイルも同様の方法で配置して読み込むことができます。

…君らそろそろ Model 使いたいやろ ? というわけで、次回からいよいよ Model です !

*1:実はこの記事も Linux 上の Firefox で書いてる

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

もうちょっとだけ hello ページをいじるよ。

hello ページを作る(続き)

テンプレートを使い回す

Django には共通のテンプレートを用意してそれを使い回す方法が用意されています。extendsinclude です。前者はベースとなるテンプレートに個別のテンプレートを埋め込むイメージ、後者は共通する部分的なテンプレートを呼び出すイメージです。今回は extends を紹介します。

続きを読む

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

Hello ページを作る(続き)

前回までで、テンプレートを利用して Web ページを表示させることができましたが、まだ Web アプリっぽくないですよね。

もう少しそれっぽくするにはフォームとかあったらいいんじゃないでしょうかね。

フォームを作成する

今回はフォームを使って

  • http://localhost:8000/hello/ にアクセスしたら、名前を入力するフォームが表示されるようにする
  • 名前を入力して送信したら、それを表示専用のページに渡して表示させる。リダイレクト先は http://localhost:8000/hello/thanks/ とする(「名前を入力してくれてありがとう !」的な感じ)
続きを読む

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

前回からの続きです。

Hello ページを作る(続き)

HttpResponse に HTML を渡すための方法として、テンプレートを用いることにします。

まず django_test\hello の下に templates というフォルダを作り、さらにその中に hello というフォルダを作ります(このようにするわけは後で説明します。)

django_test\hello\templates\hello\hello.html を以下の内容で作成します。

<!doctype html>
<html>
  <head>
    <title>Hello Page</title>
  </head>
  <body>
    <p>Hello, Django!</p>
  </body>
</html>

django_test\hello\views.py を以下のように修正します。

from django.template import loader  # 追加
from django.http import HttpResponse


def index(request):
    template = loader.get_template('hello/hello.html')
    html = template.render()
    return HttpResponse(html)

サーバを起動して確認してみましょう。

テンプレートの置き場所として、templates フォルダの中にもう一度 hello フォルダを作りましたが、これには理由があります。Django では、仮に

  • django_test\foo\templates\index.html
  • django_test\bar\templates\index.html

と、異なるアプリで同名のテンプレートがあったとき、常に 先に検出した テンプレートを読み込んでしまいます。そのため、意図しないテンプレートを読み込んでしまわないように「名前空間」を付けて

  • django_test\foo\templates\foo\index.html
  • django_test\bar\templates\bar\index.html

のように管理するわけです。

ビューで使える便利なショートカット

テンプレートを読み込んで、それをレンダリングして HttpResponse に渡す、といった処理は頻繁に行います。そのため django.shortcutsrender という関数が用意されています。

from django.shortcuts import render


def index(request):
    return render(request, 'hello/hello.html')

この 1 行目は、最初に hello アプリを作った直後の views.py のものと同じです。それだけよく使う、ということで、最初から用意してくれているんですね。ちなみに render_to_response というのもありますが、公式ドキュメントによると非推奨、かつ将来的に廃止予定になるようです。

テンプレートに変数を埋め込む

実際にはどっか(データベースとか)から取得してきたデータをテンプレートに渡して値を埋め込むような処理をすることが多いでしょう。公式ドキュメントを見ると、render の第三引数に辞書を渡すことでそれができることが分かります。

from django.shortcuts import render


def index(request):
    my_name = 'Django'
    return render(request, 'hello/hello.html', {'my_name': my_name})
<!doctype html>
<html>
  <head>
    <title>Hello Page</title>
  </head>
  <body>
    <p>Hello, {{ my_name }}!</p>
  </body>
</html>

テンプレートの方に独特の記法が出てきました。これが Django のテンプレートならではの記法です。今後いろいろなところで出てきます。ちなみに拡張子は html のままで大丈夫です。