Color Selector

default niceblue intenseblue otherblue blue puregreen grassgreen green olive gold orange pink fuchsia violet red

Container Selector

Article

[1]Python,Django,Dockerで作るWEBアプリケーション

Share on LinkedIn
Pocket

今回はPython,Django,Dockerを使いながらWEBアプリケーションを作って公開までの手順を備忘録として残しておきたいと思います。なお、環境は以下です。

■開発環境(ローカル)
OS:Mac
Python:3.5.2
Framework:Django1.10
packages:pip8.1.2,virtualenv15.0.3,django-bootstrap-form3.2.1
SQL:SQLLite3
Git:SourceTree
Docker:1.12.0 ※Dockerを使う場合は上記のインストールはbuild時に行う

■本番環境(※公開はしていません)
さくらVPS 512
OS:Ubuntu16.0.4
Python:3.5.2
Framework:Django1.10
packages:pip8.1.2,django-bootstrap-form3.2.1
SQL:SQLLite3
Git:SourceTree,Bitbucket
Deploy:deploybot
Docker:1.12.0
DockerHub:django:onbuild

ではまずPythonのインストールからスタートです。

Python3のインストール

Macには最初からPython2系(正確には2.7)は入っています。なのでまず、HomeBrewというパッケージ管理ツールをインストールしてからPython3をインストールします。

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew update
brew info python3 #Pythonパッケージの確認
python3: stable 3.5.2 (bottled), devel 3.6.0a3, HEAD

brew install python3 #Python3をインストール

Python3をインストールすると2系と同居した状態になります。バージョンの確認をします。

python -V
Python 2.7.10
python3 -V
Python 3.5.2

virtualenvのインストール

virtualenvはPythonでWebアプリケーションを開発する時の仮想環境を構築します。Dockerを使う場合は仮想コンテナを使うので必要ありません。最初はあえて正当な開発の仕方でやってみます。virtualenvを入れる前に、pipというPythonのパッケージ管理ソフトウェアを入れます。Python3.4以降からはデフォルトで付属しています。なので2系に入りますが、これを入れれば2系3系どちらも使えるようになります。

sudo easy_install pip

pip --version
pip 8.1.2 from /Library/Python/2.7/site-packages/pip-8.1.2-py2.7.egg (python 2.7)

sudo pip install virtualenv
virtualenv --version
15.0.3

pipでインストールされているパッケージを確認するにはpip freezeを使います。

pip freeze

ずらずらっと出てきましたか?

virtualenvで仮想環境を構築

まず適当なディレクトリに入り、以下コマンドを打ちます。

cd /Users/kote2/Desktop/kote2/test/20160815_2 
virtualenv venv

するとvenvというディレクトリができます。これが開発環境用のディレクトリ、正確には設定が詰まった置き場所となります。
20160815_2

sourceコマンドで仮想環境をactiveにします。するとコマンドラインに(venv)というのがつくはずです。

source venv/bin/activate
(venv) 

これで一応仮想環境モードになりました。pip freezeをしてください。先ほどと違い何も入ってない状態なので出ないと思います。

pip freeze

仮想環境から出る場合はdeactivateと打ちます。

deactivate
source venv/bin/activate #再度仮想環境モードへ

ここで一旦Gitでコミットしておきましょう。今回バージョン管理はSourceTreeで行います。

MyfirstDjangoApp__Git_

Djangoのインストール

Djangoというのを改めて説明するとPythonで実装されたWebアプリケーションフレームワークで同種類の中では一番使われています。他にはFlaskとかBottleとかあります。他のフレームワークとかの違いはまだ調べてないので詳しくは以下リンクを参考にしてください。

参考
http://www.sejuku.net/blog/3713

さて、Djangoのインストールですがpipで入れていきます。仮想モードになっていますか?

pip install django==1.9.2
pip freeze #確認
Django==1.9.2

あと、後ほど使うのでdjango-bootstrap-formも入れておきます。これはbootstrap3というCSSフレームワークがあるのですが登録、修正系のフォームをbootstrapに沿った形でクラスをつけてくれるアプリになります。

pip install django-bootstrap-form
pip freeze
Django==1.10
django-bootstrap-form==3.2.1

※Djangonのバージョンも上がったようです

PyCharmのインストール

さて、いよいよWebアプリーケーションを作っていきますが、その前にPyCharmというのをご紹介します。Pythonの総合開発環境になります。自分も使ったばかりで普段はAtomとかでやっちゃうんですが、Python開発者はほぼ間違いなくPyCharmを使っているようなので慣れておくために使います。

PyCharm

なお今回作成するものとしてはQiitaのこちらの記事で紹介されているmybookというアプリになります。仮想環境のセットアップ手順は完了してるので詳しく見たい方はこちらもご参考ください。自分の記事ではこの記事を参考に、つまずいた箇所を含めて解説して、最後は本番環境にDockerで環境構築、公開まで行きたいと思います。

http://qiita.com/kaki_k/items/7b178ad39394a031b50d

さて、今ここで作業してて仮想モードに入っているものとして進めていきます。

(venv)pwd
/Users/kote2/Desktop/kote2/test/20160815_2
django-admin.py startproject mybook

mybookというディレクトリができているのを確認します。
20160815_2

ここから先はPyCharmでやっていきます。File > New Projectから開きます。すでにディレクトリがあるよと聞かれますがYesでおっけ。
This_should_not_be_shown_と_New_Project

こんなふうに作成されました。
mybook_-____Desktop_kote2_test_20160815_2_mybook__と_20160815_2_と_dockercommand_txt_—__Users_kote2_Desktop_kote2_test_20160812_と_kote2

DBの設定
mybook > setting.pyの中にある以下の行を確認。デフォルトでSQLLite3を利用します。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

データベースをマイグレ−ドします。これをやるとプロジェクト直下にdb.sqlite3というのが作成されます。

(venv)cd mybook #mybookに入る
(venv) kote2-no-iMac:mybook kote2$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying sessions.0001_initial... OK

DB管理者用のスーパーユーザーを作成します。

python manage.py createsuperuser
Username (leave blank to use 'kote2'): 
Email address: ***@gmail.com
Password: 
Password (again): 
Superuser created successfully.

ではサーバーを起動してみましょう

python manage.py runserver

http://127.0.0.1:8000/にアクセスし、このようになったら成功です。Ctr + Cでサーバーを止め、抜けられます。
Welcome_to_Django

ここで一旦Git commitしておきます。
MyfirstDjangoApp__Git_

アプリケーションの作成

以下コマンドで作成します。

python manage.py startapp cms

モデルの作成

cms > model.pyを以下のように変更してみます。

from __future__ import unicode_literals

from django.db import models

class Book(models.Model):
    """書籍"""
    name = models.CharField('書籍名', max_length=255)
    publisher = models.CharField('出版社', max_length=255, blank=True)
    page = models.IntegerField('ページ数', blank=True, default=0)

    def __str__(self):
        return self.name


class Impression(models.Model):
    """感想"""
    book = models.ForeignKey(Book, verbose_name='書籍', related_name='impressions')
    comment = models.TextField('コメント', blank=True)

    def __str__(self):
        return self.comment

mybook > setting.pyに内容を変更します。

まず、cmsをインストールしたのでプロジェクトに教えます。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'cms',
]

ついでに言語設定なんかもいじってあげます。

# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'ja'

# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Tokyo'

ここで、以下コマンドでマイグレードファイルを作成します。

python manage.py makemigrations cms
SyntaxError: Non-ASCII character '\xe6' in file /Users/kote2/Desktop/kote2/test/20160815_2/mybook/cms/models.py on line 6, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

おっと、怒られた。どうやらmodels.pyの日本語処理がうまくいってないようです。以下のおまじないをmodels.pyの一番上に入れると解決します。

# -*- coding: utf-8 -*-

無事いけました。

python manage.py makemigrations cms
Migrations for 'cms':
  cms/migrations/0001_initial.py:
    - Create model Book
    - Create model Impression

以下コマンドで作成されたマイグレードファイルがどのようなSQLになるか確認できます。

python manage.py sqlmigrate cms 0001
BEGIN;
--
-- Create model Book
--
CREATE TABLE "cms_book" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(255) NOT NULL, "publisher" varchar(255) NOT NULL, "page" integer NOT NULL);
--
-- Create model Impression
--
CREATE TABLE "cms_impression" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "comment" text NOT NULL, "book_id" integer NOT NULL REFERENCES "cms_book" ("id"));
CREATE INDEX "cms_impression_0a4572cc" ON "cms_impression" ("book_id");
COMMIT;

新しくテーブルを作ったのでマイグレートしてみます。

python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, cms, contenttypes, sessions
Running migrations:
  Rendering model states... DONE
  Applying cms.0001_initial... OK

管理サイトの有効化

さて、Djangoのいいところは管理サイトが予め付属されている点です。管理サイト上でマスターメンテナンスできるので表示させます。

python manage.py runserver

http://127.0.0.1:8000/admin/にアクセスします。ログイン画面が出て、スーパーユーザーを作成した時に作ったid,passを打ち込んで以下のように遷移できれば成功です。

サイト管理___Django_サイト管理

データをadmin上から追加する

以下を追記してhttp://127.0.0.1:8000/admin/にアクセスします。

from django.contrib import admin
from cms.models import Book, Impression

admin.site.register(Book)
admin.site.register(Impression)

今度はBooksとImpressionsが加えられています。cmsをadmin上で管理できるようになります。
サイト管理___Django_サイト管理

ここでデータの追加をしてみます。UnicodeEncodeError_at__admin_cms_book_add_
怒られましたw

UnicodeEncodeError at /admin/cms/book/add/
'ascii' codec can't encode characters in position 6-7: ordinal not in range(128)

どうやら日本語を使うと怒られるようです。ここはかなり調べたのですが先ほどcms/models.pyを編集した時、strを使用した関数を使っていましたが、そこをunicodeに変えるようです。

そこでcms/models.pyの中身を変更します。

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

class Book(models.Model):
    """書籍"""
    name = models.CharField('書籍名', max_length=255)
    publisher = models.CharField('出版社', max_length=255, blank=True)
    page = models.IntegerField('ページ数', blank=True, default=0)

    def __str__(self):
        return self.name


class Impression(models.Model):
    """感想"""
    book = models.ForeignKey(Book, verbose_name='書籍', related_name='impressions')
    comment = models.TextField('コメント', blank=True)

    def __str__(self):
        return self.comment

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

class Book(models.Model):
    """書籍"""
    name = models.CharField('書籍名', max_length=255)
    publisher = models.CharField('出版社', max_length=255, blank=True)
    page = models.IntegerField('ページ数', blank=True, default=0)

    def __unicode__(self):
        return self.name


class Impression(models.Model):
    """感想"""
    book = models.ForeignKey(Book, verbose_name='書籍', related_name='impressions')
    comment = models.TextField('コメント', blank=True)

    def __unicode__(self):
        return self.comment

参考
http://djangoproject.jp/doc/ja/1.0/ref/unicode.html

無事登録できました。
変更する_book_を選択___Django_サイト管理

cmsの一覧ページを作る

cms/admin.pyを修正します。

from django.contrib import admin
from cms.models import Book, Impression

admin.site.register(Book)
admin.site.register(Impression)

# -*- coding: utf-8 -*-
from django.contrib import admin
from cms.models import Book, Impression

# admin.site.register(Book)
# admin.site.register(Impression)


class BookAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'publisher', 'page',)  # 一覧に出したい項目
    list_display_links = ('id', 'name',)  # 修正リンクでクリックできる項目
admin.site.register(Book, BookAdmin)


class ImpressionAdmin(admin.ModelAdmin):
    list_display = ('id', 'comment',)
    list_display_links = ('id', 'comment',)
admin.site.register(Impression, ImpressionAdmin)

これでレコード全体が見れるようになりました。

変更する_book_を選択___Django_サイト管理

cmsの管理ができたところで一旦Gitコミットしておきます。次はCRUDと呼ばれるCreate(生成)、Read(読み取り)、Update(更新)、Delete(削除)の仕組みを作ります。

CRUDの作成

staticという静的なファイルを入れておくフォルダを作り、Bootstrapとjqueryを入れておきます。

http://getbootstrap.com/
http://jquery.com/

mybook/
    static/
        css/
            bootstrap-theme.css
            bootstrap-theme.css.map
            bootstrap-theme.min.css
            bootstrap-theme.min.css.map
            bootstrap.css
            bootstrap.css.map
            bootstrap.min.css
            bootstrap.min.css.map
        fonts/
            glyphicons-halflings-regular.eot
            glyphicons-halflings-regular.svg
            glyphicons-halflings-regular.ttf
            glyphicons-halflings-regular.woff
            glyphicons-halflings-regular.woff2
        js/
            bootstrap.js
            bootstrap.min.js
            npm.js
            jquery-2.2.0.min.js

このstaticフォルダに静的なファイルが有るよと知らせるためmybook > setting.pyに以下を追記します。

# 静的ファイルを共通で置く
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

なお、日本語でコメントを書くとまた怒られるのでmybook > setting.pyの行頭にもおまじないを入れます。

# -*- coding: utf-8 -*-

一覧ページを作る

pip install時に入れたbootstrapformを使います。mybook > setting.pyに以下を追記します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bootstrapform',
    'cms',
]

ビューを作ってみます。cms > view.pyに以下を書きます。

# -*- coding: utf-8 -*-
from django.shortcuts import render
from django.http import HttpResponse


def book_list(request):
    """書籍の一覧"""
    return HttpResponse('書籍の一覧')


def book_edit(request, book_id=None):
    """書籍の編集"""
    return HttpResponse('書籍の編集')


def book_del(request, book_id):
    """書籍の削除"""
    return HttpResponse('書籍の削除')

URLスキームの作成

先ほど作成したviewと紐付けるためcms > urls.pyを新規作成します。

# -*- coding: utf-8 -*-
from django.conf.urls import url
from cms import views

urlpatterns = [
    # 書籍
    url(r'^book/$', views.book_list, name='book_list'),   # 一覧
    url(r'^book/add/$', views.book_edit, name='book_add'),  # 登録
    url(r'^book/mod/(?P\d+)/$', views.book_edit, name='book_mod'),  # 修正
    url(r'^book/del/(?P\d+)/$', views.book_del, name='book_del'),   # 削除
]

作成したcms > urls.pyを今度はmybook > urls.pyの中でインクルードさせます。

# -*- coding: utf-8 -*-
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^cms/', include('cms.urls', namespace='cms')),
]

これで以下URLを入れると一文表示されるようになります。
http://127.0.0.1:8000/cms/book/
http://127.0.0.1:8000/cms/book/add/
http://127.0.0.1:8000/cms/book/mod/5/
http://127.0.0.1:8000/cms/book/del/7/

htmlを作っていく

一覧ページ用のhtmlを作っていきます。ページのヘッダー・フッターなどは共通のbase.htmlとして作り、中身をtemplatesとして作成していきます。

mybook/cms/templates/base.html
mybook/cms/templates/cms/book_list.html
mybook/cms/templates/cms/book_edit.html

まず、ベースとなるbase.htmlとbook_list.html、book_edit.htmlを作成します。

base.html

{% load staticfiles %}
<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}">
<head>
<meta charset="UTF-8">
<title>{% block title %}My books{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static 'css/bootstrap-theme.min.css' %}" rel="stylesheet">
<script src="{% static 'js/jquery-3.1.0.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% block extrahead %}{% endblock %}
</head>
<body>
<div class="container">
{% block content %}
{{ content }}
{% endblock %}
</div>
</body>
</html>

book_list.html

{% extends "base.html" %}

{% block title %}書籍の一覧{% endblock title %}

{% block extrahead %}
<style>
table {
margin-top: 8px;
}
</style>
{% endblock %}

{% block content %}
<h3 class="page-header">書籍の一覧</h3>
<a href="{% url 'cms:book_add' %}" class="btn btn-default btn-sm">追加</a>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>書籍名</th>
<th>出版社</th>
<th>ページ数</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
<td>{{ book.publisher }}</td>
<td>{{ book.page }}</td>
<td>
<a href="{% url 'cms:book_mod' book_id=book.id %}" class="btn btn-default btn-sm">修正</a>
<a href="{% url 'cms:book_del' book_id=book.id %}" class="btn btn-default btn-sm">削除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}

book_edit.html

{% extends "base.html" %}
{% load bootstrap %}

{% block title %}書籍の編集{% endblock title %}

{% block content %}
<h3 class="page-header">書籍の編集</h3>
{% if book_id %}
<form action="{% url 'cms:book_mod' book_id=book_id %}" method="post" class="form-horizontal" role="form">
{% else %}
<form action="{% url 'cms:book_add' %}" method="post" class="form-horizontal" role="form">
{% endif %}
{% csrf_token %}
{{ form|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">送信</button>
</div>
</div>
</form>
<a href="{% url 'cms:book_list' %}" class="btn btn-default btn-sm">戻る</a>
{% endblock content %}

cms/view.pyに一覧、追加、修正のビューを書きます。まず一覧を書いてみます。以下書き加えます。

・
・
・
from cms.models import Book

def book_list(request):
    """書籍の一覧"""
#    return HttpResponse('書籍の一覧')
    books = Book.objects.all().order_by('id')
    return render(request,
                  'cms/book_list.html',
                  {'books': books})   
・
・
・

追加、修正のビュー

追加と修正のビューもcms/view.pyに加えます。

from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse

from cms.models import Book
from cms.forms import BookForm
・
・
・
def book_edit(request, book_id=None):
    """書籍の編集"""
#     return HttpResponse('書籍の編集')
    if book_id:   # book_id が指定されている (修正時)
        book = get_object_or_404(Book, pk=book_id)
    else:         # book_id が指定されていない (追加時)
        book = Book()

    if request.method == 'POST':
        form = BookForm(request.POST, instance=book)  # POST された request データからフォームを作成
        if form.is_valid():    # フォームのバリデーション
            book = form.save(commit=False)
            book.save()
            return redirect('cms:book_list')
    else:    # GET の時
        form = BookForm(instance=book)  # book インスタンスからフォームを作成

    return render(request, 'cms/book_edit.html', dict(form=form, book_id=book_id))
・
・
・

さらに削除のビューを加えます。

def book_del(request, book_id):
    """書籍の削除"""
#     return HttpResponse('書籍の削除')
    book = get_object_or_404(Book, pk=book_id)
    book.delete()
    return redirect('cms:book_list')

cms/forms.pyを作り以下を記述します。

from django.forms import ModelForm
from cms.models import Book

class BookForm(ModelForm):
    """書籍のフォーム"""
    class Meta:
        model = Book
        fields = ('name', 'publisher', 'page', )

これで追加、削除等ができるようになったのでhttp://127.0.0.1:8000/cms/book/にアクセスしてみます。
書籍の一覧

書籍の感想を子モデルとして使う

書籍の感想を書き込めるように書籍idを親に子どもとして持たせてみます。cms/model.pyに書籍の感想を外部キーを持つように記述します。

class Impression(models.Model):
    """感想"""
    book = models.ForeignKey(Book, verbose_name='書籍', related_name='impressions')
    comment = models.TextField('コメント', blank=True)

次にcms/views.pyを以下のように追記します。

from django.views.generic.list import ListView
  :

class ImpressionList(ListView):
    """感想の一覧"""
    context_object_name='impressions'
    template_name='cms/impression_list.html'
    paginate_by = 2  # 1ページは最大2件ずつでページングする

    def get(self, request, *args, **kwargs):
        book = get_object_or_404(Book, pk=kwargs['book_id'])  # 親の書籍を読む
        impressions = book.impressions.all().order_by('id')   # 書籍の子供の、感想を読む
        self.object_list = impressions

        context = self.get_context_data(object_list=self.object_list, book=book)    
        return self.render_to_response(context)

htmlを作ります。

mybook/cms/templates/cms/impression_list.html
mybook/cms/templates/cms/impression_edit.html

impression_list.html

{% extends "base.html" %}

{% block title %}感想の一覧{% endblock title %}

{% block extrahead %}
<script>
$(function() {
$('.del_confirm').on('click', function () {
$("#del_pk").text($(this).attr("pk"));
$('#del_url').attr('href', $(this).attr("url"));
});
});
</script>
<style>
table {
margin-top: 8px;
}
</style>
{% endblock %}

{% block content %}
<h3 class="page-header">感想の一覧 <small>{{ book.name }}</small></h3>
<a href="{% url 'cms:impression_add' book_id=book.id %}" class="btn btn-default btn-sm">追加</a>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>コメント</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for impression in impressions %}
<tr>
<td>{{ impression.id }}</td>
<td>{{ impression.comment|linebreaksbr }}</td>
<td>
<a href="{% url 'cms:impression_mod' book_id=book.id impression_id=impression.id %}" class="btn btn-default btn-sm">修正</a>
<button class="btn btn-default btn-sm del_confirm" data-toggle="modal" data-target="#deleteModal" pk="{{ impression.id }}" url="{% url 'cms:impression_del' book_id=book.id impression_id=impression.id %}">削除</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>

{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li><a href="?page={{ page_obj.previous_page_number }}">&laquo;</a></li>
{% else %}
<li class="disabled"><a href="#">&laquo;</a></li>
{% endif %}
{% for linkpage in page_obj.paginator.page_range %}
{% ifequal linkpage page_obj.number %}
<li class="active"><a href="#">{{ linkpage }}</a></li>
{% else %}
<li><a href="?page={{ linkpage }}">{{ linkpage }}</a></li>
{% endifequal %}
{% endfor %}
{% if page_obj.has_next %}
<li><a href="?page={{ page_obj.next_page_number }}">&raquo;</a></li>
{% else %}
<li class="disabled"><a href="#">&raquo;</a></li>
{% endif %}
</ul>
{% endif %}

<div>
<a href="{% url 'cms:book_list' %}" class="btn btn-default btn-sm">戻る</a>
</div>

{# 削除を確認するモーダル ダイアログ #}
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="deleteModalLabel">確認</h4>
</div>
<div class="modal-body">
<p>ID: <span id="del_pk"></span> を削除しますか?</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" id="del_url">OK</a>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
{% endblock content %}

impression_edit.html

{% extends "base.html" %}
{% load bootstrap %}

{% block title %}感想の編集{% endblock title %}

{% block content %}
<h3 class="page-header">感想の編集</h3>
{% if impression_id %}
<form action="{% url 'cms:impression_mod' book_id=book_id impression_id=impression_id %}" method="post" class="form-horizontal" role="form">
{% else %}
<form action="{% url 'cms:impression_add' book_id=book_id %}" method="post" class="form-horizontal" role="form">
{% endif %}
{% csrf_token %}
{{ form|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">送信</button>
</div>
</div>
</form>
<a href="{% url 'cms:impression_list' book_id=book_id %}" class="btn btn-default btn-sm">戻る</a>
{% endblock content %}

書籍感想の追加修正フォーム

最後に書籍感想の追加修正フォームを作成します。cms/forms.pyに追記します。

・
・
・
from cms.models import Book, Impression
・
・
・
class ImpressionForm(ModelForm):
    """感想のフォーム"""
    class Meta:
        model = Impression
        fields = ('comment', )

cms/views.py に以下のように追加します

・
・
・
from cms.models import Book, Impression
from cms.forms import BookForm, ImpressionForm
・
・
・
def impression_edit(request, book_id, impression_id=None):
    """感想の編集"""
    book = get_object_or_404(Book, pk=book_id)  # 親の書籍を読む
    if impression_id:   # impression_id が指定されている (修正時)
        impression = get_object_or_404(Impression, pk=impression_id)
    else:               # impression_id が指定されていない (追加時)
        impression = Impression()

    if request.method == 'POST':
        form = ImpressionForm(request.POST, instance=impression)  # POST された request データからフォームを作成
        if form.is_valid():    # フォームのバリデーション
            impression = form.save(commit=False)
            impression.book = book  # この感想の、親の書籍をセット
            impression.save()
            return redirect('cms:impression_list', book_id=book_id)
    else:    # GET の時
        form = ImpressionForm(instance=impression)  # impression インスタンスからフォームを作成

    return render(request,
                  'cms/impression_edit.html',
                  dict(form=form, book_id=book_id, impression_id=impression_id))

感想の削除ビュー

感想の削除はBootstrapのモーダルダイアログを使用して確認を入れてみます。cms/views.py に以下のように追加します

def impression_del(request, book_id, impression_id):
    """感想の削除"""
    impression = get_object_or_404(Impression, pk=impression_id)
    impression.delete()
    return redirect('cms:impression_list', book_id=book_id)

URLスキームの編集

cms/urls.pyを以下のように追記します。

・
・
・
    # 感想
    url(r'^impression/(?P\d+)/$', views.ImpressionList.as_view(), name='impression_list'),  # 一覧
    url(r'^impression/add/(?P\d+)/$', views.impression_edit, name='impression_add'), # 登録
    url(r'^impression/mod/(?P\d+)/(?P\d+)/$', views.impression_edit, name='impression_mod'),  # 修正
    url(r'^impression/del/(?P\d+)/(?P\d+)/$', views.impression_del, name='impression_del'),   # 削除

最後にmybook/cms/templates/cms/book_list.htmlに一行、感想へのリンクを追加します。

<td> 
    <a href="{% url 'cms:book_mod' book_id=book.id %}" class="btn btn-default btn-sm">修正</a> 
    <a href="{% url 'cms:book_del' book_id=book.id %}" class="btn btn-default btn-sm">削除</a> 
    <a href="{% url 'cms:impression_list' book_id=book.id %}" class="btn btn-default btn-sm btn-primary">感想の一覧</a> 
 </td>

このようにリンクが増えました。
書籍の一覧

以上でアプリケーションはだいたい完成です。GitでCommit!

MyfirstDjangoApp__Git_

2ではDockerを使ってローカル上での確認、deploybotを使って本番サーバーへの公開を行いたいと思います。

[1]Python,Django,Dockerで作るWEBアプリケーション
[2]Python,Django,Dockerで作るWEBアプリケーション

Share on LinkedIn
Pocket

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

Favorite Post

Recent Comments