[Django]データベースの管理ツール

Djangoにはデータベースの管理ツールが用意されていて、それを使ってWeb上でテーブルなどの編集が行える

### 管理者の作成
$ python manage.py createsuperuser
Username (leave blank to use ‘vagrant’): admin
Email address: ****@gmail.com
Password:
Password (again):
Superuser created successfully.

### モデルを管理ツールで利用できるようにする
– アプリケーションのadmin.pyで編集する
/hello/admin.py

from django.contrib import admin
from .models import Friend

admin.site.register(Friend)

### adminログイン
http://192.168.33.10:8000/admin


– AUTHENTICATION AND AUTHORIZATIONに「Groups」「Users」モデルが用意されている
– 下がmigrationで作成したテーブル

### 管理ツールからレコードの作成

### 利用者の管理ページ
add userから追加する

まあ、便利といえば便利

[Django]データベースの初期設定

Djangoで使えるデータベース: MySQL, PostgreSQL, SQLite
Djangoはプロジェクトにスクリプトを書くと、テーブルを自動生成する

### データベースの設定
– プロジェクトフォルダのsettings.pyを開く

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

– ENGINE: アクセスに使われるプログラム名
– NAME: データベース名 SQLiteの場合、パスを設定する

– mysqlの場合

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'databasename',
        'USER': 'root',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

– PostgreSQLの場合

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'databasename',
        'USER': 'root',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

### モデルの作成
– アプリケーションのmodels.pyを開く
/hello/models.py

from django.db import models

# Create your models here.

モデルクラスの作成
– 書き方はFormクラスとほぼ同じ
– django.db.modelsにあるModelクラスを継承している
– def __str__(self)はテキストの内容を返すもの

from django.db import models

class Friend(models.Model):
	name = models.CharField(max_length=100)
	mail = models.EmailField(max_length=200)
	gender = models.BooleanField()
	age = models.IntegerField(default=0)
	birthday = models.DateField()

	def __str__(self):
		return '<Friend:id=' + str(self.id) + ', ' + self.name + '(' + self.age + ')>'

### マイグレーションファイルの作成
$ python manage.py makemigrations helloMigrations for ‘hello’:
hello/migrations/0001_initial.py
– Create model Friend

### マイグレーションの実行
$ python manage.py migrate

作成されたmigrantionファイル
/hello/migrations/0001_initial.py

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Friend',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=100)),
                ('mail', models.EmailField(max_length=200)),
                ('gender', models.BooleanField()),
                ('age', models.IntegerField(default=0)),
                ('birthday', models.DateField()),
            ],
        ),
    ]

– Migrationクラスはdjango.db.migraitonsにあるクラス
– operationsが実行するクラス

[Django]formモジュール2

### ChoiceField
/hello/forms.py

class HelloForm(forms.Form):
	data = [
		('one', 'item 1'),
		('two', 'item 2'),
		('three', 'item 3')
	]
	choice = forms.ChoiceField(label='Choice', choices=data)

/hello/views.py

	def post(self, request):
		ch = request.POST['choice']
		self.params['result'] = 'you selected: "' + ch + '".'
		self.params['form'] = HelloForm(request.POST)
		return render(request, 'hello/index.html', self.params)

### ラジオボタン
– radioもChoiceFieldを使って作成する
– forms.ChoiceField(choices=value, widget=forms.RadioSelect())
/hello/forms.py

class HelloForm(forms.Form):
	data = [
		('one', 'item 1'),
		('two', 'item 2'),
		('three', 'item 3')
	]
	choice = forms.ChoiceField(label='radio', choices=data, widget=forms.RadioSelect())

### 選択リスト
– forms.ChoiceField(choices=value, widget=forms.Select())
/hello/forms.py

class HelloForm(forms.Form):
	data = [
		('one', 'item 1'),
		('two', 'item 2'),
		('three', 'item 3'),
		('four', 'item 4'),
		('five', 'item 5'),
	]
	choice = forms.ChoiceField(label='radio', choices=data, widget=forms.Select(attrs={'size':5}))

### 複数選択
– forms.MultipleChoiceField(choices=value, widget=forms.SelectMultiple())
/hello/forms.py

class HelloForm(forms.Form):
	data = [
		('one', 'item 1'),
		('two', 'item 2'),
		('three', 'item 3'),
		('four', 'item 4'),
		('five', 'item 5'),
	]
	choice = forms.MultipleChoiceField(label='radio', choices=data, widget=forms.SelectMultiple(attrs={'size':6}))

– 複数項目はgetlistで取得する
/hello/views.py

	def post(self, request):
		ch = request.POST.getlist('choice')
		self.params['result'] = 'you selected: "' + str(ch) + '".'
		self.params['form'] = HelloForm(request.POST)
		return render(request, 'hello/index.html', self.params)

views.py側でfor文をかける

	def post(self, request):
		ch = request.POST.getlist('choice')
		result = '<ol class="list-group"><b>selected:</b>'
		for item in ch:
			result += '<li class="list-group-item">' + item + '</li>'
		result += '</ol>'
		self.params['result'] = result
		self.params['form'] = HelloForm(request.POST)
		return render(request, 'hello/index.html', self.params)

forms.pyでデータ型を用意して、views.pyでそれを利用するってイメージか。

[Django]formモジュール1

## formモジュール
### CharField
– input type=”text”を生成
– requred, min_length, max_length

### EmailField
– input type=”emailを生成

### IntegerField, FloatField
– input type=”number”を生成

### URLField
– input type=”url”

### 日時に関するフィールド
– DateField, TimeField, DateTimeField

### BooleanField
/hello/forms.py

class HelloForm(forms.Form):
	check = forms.BooleanField(label='Checkbox', required=False)

/hello/templates/hello/index.html

<h1 class="display-4 text-primary">{{title}}</h1>
	<p class="h5 mt-4">{{result|safe}}</p>
	<form action="{% url 'index' %}" method="post">
		{% csrf_token %}
		<table>
			{{ form.as_p }}
			<tr>
				<td></td>
				<td><input class="btn btn-primary my-2" type="submit" value="click"></td>
			</tr>
		</table>
	</form>

/hello/views.py

class HelloView(TemplateView):

	def __init__(self):
		self.params = {
			'title': 'Hello',
			'form': HelloForm(),
			'result': None
		}

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

	def post(self, request):
		if('check' in request.POST):
			self.params['result'] = 'Checked!!'
		else:
			self.params['result'] = 'not checked...'
		self.params['form'] = HelloForm(request.POST)
		return render(request, 'hello/index.html', self.params)

### NullBooleanField
– Yes, No, Unknownの項目を持つメニュー

class HelloForm(forms.Form):
	check = forms.NullBooleanField(label='Checkbox')
	def post(self, request):
		chk = request.POST['check']
		self.params['result'] = 'you selected: "' + chk + '".'
		self.params['form'] = HelloForm(request.POST)
		return render(request, 'hello/index.html', self.params)

[Django]ビュー関数をクラス化

– TemplateViewクラスを継承して定義する
– TemplateViewクラスはViewクラスの派生クラス

class クラス名 (TemplateView)
	
	def get(self, request):
		...GET時の処理...

	def post(self, request):
		...GET時の処理...

### HelloViewクラス
/hello/views.py

from django.shortcuts import render
from django.http import HttpResponse
from django.views.generic import TemplateView
from .forms import HelloForm

class HelloView(TemplateView):

	def __init__(self):
		self.params = {
			'title': 'Hello',
			'message': 'your data:',
			'form': HelloForm()
		}

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

	def post(self, request):
		msg = 'あなたは、<b>' + request.POST['name'] + \
			'(' + request.POST['age'] + \
			')</b>さんです。<br>メールアドレスは<b>' + request.POST['mail'] + \
			'</b>ですね。'
		self.params['message'] = msg
		self.params['form'] = HelloForm(request.POST)
		return render(request, 'hello/index.html', self.params)

– __init__は初期化メソッド

/hello/urls.py

from django.conf.urls import url
from .views import HelloView

urlpatterns = [ 
	url(r'', HelloView.as_view(), name='index'),
]

フォームは関数で書くよりもクラス推奨のようです。

[Django]フィールドをタグで整形

Djangoのフォームクラスには出力機能が備えられている
– form.as_table
– form.as_p
– form.as_ul

<form action="{% url 'index' %}" method="post">
		{% csrf_token %}
		<table>
		{{ form.as_table }}
			<tr>
				<td></td>
				<td><input class="btn btn-primary" type="submit" value="click"></td>
			</tr>
		</table>
	</form>

– form.as_tableだけだとtr, tdタグしか出力されない為、tableタグを用意する必要がある

## Bootstrapクラス
### forms.py
/hello/forms.py

class HelloForm(forms.Form):
	name = forms.CharField(label='name', widget=forms.TextInput(attrs={'class':'form-control'}))
	mail = forms.CharField(label='mail', widget=forms.TextInput(attrs={'class':'form-control'}))
	age = forms.IntegerField(label='age', widget=forms.NumberInput(attrs={'class':'form-control'}))

– TextInputはformsはinput type=”text”
– NumberInputはformsはinput type=”number”

### index.html
/hello/templates/hello/index.html

<form action="{% url 'index' %}" method="post">
		{% csrf_token %}
		{{ form.as_table }}
		<input class="btn btn-primary my-2" type="submit" value="click">
	</form>

attributeは絶対に実装するので、このwidgetの書き方は必須です。

[Django]フォーム機能

### フォームクラス
Djangoに予め用意されているフォームクラスを使う
– アプリケーションフォルダ内にforms.pyを作成する

/hello/forms.py

from django import forms

class HelloForm(forms.Form):
	name = forms.CharField(label='name')
	mail = forms.CharField(label='mail')
	age = forms.IntegerField(label='age')

– Formクラスはform.Formというクラスを継承している
– クラス内には用意するフィールドを変数として用意する
– forms.CharFieldはテキスト、forms.IntegerFieldは整数値

### views.py
/hello/views.py

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

def index(request):
	params = {
		'title':'Hello',
		'message':'your data',
		'form': HelloForm(),
	}
	if (request.method == 'POST'):
		params['message'] = 'name: ' + request.POST['name'] + \
			"<br>mail: " + request.POST['mail'] + \
			"<br>age: " + request.POST['age']
		params['form'] = HelloForm(request.POST)
	return render(request, 'hello/index.html', params)

– 初期値は’form’: HelloForm()とし、送信後はparams[‘form’] = HelloForm(request.POST)として上書きしている

### index.html
/hello/templates/hello/index.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>{{title}}</title>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossoorigin="anonymous">
</head>
<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p class="h5 mt-4">{{message|safe}}</p>
	<form action="{% url 'index' %}" method="post">
		{% csrf_token %}
		{{ form }}
		<input class="btn btn-primary" type="submit" value="click">
	</form>
</body>
</html>

– formの具体的内容は既に作成済の為、{{ form }}のみ記載
– {{messsage|safe}}はエスケープ処理なし

### urls.py
/hello/urls.py

urlpatterns = [ 
	path('', views.index, name='index'),
]

[Django]フォームの送信

### index.html
/hello/templates/hello/index.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>{{title}}</title>
	<link rel="stylesheet" type="text/css" href="{% static 'hello/css/styles.css' %}">
</head>
<body>
	<h1>{{title}}</h1>
	<p>{{msg}}</p>
	<form action="{% url 'form' %}" method="post">
		{% csrf_token %}
		<label for="msg">message: </label>
		<input id="msg" type="text" name="msg">
		<input type="submit" value="click">
	</form>
</body>
</html>

– urls.pyにnameの’form’を追加する
– {% csrf_token %}はCSRF対策

### views.py
/hello/views.py

def index(request):
	params = {
		'title':'Hello/Index',
		'msg':'what is your name?',
		'goto':'next',
	}
	return render(request, 'hello/index.html', params)

def form(request):
	msg = request.POST['msg']
	params = {
		'title':'Hello/Form',
		'msg':'hello ' + msg + '!',
	}
	return render(request, 'hello/index.html', params)

### urls.py
/hello/urls.py

urlpatterns = [ 
	path('', views.index, name='index'),
	path('form', views.form, name='form'),
]

### Bootstrap
– bootstrapを使ったデザイン

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>{{title}}</title>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossoorigin="anonymous">
</head>
<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p class="h5 mt-4">{{msg}}</p>
	<form action="{% url 'form' %}" method="post">
		{% csrf_token %}
		<div class="form-group">
			<label for="msg">message: </label>
			<input id="msg" type="text" class="form-control" name="msg">
		</div>
		<input class="btn btn-primary" type="submit" value="click">
	</form>
</body>
</html>

ほう

[Django]静的ファイルの利用

### staticフォルダ
– 静的ファイルは各アプリケーションのstaticフォルダに配置する
– ここではstatic, hello, cssフォルダ配下にcssファイルを作成する

/hello/static/hello/css/styles.css

body {
	color: gray;
	font-size: 16pt;
}
h1 {
	color: red;
	opacity: 0.2;
	font-size: 60pt;
	margin-top: -20px;
	margin-bottom: 0px;
	text-align: right;
}
p {
	margin: 10px;
}
a {
	color: blue;
	text-decoration: none;
}

### index.html
/hello/templates/hello/index.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>{{title}}</title>
	<link rel="stylesheet" type="text/css" href="{% static 'hello/css/styles.css' %}">
</head>
<body>
	<h1>{{title}}</h1>
	<p>{{msg}}</p>
	<p><a href="{% url goto %}">{{goto}}</a></p>
</body>
</html>

– 静的ファイルをロードする際にはテンプレートファイルで{% load static %}と書く
– staticファイルの読み込みは{% static ‘hello/css/styles.css’ %}と書く

なんじゃこりゃ

[Django]複数ページの移動

### テンプレート側
/hello/templates/hello/index.html

<body>
	<h1>{{title}}</h1>
	<p>{{msg}}</p>
	<p><a href="{% url goto %}">{{goto}}</a></p>
</body>
</html>

– {% %}はテンプレートタグ
– {% url ${name} %}で、urls.pyで指定しているnameのパスに遷移する

### views.py
/hello/views.py

def index(request):
	params = {
		'title':'Hello/Index',
		'msg':'this is sample page.',
		'goto':'next',
	}
	return render(request, 'hello/index.html', params)

def next(request):
	params = {
		'title':'Hello/Index',
		'msg':'this is another page.',
		'goto':'index',
	}
	return render(request, 'hello/index.html', params)

### urls.py
/hello/urls.py

urlpatterns = [ 
	path('', views.index, name='index'),
	path('next', views.next, name='next'),
]

http://192.168.33.10:8000/hello/

http://192.168.33.10:8000/hello/next

{% %}のテンプレートタグがやや特殊な動きをします。