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

くろねこ日記

ソフトウェアに関する技術メモが多いです.

django始めました

はじめに

PythonにはDjangoというフルスタックウェブフレームワークがあります. これまでここのブログではマイクロウェブフレームワークであるflaskの記事が多かったのですが, 最近,Djangoを使う機会を得たことで,学ぶ機会ができ,使ってみることにしました.

なお,本記事で扱う環境は以下の通りです

学ぶにあたって,まずはここを見て一通り動かしてみました.

Python Django入門 (1) - Qiita

解説がよくまとまっており内容もわかりやすいのですが、自分でどんなアプリを作るか決めないまま進めたこともあってか曖昧模糊だったので、シンプルなブログを作って練習してみました。

今回は以下のような仕様のブログの作成しました.

作成するブログ

  • 機能
    • 記事投稿
      • タイトル
      • 本文
      • 日付
    • 記事表示

これだけです. 見た目についてもbootstrapなどを使えば簡単にかっこいいウェブアプリがつくれると思いますが,djangoに必要な部分以外外すことで、よりシンプルにして理解しやすくしました..

作成

作成にあたり,まずはdjangoの基本的な使い方を説明します.

djangoのプロジェクト(kuronekoblogとする)をスタートするには

django-admin startproject kuronekoblog

です.

これで基本的なディレクトリが勝手に生成されます.

kuronekoblog/
    manage.py
    kuronekoblog/
        __init__.py
        settings.py
        urls.py
        wsgi.py

それぞれのファイルについて説明しましょう.

  • kuronekoblog/manage.py これはdjangoのアプリを新規作成したり,データベースを扱う,サーバを立ち上げるなどの役割を持つファイルです.

  • kuronekoblog/kuronekoblog/settings.py 外部ライブラリや日本語ロケールの設定などを行なうファイルです

  • kuronekoblog/kuronekoblog/urls.py ユーザのアクセスURLによってviews.pyで定義した関数のどれに渡すかを決めるファイル

これらを、まとめると、開発の流れとしては、

settings.pyで日本語ロケール設定をする。

models.pyでデータベースの設計をする。

views.pyにリクエストに応じた関数を書く。

urls.pyにリクエストを受ける時のURLを設定する。

という感じです。

運用の流れとしては

ユーザからリクエストが来たらurls.pyでリクエストの内容に応じてviews.pyのなかに書かれている関数に投げる。

urls.pyよりきたリクエストにしたがってviews.pyでmodels.pyで定義したデータベースを操作したり、templatesで定義したhtmlファイルを呼び出して、ユーザに送信する。

という流れです。

実装

プログラムを組んでいきましょう。

まずは,プロジェクト内にウェブアプリを生成しましょう.

python manage.py startapp blog

これでkuronekoblog/blogというディレクトリが生成されるので全体として次のようになるはずです

kuronekoblog/
    manage.py
    blog/
        __init__.py
        settings.py
        urls.py
        wsgi.py
    kuronekoblog/
        __init__.py
        settings.py
        urls.py
        wsgi.py

まずはsettings.pyで日本語設定やblogを扱うように登録をします. 次のように編集してください.

kuronekoblog/settings.py

・・・
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
)
・・・
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
・・・

ここまでいったら,次はadminページを作ります

まずは次のコマンドを実行してください

python manage.py createsuperuser

実行したら

ユーザID
メールアドレス
パスワード

が聞かれるので,それぞれ設定してください

次にAdminページで表示させるものについて設定を次のように行なってください.

blog/admin.py

from django.contrib import admin
from blog.models import blogdata

# Register your models here.
class blogdataAdmin(admin.ModelAdmin):
    list_display = ('id', 'title', 'date',)
    list_display_links = ('id', 'title',)

admin.site.register(blogdata, blogdataAdmin)

次にmodels.pyにてブログのデーターベースを定義します. 今回は,シンプルなブログを作るので次のような情報が扱えるようにしましょう.

  • ブログタイトル
  • ブログ本文
  • ブログの生成日時

では実際に定義していきます.

blog/models.py

from django.db import models
from datetime import datetime


class blogdata(models.Model):
    title = models.CharField(max_length=512)
    text = models.TextField()
    date = models.DateTimeField(default=datetime.now)
  • タイトル(title)はCharFieldで最大512文字まで扱えるようにしました
  • 本文(text)はテキストフィールドで好きなように記述できるようにしました
  • 投稿日時(date)はDatetimeで現在の日時を渡すようにしました 面白いのはここでは一切SQLを書いてないことです. SQLを書かなくてもこのように直感的な書き方でデータベースが扱えるなんて凄いですね.

では次は,データベースの生成を行ないます

まずは,kuronekoblog自体のデータベースを作りましょう

python manage.py migrate

これで直下にsqliteのファイルが生成されます 次にmodelで定義したblogアプリのデータベースを作ります

python manage.py makemigrations blog

そして反映

python manage.py migrate

ここで一旦ウェブアプリを立ち上げてみましょう

python manage.py runserver

これでlocalhost:8000に立ち上がります. 以下のページにアクセスしてください

http://localhost:8000/admin/

先程,定義したAdmin.pyの内容に従って表示されるはずです.

ではviews.pyに処理を書いていきましょう. まずはviews.pyとurls.py動作を確かめるために次のようにしましょう. urls.pyはblog以下に無いはずなので生成してください.

blog/views.py

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
def edit(request):
    return HttpResponse('編集')

def show(request):
    return HttpResponse('読む')

blog/urls.py

#!coding:utf-8
from django.conf.urls import patterns, url
from blog import views

urlpatterns = patterns('',
        url(r'^$', views.show, name='show'),
        url(r'^edit/$', views.edit, name='edit'),
)

kuronekoblog/urls.py

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

urlpatterns = patterns('',

    url(r'^admin/', include(admin.site.urls)),
    url(r'^blog/', include('blog.urls', namespace='blog')),
)

これでサーバを立ち上げて以下のURLにアクセスしてください

python manage.py runserver

http://localhost:8000/blog

http://localhost:8000/blog/edit

localhost:8000/blogのほうでは「読む」という文字列だけが返ってくるはずです.

localhost:8000/blog/editのほうでは「編集」という文字列だけが返ってくるはずです.

簡単にurls.pyとviews.pyの関係を説明しますと, まずはblog/views.pyの関数では

  • edit
  • show

の二つの関数をつくりました

次にこれらの関数にリクエストを投げるためにblog/urls.pyに正規表現をつかってリンクさせております.

url(r'^$', views.show, name='show'),

url関数の第一引数がURLのパラメータです.第二引数ではviews.showと書くことで,views.pyの中にあるshow関数にリクエストを投げること. という指定をしてます.第三引数は名前を持たせているだけです(たぶん...).

次にblog/urls.pyをkuronekoblog自体に伝えるために kuronekoblog/urls.pyにblog/urls.pyを登録したわけです.

これで紐付けは完了です.

次はテンプレートまわりを実装しましょう.

テンプレートはjinja2のフォーマットが対応しており,flaskでhtmlを使ったことがある人は馴染み深いはずです.

まずはblog以下にtemplatesを作りましょう.

mkdir blog/templates

この中にbase.htmlというflaskでいうところのlayout.htmlにあたる,各ページ共通部分のヘッダなどを作ります.

touch blog/templates/base.html

base.htmlを編集しましょう.

blog/templates/base.html

<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}">
<head>
<meta charset="UTF-8">
<title>{% block title %}BlogPage{% endblock %}</title>
</head>
<body>
    {% block content %}
        {{ content }}
    {% endblock %}
</body>
</html>

次にshow関数で呼ばれるテンプレートを書きます.そのためにディレクトリを次のように追加してください

mkdir blog/templates/blog

ここにテンプレートを作ります.ここではblogshow.htmlというファイルにしましょう.

touch blog/templates/blog/blogshow.html

この中にbase.htmlを継承してブログを表示する部分を書きます. blog/templates/blog/blogshow.html

{% extends "base.html" %}
{% block title %}ブログ内容{% endblock title %}

{% block content %}
    {% for blog in blogs %}
        <h3>日付</h3>
        {{ blog.date }}
        <h3>タイトル</h3>
        {{ blog.title }}
        <h4>本文</h4>
        {{ blog.text }}
    {% endfor %}
{% endblock content %}

かなりシンプルですね.日付,タイトル,本文という順で表示されるように想定してます.

では,今つくったtemplatesをviewsに書きましょう. 先程つくったviewsの中身を次のように改造してください

blog/views.py

from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.template import RequestContext
from blog.models import blogdata

# Create your views here.
def edit(request):
    return HttpResponse('編集')

def show(request):
    blogs = blogdata.objects.all()
    return render_to_response('blog/blogshow.html', {'blogs': blogs}, context_instance=RequestContext(request))

flaskではテンプレートを呼ぶときはrender_templatesをつかいましたが,djangoではrender_to_responseを使います. blogsというので,データベースのオブジェクトを受けとって,辞書でテンプレートのhtmlに渡してます.

では実際にAdminを弄ってデータが入ることを確認しましょう. 次のURLにアクセスしてテキトーなブログを作りましょう.

http://localhost:8000/admin/blog/blogdata/

blogdataに追加という箇所をクリックすると編集画面が現われるので書いてみてください.

f:id:kuroneko0208:20141112203354p:plain

データベースを管理画面から直感的な操作で弄れるので便利ですね.

追加したら以下のURLにアクセスしてshowがデータベースの内容をひっぱってブログを表示していることを確認してください.

http://localhost:8000/blog/

見れたでしょうか.

次は投稿機能を実装します.

基本的にshowを実装した流れは同じなのですが,投稿フォームはdjangoがもともと持っているものを使います. このあたりはさすがフルスタックフレームワークといったところです.

views.pyを開いて次のように編集してください

blog/views.py

#!coding:utf-8
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.template import RequestContext
from blog.models import blogdata
from django.forms import ModelForm


class blogform(ModelForm):
    class Meta:
        model = blogdata
        fields = ('title', 'text', 'date', )

def edit(request):
    blogs = blogdata()
    form = None
    if request.method == 'POST':
        pass
    else:
        form = blogform(instance=blogs)
    return render_to_response('blog/blogedit.html', {'form': form}, context_instance=RequestContext(request))

def show(request):
    blogs = blogdata.objects.all()
    return render_to_response('blog/blogshow.html', {'blogs': blogs}, context_instance=RequestContext(request))

さっきと違うのはformの処理とeditの内容を追加したことですね. blogformというクラスでは,modelでは編集するデータベースを指定,fieldsでは投稿画面で編集する内容を記載します.

editの中ではblogdataのインスタンスをblogsで受けとって,それをblogformのクラスに渡したあと,formで受けとっています. そのformをrender_to_responseで処理しているわけです.まずはGET部分の処理だけを書きました.

次はtemplatesです.showと同様にblogedit.htmlをtemplates/blogの中に作って編集します.

blog/templates/blog/blogedit.html

{% extends "base.html" %}
{% block title %}投稿{% endblock title %}

{% block content %}
    <form action="{% url 'blog:edit' %}" method="post">
        {% csrf_token %}
        {{ form }}
        <button type="submit">投稿</button>
    </form>
{% endblock content %}

formアクションの

{% url 'blog:edit' %}

ではblog内のviewsに定義されているeditのformを呼んでいることを示しています.

もう一つ

{% csrf_token %}

というのはこれがないと強制的なリダイレクトとdjangoが誤認識をしてしまうので,その対策に書いてます.

あとは

{{ form }}

でformの内容を呼んでいます.

それでは以下のURLにアクセスして,投稿画面が動いていることを確認してください. レイアウトなどはまったく弄ってないので汚ないと思います(笑)

http://localhost:8000/blog/edit

確認したら,次はviews.pyにpostの処理を追加しましょう.

blog/views.py

#!coding:utf-8
from django.shortcuts import render_to_response, redirect
from django.http import HttpResponse
from django.template import RequestContext
from blog.models import blogdata
from django.forms import ModelForm


class blogform(ModelForm):
    class Meta:
        model = blogdata
        fields = ('title', 'text', 'date', )

# Create your views here.
def edit(request):
    blogs = blogdata()
    form = None
    if request.method == 'POST':
        form = blogform(request.POST, instance=blogs)
        if form.is_valid():
            blog = form.save(commit=False)
            blog.save()
            return redirect('blog:show')
        pass
    else:
        form = blogform(instance=blogs)
    return render_to_response('blog/blogedit.html', {'form': form}, context_instance=RequestContext(request))

def show(request):
    blogs = blogdata.objects.all()
    return render_to_response('blog/blogshow.html', {'blogs': blogs}, context_instance=RequestContext(request))

POSTで受けとった内容をblogformを経由してformで受けとってます.

form.is_valid()

というのは形式が正しいかを判断する部分らしく,バリデーションと呼ばれるようです(知らなかった...).

バリデーションが正しければその投稿内容を保存します.

保存したらredirectでshow関数を呼びだしてブログ内容を表示するようにします.

これでだいたいの処理は書き終わりましたが,

http://localhost:8000/blog

から編集画面のリンクを張りわすれていたので張っておきましょう.

blog/templates/blog/blogedit.html

{% extends "base.html" %}
{% block title %}ブログ内容{% endblock title %}

{% block content %}
    <a href="{% url 'blog:edit' %}">投稿画面</a>
    {% for blog in blogs %}
        <h3>日付</h3>
        {{ blog.date }}
        <h3>タイトル</h3>
        {{ blog.title }}
        <h4>本文</h4>
        {{ blog.text }}
    {% endfor %}
{% endblock content %}

これで完成です.

まとめ

非常に簡易なブログをquitaの記事を参考にしながら作成した.