はじめに
Vagrant+Python3+Django環境でHelloWorldの続編です。
Hello Worldを出したら、とりあえずデータ登録(C)、参照(R)、更新(U)、削除(D)をしたかったので、todoアプリ的なものを作りました。
ただただ、一覧表示、登録、更新・削除、をするアプリです。
うまくいけば、環境構築含め1日あれば作れる規模感です
基本的には、はじめての Django アプリ作成にそって進めます(多少アレンジが入ります)
アプリケーションをはじめよう
todoアプリケーションを作成する
※python3で実行しているので注意!!
[vagrant@localhost mysite]$ pwd
/home/vagrant/app/mysite
[vagrant@localhost mysite]$ python3 manage.py startapp todos
[vagrant@localhost mysite]$ ls -ltr todos/
total 20
-rw-r--r--. 1 vagrant vagrant 63 Jul 30 16:31 views.py
-rw-r--r--. 1 vagrant vagrant 60 Jul 30 16:31 tests.py
-rw-r--r--. 1 vagrant vagrant 57 Jul 30 16:31 models.py
drwxr-xr-x. 1 vagrant vagrant 102 Jul 30 16:31 migrations
-rw-r--r--. 1 vagrant vagrant 0 Jul 30 16:31 __init__.py
-rw-r--r--. 1 vagrant vagrant 85 Jul 30 16:31 apps.py
-rw-r--r--. 1 vagrant vagrant 63 Jul 30 16:31 admin.py
ちなみに、「プロジェクト(mysite)」と「アプリケーション(todos)」の違いはこんな感じ
プロジェクトとアプリケーション
プロジェクトとアプリケーションの違いとは何でしょうか?アプリケーションとは、実際に何らかの処理を行う Web アプリケーションを指します。例えばブログシステムや公開レコードのデータベース、単純な投票アプリといった具合です。プロジェクトとは、あるウェブサイト向けに設定とアプリケーションを集めたものです。一つのプロジェクトには複数のアプリケーションを入れられ ます。また、一つのアプリケーションは複数のプロジェクトで使えます。
アプリケーションができたところで、新しい画面をつくってみよう。
まずはtodos/views.py
を作成
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world! new page!")
続いて、todos/urls.py
を作成し、urlとviews.pyをマッピング
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
最後に、mysite/urls.py
を修正して、プロジェクト(mysite)のurlマッピングに上記を追加する
from django.contrib import admin
from django.urls import include, path ## ここに「include」を追加
urlpatterns = [
path('admin/', admin.site.urls),
path('todos/', include('todos.urls')), ## ここを追加
]
これで、動作確認する。
python3 manage.py runserver 0:8000
を実行してから、http://localhost:18000/todos/
を確認
寂しい画面で**Hello, world! new page!**って出たと思います。
DataBaseを利用しよう
今回は楽をしてSQLiteを使います(デフォルト設定でSQLiteになっています)
え?ホント?って方はmysite/setting.py
を確認。
以下みたいに'ENGINE': 'django.db.backends.sqlite3',
ってなっているはず。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
ちょっと試しにマイグレーションしてみる(デフォルトの設定が読み込まれる)
[vagrant@localhost mysite]$ python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
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 auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK
migrate
でmysite/setting.py INSTALLED_APPS
の設定を読み込んで、プロジェクトで必要なデータベースを作ってくれます。
では、今回作りたいモデルを作成します
from django.db import models
# Create your models here.
class Todo(models.Model):
todo_id = models.CharField(unique=True, max_length=5)
title = models.CharField(max_length=50)
main_text = models.CharField(max_length=300)
update_date = models.DateTimeField('date published')
※fieldのリファレンスはModel field referenceを参考ください
またまた、mysite/setting.py
を修正して、上記をプロジェクトに反映
INSTALLED_APPS = [
'todos.apps.TodosConfig', ## ここを追加
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
テーブルをつくる(モデルの変更分を反映)
[vagrant@localhost mysite]$ python3 manage.py makemigrations todos
Migrations for 'todos':
todos/migrations/0001_initial.py
- Create model Todo
これでテーブルができた!!
CRUDを作ろう
まずはAPIを利用して、いろいろDBを操作してみよう。
python3 manage.py shell
でPython 対話シェルを起動。
[vagrant@localhost mysite]$ python3 manage.py shell
Python 3.7.0 (default, Jul 26 2018, 15:06:09)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from todos.models import Todo
>>> from django.utils import timezone
>>>
>>> Todo.objects.all()
<QuerySet []>
>>>
>>> t = Todo(todo_id=1, title="hoge", main_text="fuga", update_date=timezone.now())
>>> t.save()
>>> Todo.objects.all()
<QuerySet [<Todo: Todo object (1)>]>
>>>
>>> t1 = Todo.objects.get(todo_id=1)
>>> t1.title = 'hogehoge'
>>> t1.save()
>>>
>>> t2 = Todo.objects.get(todo_id=1)
>>> t2.title
'hogehoge'
>>>
>>> t2.delete()
(1, {'todos.Todo': 1})
>>>
>>> Todo.objects.all()
<QuerySet []>
>>>
これで参照、登録、更新、削除が一式できた
-
Todo.objects.all()
で全件取得、Todo.objects.get(todo_id=1)
でtodo_id=1と一致するものを取得。 -
Todo(todo_id=1, title="hoge", main_text="fuga", update_date=timezone.now()).save()
で登録。重複していたら更新になる。(上記例では、変数においてからsave()
している) -
Todo.objects.get(todo_id=1).delete()
で削除
これをアプリケーションへ反映
todos/views.py
でCRUD用の画面を作成
from django.http import HttpResponse
from django.utils import timezone
from todos.models import Todo
def index(request):
return HttpResponse("Hello, world! new page!")
def create(request):
t = Todo(todo_id=1, title="hoge", main_text="fuga", update_date=timezone.now())
t.save()
return HttpResponse("insert!")
def read(request):
t = Todo.objects.get(todo_id=1)
return HttpResponse('todo_id:%s, title:%s, main_text:%s.' % (t.todo_id, t.title, t.main_text))
def update(request):
t = Todo.objects.get(todo_id=1)
t.title = 'changed!'
t.save()
return HttpResponse('update!')
def delete(request):
t = Todo.objects.get(todo_id=1)
t.delete()
return HttpResponse('delete!')
もちろん、todos/urls.py
も修正
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('create', views.create, name='create'),
path('read', views.read, name='read'),
path('update', views.update, name='update'),
path('delete', views.delete, name='delete'),
]
これでアプリケーションを起動(python3 manage.py runserver 0:8000
)
以下でそれぞれがちゃんと動いていることを確認
-
http://localhost:18000/todos/create
→ **insert!**と寂しく表示(裏でデータが登録されてる) -
http://localhost:18000/todos/read
→ **todo_id:1, title:hoge, main_text:fuga.**と出力。さっき登録したデータが表示できた -
http://localhost:18000/todos/update
→ **update!**と寂しく表示(裏でデータが更新されてる) -
http://localhost:18000/todos/read
→ **todo_id:1, title:changed!, main_text:fuga.**と出力。更新されていることを確認 -
http://localhost:18000/todos/delete
→ **delete!**と寂しく表示(裏でデータが削除されてる) -
http://localhost:18000/todos/read
→ 対象のデータがなくてエラー -
http://localhost:18000/todos/create
→ 次の内容のためにInsertしておく
Templatesを利用しよう
さすがに雑すぎるので、このCRUDを(cssあてたら)ちゃんとした画面にする
from django.http import HttpResponse
from django.utils import timezone
from django.shortcuts import render
from todos.models import Todo
def index(request):
todo_list = Todo.objects.all()
context = {'todo_list': todo_list}
return render(request, 'todos/index.html', context)
これで、全件データ取得して、そのデータをtodo_list
というキー値でテンプレートに渡す感じ
では、テンプレート側を作成する。
新規でtodos/templates/todos/index.html
を作る
<html>
<head></head>
<body>
<a href="/todos/new/">登録</a>
{% if todo_list %}
<table>
<tr>
<td>id</td>
<td>title</td>
<td></td>
</tr>
{% for todo in todo_list %}
<tr>
<td><a href="/todos/{{ todo.todo_id }}/">{{ todo.todo_id }}</a></td>
<td>{{ todo.title }}</td>
<td><a href="/todos/delete/{{ todo.todo_id }}/">削除</a></td>
</tr>
{% endfor %}
</table>
{% else %}
<p>No todos are available.</p>
{% endif %}
</body>
</html>
こんな感じ
-
{% if todo_list %}
はtodo_list
が空じゃなければ中身の処理を実施 -
{% for todo in todo_list %}
これでfor文を実行(1つ1つをtodoという変数においている) -
{{ todo.todo_id }}
で値の出力
テンプレートとの紐付けもできた!
CRUDを完成させよう
今まで使ったもの+αでCRUDを完成させる
まずはtodos/urls.py
、todos/views.py
で一式画面紐付けさせる
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('new/', views.new, name='new'),
path('add/', views.add, name='add'),
path('<int:todo_id>/', views.detail, name='detail'),
path('add/<int:todo_id>/', views.update, name='update'),
path('delete/<int:todo_id>/', views.delete, name='delete'),
]
<int:todo_id>
と書けば、パラメータを渡せる。views.py
を見ればイメージつくはず。
続いて
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import timezone
from django.urls import reverse
from django.shortcuts import render
from todos.models import Todo, TodoForm
def index(request):
todo_list = Todo.objects.all()
context = {'todo_list': todo_list}
return render(request, 'todos/index.html', context)
def new(request):
return render(request, 'todos/new.html')
def add(request):
t1 = Todo()
t1.todo_id = len(Todo.objects.order_by('-todo_id'))+1
t1.update_date = timezone.now()
t = TodoForm(request.POST, instance=t1)
t.save()
return HttpResponseRedirect(reverse('index'))
def detail(request, todo_id):
todo = Todo.objects.get(todo_id=todo_id)
context = {'todo': todo}
return render(request, 'todos/new.html', context)
def update(request, todo_id):
t1 = Todo.objects.get(todo_id=todo_id)
t = TodoForm(request.POST, instance=t1)
t.save()
return HttpResponseRedirect(reverse('index'))
def delete(request, todo_id):
t = Todo.objects.get(todo_id=todo_id)
t.delete()
return HttpResponseRedirect(reverse('index'))
新しい内容をつらつらと書いていくと、
-
HttpResponseRedirect
→ 登録完了後にリダイレクトする(reverse
を使ってurls.py
のnameと紐づけている) -
TodoForm
→form
とmodel
の結びつける。(使ってみたものの、使いこなせなかった。。。) -
def detail(request, todo_id):
→urls.py
の<int:todo_id>
と結びつける。urlをパラメータとして利用できる -
request.POST
→ POSTデータを利用できる。request.POST['key値']
とすればパラメータを取得できるが、そのkey値
がないとErrorになるので注意
ということで、新しく出てきたtodos/models.py
とtodos/templates/todos/new.html
を実装して終了
from django.db import models
from django.forms import ModelForm
# Create your models here.
class Todo(models.Model):
todo_id = models.CharField(primary_key=True, max_length=5)
title = models.CharField(max_length=50)
main_text = models.CharField(max_length=300)
update_date = models.DateTimeField('date published')
class TodoForm(ModelForm):
class Meta:
model = Todo
fields = ['todo_id', 'title', 'main_text', 'update_date']
exclude = ['todo_id', 'update_date']
exclude = ['todo_id', 'update_date']
を指定することで画面に表示されないデータを表現できる、チェックを除外できる
とあるが、よくわかっていない。。。
最後にnew.html
を作る
<html>
<head></head>
<body>
<form action="/todos/add/{% if todo %}{{ todo.todo_id }}/{% endif %}" method="post">
{% csrf_token %}
title: <input type="text" name="title" value="{{ todo.title }}"/>
main: <input type="text" name="main_text" value="{{ todo.main_text }}"/>
<input type="hidden" name="todo_id" value="{{ todo.todo_id }}"/>
<input type="submit" value="登録"/>
</form>
</body>
</html>
これで一式できたので、ポチポチしてみてください!
おわりに
最初にも書いた通り以下に沿って作っています。
なので、今これを見るとすんなり入ってくるのでは?と思います。
※まだ、一部(ModelForm
など)使いこなせていないので、この辺りは別途整理しようと思っています。
結構大変だったな、と思いつつも3〜5hほどでここまでできたので、これを参考にしてもらたら半日でできる(と信じてます)
まだまだ、勉強中なので、誤りとか、要望とかあればご指摘いただけると嬉しいです!