django入門

django入門

目次

1 概要

1.1 djangoの概要

  • djangoは2019/11でPythonで最もユーザー数が多いとされているWebアプリケーションフレームワーク
  • 最新のdjangoはpython3系をサポートらしい

1.2 この文書の概要

  • django関係の入門者用文書
  • Ubuntu19系をを利用して最新のdjangoを使って確認してますが、他のOSでもほぼ同じ手順で作成可能
  • 実際の作業部分を真似しやすいように作成
  • 学習手順として、実際やってみてから、修正したり調べた方が効率良いケースが多い為
  • ベースはdjangoの本家の文書なので、この文書のライセンスもdjangoの本家と同じとします。

2 リンク

2.1 本家

Django ドキュメント (version 2.2)
https://docs.djangoproject.com/ja/2.2/
入門文書(version 2.2)
https://docs.djangoproject.com/ja/2.2/intro/
本家ドキュメント目次
https://docs.djangoproject.com/ja/2.2/contents/
本家ドキュメント索引
https://docs.djangoproject.com/ja/2.2/genindex/
モジュール索引
https://docs.djangoproject.com/ja/2.2/py-modindex/

3 インストール

  • 2019/10月頃、Ubuntuのパッケージのdjangoは1系統であるため、Ubuntuのパッケージによるインストールはお勧めしません。
  • WindowsやMacの場合、Ubuntuの仮想環境や模擬環境であれば、同じ操作で可能だと思います。

3.1 以下の操作を行っている動画


3.2 pipによるローカルへのインストール手順

3.2.1 必要パッケージのインストール

sudo apt install pip3

3.2.2 ローカルディレクトリへのインストール

  • ホームディレクトリの python3/dist-packages ディレクトリにインストールする場合
  • 下のコマンドを実行しパッケージ関係のファイルをインストール
pip3 install Django -t ~/python3/dist-packages
  • インストールしたファイルを検索パスで検索できるように以下の設定を ~/.bashrcに追加し、再起動するか、端末を開きなおし、設定を読み込んだ状態にする
export PYTHONPATH="$HOME/python3/dist-packages:$PYTHONPATH"
export PATH="$HOME/python3/dist-packages/bin:$PATH"
alias python=python3

4 ローカルディレクトリへのインストールしたものを最新のバージョンにアップグレード

4.1 以下の操作を行っている動画


4.2 アップグレードするコマンド

pip3 install --upgrade Django -t ~/python3/dist-packages

5 最初のdjangoアプリ

5.1 以下の操作を行っている動画


5.2 現状のdjangoのバージョン確認

python -m django --version
  • 2019/11/10現在で 2.2.7 の出力が得られています

5.3 python自体のバージョン確認

python --version
  • 2019/11/10現在で Python 3.7.5rc1 の出力が得られています

5.4 プロジェクトの作成

  • 以下のコマンドで mysite という名前のプロジェクトを作成できます。
  • mysiteというディレクトリが作成され、その中にディレクトリや各種ファイルが生成されます
  • プロジェクト名は mysite である必用はなく別の名前でもOK
django-admin startproject mysite

5.5 生成した空プロジェクトを動かしてブラウザで確認する方法

  • 以下のコマンドでプロジェクトのルートにカレントディレクトリを移動
cd mysite
  • 以下のコマンドでサーバーを起動
  • デフォルトで http://127.0.0.1:8000/ で動きます
  • ポート番号とかはオプションで変更可能
  • http://127.0.0.1:8000/ をブラウザで開くとデフォルト画面が表示されます。
python manage.py runserver

6 djangoアプリ2(Hello World)

  • https://docs.djangoproject.com/ja/2.2/intro/ がお勧め
  • 上の本家の文書の手順をベースとして簡略化し、多少アレンジした内容になってます。
  • 指定のurlにアクセスすると、指定の文字が表示されるものを作ります

6.1 以下の操作を行っている動画


6.2 指定のurlで指定の文字が表示されるウェッブアプリを作成する手順

6.2.1 mytest001というアプリを作成し、その中にmypageというアプリケーションを作成

django-admin startproject mytest001
cd mytest001
python manage.py startapp mypage

6.2.2 mypage/views.py を以下に変更します。

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world!")

6.2.3 mypage/urls.py には以下を記述します

from django.urls import path

from . import views

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

6.2.4 mytest001/urls.py に 以下の変更を行う

from django.contrib import admin
from django.urls import include, path

from django.views.generic.base import RedirectView

urlpatterns = [
    path('mypage/', include('mypage.urls')),
    path('', RedirectView.as_view(url='/mypage/')),
    path('admin/', admin.site.urls),
]

6.2.5 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

7 djangoアプリ3(テンプレートの利用)

7.1 以下の操作を行っている動画


7.2 テンプレートファイルを利用して表示させるための手順

7.2.1 テンプレートファイルを入れるディレクトリの準備

  • プロジェクトのルート(manage.py があるディレクトリにあるmypageディレクトリ)に templates/mypage ディレクトリを作成します。
  • 以下のコマンド実行前に プロジェクトのルート(manage.py があるディレクトリ)に移動してから実行
mkdir -p mypage/templates/mypage

7.2.2 プロジェクトのセッティング修正

  • INSTALLED_APPS 以外の部分は変更しないでください。
  • mytest001/settings.py の INSTALLED_APPS に ‘mypage.apps.MypageConfig’, を追加
  • これを行うことで、テンプレートファイルの検索パスにmypageが含まれるようになります。
... 省略
# Application definition

INSTALLED_APPS = [
    'mypage.apps.MypageConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
... 省略

7.2.3 テンプレートファイル用意

  • 今回はemacsのorgモードを利用して自動生成したhtmlをベースに改造して以下を作成しました。
  • 以下の内容をmypage/templates/mypage/index.html として保存します。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>djangoの始め方</title>
</head>
<body>
<h1 class="title">djangoの始め方</h1>
</body>
</html>

7.2.4 表示部分の改造

  • mypage/views.py を以下の内容に変更します
from django.http import HttpResponse
from django.template import loader

def index(request):
    template = loader.get_template('mypage/index.html')
    context = {}
    return HttpResponse(template.render(context, request))

7.2.5 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

7.3 この章まとめ

  • テンプレートファイルを利用して表示できる手順を説明

8 djangoアプリ4(計算アプリ)

8.1 以下の操作を行っている動画


8.2 djangoアプリ4(計算アプリ)作成手順

8.2.1 プロジェクトとmycalcアプリのベース作成

  • 今回はmytest002という名前で新規に作っていきます。
django-admin startproject mytest002
cd mytest002
python manage.py startapp mycalc

8.2.2 プロジェクトのセッティング修正

  • INSTALLED_APPS 以外の部分は変更しないでください。
  • mytest002/settings.py の INSTALLED_APPS に ‘mycalc.apps.MypageConfig’, を追加
  • これを行うことで、テンプレートファイルの検索パスにmycalcが含まれるようになります。
... 省略
# Application definition

INSTALLED_APPS = [
    'mycalc.apps.MycalcConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
... 省略

8.2.3 テンプレートファイルを入れるディレクトリの準備

mkdir -p mycalc/templates/mycalc

8.2.4 テンプレートファイル用意

  • 以下の内容をmycalc/templates/mycalc/index.html として保存します。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>計算アプリ</title>
</head>
<body>
<form action="" method="POST">
  <table>
    {{ form.as_table }}
  </table>
  {% csrf_token %}
  <button type="submit">計算</button>
</form>
{% if ans != "" %}
  {{ ans }} </br>
{% endif %}
</body>
</html>

8.2.5 mycalc/form.py を作成

from django import forms

class numForm(forms.Form):
  num1 = forms.DecimalField(label='num1', initial=2, required=True)
  num2 = forms.DecimalField(label='num2', initial=1, required=True)

8.2.6 mycalc/views.py を以下に変更します。

from django.shortcuts import render
from .form import numForm

def index(request):
  if request.method == 'POST':
    form = numForm(request.POST)
    if form.is_valid():
      # print(request.POST)
      # print(form.cleaned_data['num1'])
      # print(form.cleaned_data['num2'])
      # return HttpResponseRedirect('/answer/')
      # print([form.cleaned_data['num1'],form.cleaned_data['num2']])
      n1=int(form.cleaned_data['num1'])
      n2=int(form.cleaned_data['num2'])
      ans=str(n1)+" + "+str(n2)+" = "+str(n1+n2)
      # print(ans)
  else:
    form=numForm()
    ans=""
  return render(request,'mycalc/index.html',{'form':form, 'ans' : ans})

8.2.7 mycalc/urls.py には以下を記述します

from django.urls import path

from . import views

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

8.2.8 mytest002/urls.py に 以下の変更を行う

from django.contrib import admin
from django.urls import include, path

from django.views.generic.base import RedirectView

urlpatterns = [
    path('mycalc/', include('mycalc.urls')),
    path('', RedirectView.as_view(url='/mycalc/')),
    path('admin/', admin.site.urls),
]

8.2.9 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

8.3 この章まとめ

  • 整数を2つ入力して、足し算結果を返すアプリの作成(JavaScriptで作った方が楽っぽいけど)
  • formの使い方の基礎を確認

9 djangoアプリ5(ファイルアップロード)

9.1 以下の操作を行っている動画



9.2 djangoアプリ5(ファイルアップロード)作成手順

9.2.1 プロジェクトとmyfile_uploadアプリのベース作成

  • 今回はmytest003という名前で新規に作っていきます。
django-admin startproject mytest003
cd mytest003
python manage.py startapp myfile_upload

9.2.2 プロジェクトのセッティング修正

  • INSTALLED_APPS 以外の部分は変更しないでください。
  • mytest003/settings.py の INSTALLED_APPS に ‘myfileUpload.apps.MypageConfig’, を追加
  • これを行うことで、テンプレートファイルの検索パスにmyfile_uploadが含まれるようになります。
... 省略
# Application definition

INSTALLED_APPS = [
    'myfile_upload.apps.MyfileUploadConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
... 省略

9.2.3 myfile_upload/urls.py には以下を記述します

from django.urls import path

from . import views

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

9.2.4 mytest003/urls.py に 以下の変更を行う

from django.contrib import admin
from django.urls import include, path

from django.views.generic.base import RedirectView

import myfile_upload.views as myfile_upload

urlpatterns = [
    path('success/url/',myfile_upload.success),
    path('myfile_upload/', include('myfile_upload.urls')),
    path('', RedirectView.as_view(url='/myfile_upload/')),
    path('admin/', admin.site.urls),
]

9.2.5 テンプレートファイルを入れるディレクトリの準備

mkdir -p myfile_upload/templates/myfile_upload

9.2.6 テンプレートファイル1用意

  • 以下の内容をmyfile_upload/templates/myfile_upload/index.html として保存します。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>ファイルアップロードアプリ</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data">
  <table>
    {{ form.as_table }}
  </table>
  {% csrf_token %}
  <button type="submit">ファイルアップロード実行</button>
</form>
</body>
</html>

9.2.7 テンプレートファイル2(成功時用)用意

  • 以下の内容をmyfile_upload/templates/myfile_upload/success.html として保存します。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>ファイルアップロードアプリ</title>
</head>
<body>
<h1>ファイルのアップロードに成功しました!</h1>
</body>
</html>

9.2.8 myfile_upload/forms.py を作成

from django import forms

class UploadFileForm(forms.Form):
    # title = forms.CharField(max_length=50)
    file = forms.FileField()

9.2.9 myfile_upload/views.py を以下に変更します。

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm

# Imaginary function to handle an uploaded file.
# from somewhere import handle_uploaded_file

def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponseRedirect('/success/url/')
    else:
        form = UploadFileForm()
    return render(request, 'myfile_upload/index.html', {'form': form})

def handle_uploaded_file(f):
    fname='files/'+f.name
    print("fname:"+fname)
    with open(fname, 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)

def success(request):
    return render(request, 'myfile_upload/success.html', {})

9.2.10 ファイル保管用のディレクトリ作成

mkdir -p files

9.2.11 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

9.3 この章まとめ

  • ファイルのアップロードを行う方法を確認

10 urlでのパラメータ渡し

10.1 以下の操作を行っている動画


10.2 djangoアプリ4(計算アプリ)の改造手順

10.2.1 mycalc/urls.py を以下に修正

  • <int: だとマイナスの数値にマッチしてくれないため、re_pathで正規表現マッチを利用 (?P<name>pattern)
from django.urls import path, re_path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    # ex: /mycalc/5/6/results/
    # path('<int:num1>/<int:num2>/results/', views.results, name='results'),
    # ex: /mycalc/5/-1/4/results/
    re_path(r'^(?P<num1>-?\d+)/(?P<num2>-?\d+)/(?P<num3>-?\d+)/results/$', views.results, name='results'),
]

10.2.2 mycalc/views.py を以下に変更します。

from django.shortcuts import render, redirect
from .form import numForm
from django.views.generic.base import RedirectView

def index(request):
  if request.method == 'POST':
    form = numForm(request.POST)
    if form.is_valid():
      n1=int(form.cleaned_data['num1'])
      n2=int(form.cleaned_data['num2'])
      return redirect(str(n1)+'/'+str(n2)+'/'+str(n1+n2)+'/results/')
  else:
    form=numForm()
  return render(request,'mycalc/index.html',{'form':form})

def results(request, num1, num2, num3):
  return render(request,'mycalc/results.html',{'num1':num1, 'num2':num2, 'num3':num3})

10.2.3 元のテンプレートファイルを修正

  • mycalc/templates/mycalc/index.html を以下に修正
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>計算アプリ</title>
</head>
<body>
<form action="" method="POST">
  <table>
    {{ form.as_table }}
  </table>
  {% csrf_token %}
  <button type="submit">計算</button>
</form>
</body>
</html>

10.2.4 結果表示用のテンプレートファイル作成

  • mycalc/templates/mycalc/results.html を以下の内容で作る
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>計算アプリ 計算結果</title>
</head>
<body>
    {{ num1 }} + {{ num2 }} = {{ num3 }} </br>
    {{ num1 }} + {{ num2 }} = {{ num1|add:num2 }} </br>
<a href="/mycalc/">計算設定画面へ</a>
</form>
</body>
</html>

10.2.5 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

10.3 この章まとめ

  • urlでのパラメーター渡し
  • 正規表現を利用したマッチも利用
  • 組み込みタグとフィルタの活用

11 カスタムフィルタ

11.1 以下の操作を行っている動画


11.2 カスタムフィルタの作成手順

11.2.1 カスタムフィルタ保存用のフォルダ作成

mkdir -p mycalc/templatetags

11.2.2 カスタムフィルタ用ファイルの作成

  • touchコマンドで空の__init__.pyを作成してます。エディタで空の__init__.pyを作成してもOK
touch mycalc/templatetags/__init__.py
  • mycalc/templatetags/mysub.py を以下の内容で作成
from django import template

register = template.Library()

@register.filter("mysub")
def mysub(value, arg):
    return int(value)-int(arg)

@register.filter("mymul")
def mymul(value, arg):
    return int(value)*int(arg)

@register.filter("mydiv")
def mydiv(value, arg):
    return int(value)/int(arg)

@register.filter("mymod")
def mymod(value, arg):
    return int(value)%int(arg)

11.2.3 結果表示用のテンプレートファイル修正

  • mycalc/templates/mycalc/results.html を以下の内容で作る
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>計算アプリ 計算結果</title>
</head>
<body>
    {{ num1 }} + {{ num2 }} = {{ num3 }} </br>
    {{ num1 }} + {{ num2 }} = {{ num1|add:num2 }} </br>
    {% load mysub %}
    {{ num1 }} - {{ num2 }} = {{ num1|mysub:num2 }} </br>
    {{ num1 }} * {{ num2 }} = {{ num1|mymul:num2 }} </br>
    {{ num1 }} / {{ num2 }} = {{ num1|mydiv:num2 }} </br>
    {{ num1 }} % {{ num2 }} = {{ num1|mymod:num2 }} </br>
<a href="/mycalc/">計算設定画面へ</a>
</form>
</body>
</html>

11.2.4 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

11.3 この章まとめ

  • カスタムフィルタを作成し利用してみた

12 セッション(session)関係の機能を試してみる

12.1 以下の操作を行っている動画


12.2 djangoアプリ5(ファイルアップロード mytest003)の改造手順

12.2.1 マイグレートを行う

  • セッション機能を利用するのに必用(デフォルトのセッションの設定の場合、データベースを利用するため)
python manage.py migrate

12.2.2 テンプレートファイル2(成功時用)を改造

  • myfile_upload/templates/myfile_upload/success.html を以下の内容に変更
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>ファイルアップロードアプリ</title>
</head>
<body>
<h1>ファイル {{ orgfilename }} を {{ dfilename }} として、アップロードすることに成功しました!</h1>
</body>
</html>

12.2.3 myfile_upload/views.py を以下に変更します。

  • テンポラリファイルを利用するように変更
  • success画面で、セッションに保存したファイル名を利用する様に変更
  • セッションでの値の保存は request.session[‘名前’]=値
  • 保存していたセッションでの値の取り出し request.session[‘名前’]
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
import tempfile

# Imaginary function to handle an uploaded file.
# from somewhere import handle_uploaded_file

def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            tfname=handle_uploaded_file(request.FILES['file'])
            request.session['org_fname']=request.FILES['file'].name
            request.session['tmp_fname']=tfname
            return HttpResponseRedirect('/success/url/')
    else:
        form = UploadFileForm()
    return render(request, 'myfile_upload/index.html', {'form': form})

def handle_uploaded_file(f):
    # fname='files/'+f.name
    tfname=""
    # print("fname:"+fname)
    # with open(fname, 'wb+') as destination:
    with tempfile.NamedTemporaryFile(mode='wb+',delete=False) as destination:
        tfname=destination.name
        print(tfname)
        for chunk in f.chunks():
            destination.write(chunk)
    return tfname

def success(request):
    return render(request, 'myfile_upload/success.html', {'orgfilename':request.session['org_fname'], 'dfilename':request.session['tmp_fname']})

12.2.4 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

12.3 この章まとめ

  • セッション関係の機能を利用
  • 一時ファイル(tempfile)を利用

13 カスタムユーザーの作成(AbstractUserを継承タイプ)

13.1 以下の操作を行っている動画


13.2 カスタムユーザー設定(AbstractUserを継承タイプ)を行ってプロジェクトを始める手順

13.2.1 mytest004というアプリを作成し、その中にmyuserというアプリケーションを作成

django-admin startproject mytest004
cd mytest004
python manage.py startapp myuser

13.2.2 mytest004/settings.py 修正

  • INSTALLED_APPS に ‘myuser’, を追加
  • AUTH_USER_MODEL = ‘myuser.User’ を追加
... 省略
# Application definition

INSTALLED_APPS = [
    'mypage.apps.MypageConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myuser',
]

AUTH_USER_MODEL = 'myuser.User'
... 省略

13.2.3 myuser/models.py 修正

  • 「pass はヌル操作 (null operation) です — pass が実行されても、何も起きません。 pass は、構文法的には文が必要だが、コードとしては何も実行したくない場合のプレースホルダとして有用です。」 https://docs.python.org/ja/3/reference/simple_stmts.html#pass から引用
  • 後でUserを修正してカスタマイズ出来ます
# from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

13.2.4 myuser/admin.py 修正

rom django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

13.2.5 migration作成、適用

python manage.py makemigrations
python manage.py migrate

13.2.6 superuserを作成

python manage.py createsuperuser
  • 以下の感じで、ユーザーネーム、メールアドレスを入れて(ここでメールアドレスは適当なもにしてます、わたしのメールアドレスではありません)
Username: admin
Email address: xxx@xxx.xxx
Password: 
Password (again): 
  • パスワードが簡単すぎると以下のようなメッセージが表示されます、以下の例では強引にそれを認めさせてます
This password is too short. It must contain at least 8 characters.
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: y
  • パスワード2つ入力したものが異なると以下のメッセージが出ます。そのときは再度2回同じパスワードを入れてください
Error: Your passwords didn't match.

13.2.7 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver
  • http://localhost:8000/admin/ にアクセスして、設定した管理者権限のユーザー名とパスワードでログインします

13.3 この章まとめ

  • プロジェクト開始時にカスタムユーザー作成

14 カスタムユーザーの作成2(AbstractBaseUserを継承タイプ)

  • 前章はAbstractUserを継承していましたが、今回はより自由度が高くなるAbstractBaseUserを継承
  • myuser/models.py 修正する箇所が異なるのみで、他はほぼ一緒
  • 本家で関係する文書 は前章と同じになります。
  • 関係するインストールしたpythonファイル (ローカルの~/python3/にインストールした場合) ~/python3/dist-packages/django/contrib/auth/models.py

14.1 以下の操作を行っている動画


14.2 カスタムユーザー設定(AbstractBaseUserを継承タイプ)を行ってプロジェクトを始める手順

14.2.1 mytest005というアプリを作成し、その中にmyuserというアプリケーションを作成

django-admin startproject mytest005
cd mytest005
python manage.py startapp myuser

14.2.2 mytest005/settings.py 修正

  • INSTALLED_APPS に ‘myuser’, を追加
  • AUTH_USER_MODEL = ‘myuser.User’ を追加
... 省略
# Application definition

INSTALLED_APPS = [
    'myuser',
    'mypage.apps.MypageConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

AUTH_USER_MODEL = 'myuser.User'
... 省略

14.2.3 mytest005/settings.py 修正2

  • ローカライズの修正も行います。(今回特に必用ないけど)
  • LANGUAGE_CODE を ‘en-us’ から ‘ja’に変更
  • TIME_ZONE を ‘UTC’ から ‘Asia/Tokyo’ に変更
... 省略
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
... 省略

14.2.4 myuser/models.py 修正

  • 関係するインストールしたpythonファイル (ローカルの~/python3/にインストールした場合) ~/python3/dist-packages/django/contrib/auth/models.py をベースに削って作成。修正内容は以下の感じ
    • 関係しそうなimportのみを残す
    • from django.contrib.auth.models import PermissionsMixin, UserManager を追加(オリジナルの PermissionsMixin, UserManagerを利用するため)
    • from django.contrib.auth.validators import UnicodeUsernameValidator を追加
    • AbstractUser 部分をコピペ
      • クラス名をUserに変更
      • abstract = True をコメントアウト
  • 後で修正してカスタマイズ可能
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from django.contrib.auth.models import PermissionsMixin, UserManager

from django.contrib.auth.validators import UnicodeUsernameValidator


class User(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username and password are required. Other fields are optional.
    """
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        # abstract = True

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

14.2.5 myuser/admin.py 修正

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

14.2.6 migration作成、適用

python manage.py makemigrations
python manage.py migrate

14.2.7 superuserを作成

  • 質問に答えていって、作成
  • 実際の途中の様子は前章参照
python manage.py createsuperuser

14.2.8 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver
  • http://localhost:8000/admin/ にアクセスして、設定した管理者権限のユーザー名とパスワードでログインします

14.3 この章まとめ

  • プロジェクト開始時にカスタムユーザー作成

15 郵便番号検索アプリの作成

15.1 以下の操作を行っている動画


15.2 郵便番号csvデータの取得と準備

15.2.1 データの取得が面倒な方は ken_all_utf_wh.json として以下を保存

  • 3つのデータのみ入っているデータ
[
  {
    "model": "zipcodes.zipcode",
    "pk": 1,
    "fields": {
      "jisx": "01101",
      "zipold": "060  ",
      "zip": "0600000",
      "prefecture": "ホッカイドウ",
      "city": "サッポロシチュウオウク",
      "street": "イカニケイサイガナイバアイ",
      "kprefecture": "北海道",
      "kcity": "札幌市中央区",
      "kstreet": "以下に掲載がない場合",
      "c1": "0",
      "c2": "0",
      "c3": "0",
      "c4": "0",
      "c5": "0",
      "c6": "0"
    }
  },
  {
    "model": "zipcodes.zipcode",
    "pk": 79393,
    "fields": {
      "jisx": "26104",
      "zipold": "604  ",
      "zip": "6048182",
      "prefecture": "キョウトフ",
      "city": "キョウトシナカギョウク",
      "street": "オオサカザイモクチョウ",
      "kprefecture": "京都府",
      "kcity": "京都市中京区",
      "kstreet": "大阪材木町",
      "c1": "0",
      "c2": "0",
      "c3": "0",
      "c4": "0",
      "c5": "0",
      "c6": "0"
    }
  },
  {
    "model": "zipcodes.zipcode",
    "pk": 124340,
    "fields": {
      "jisx": "47382",
      "zipold": "90718",
      "zip": "9071801",
      "prefecture": "オキナワケン",
      "city": "ヤエヤマグンヨナグニチョウ",
      "street": "ヨナグニ",
      "kprefecture": "沖縄県",
      "kcity": "八重山郡与那国町",
      "kstreet": "与那国",
      "c1": "0",
      "c2": "0",
      "c3": "0",
      "c4": "0",
      "c5": "0",
      "c6": "0"
    }
  }
]

15.2.2 データの取得

  • 郵便番号と住所のデータの準備が面倒な方は上のken_all_utf_wh.jsonを準備してスキップ可
  1. ブラウザでダウンロードしに行く場合
  2. wgetを利用してのダウンロードする場合
    • データと、データの説明ページをダウンロードしてます
    wget "https://www.post.japanpost.jp/zipcode/dl/kogaki/zip/ken_all.zip"
    wget "https://www.post.japanpost.jp/zipcode/dl/readme.html"
    
  3. unarを利用してzipファイルを解凍する場合
    • unar以外のツールでzipを解凍してもOK
    • KEN_ALL.CSV を取り出せます
    unar ken_all.zip
    
  4. 文字コードをユニコードに変換(nkfを利用する場合)
    • 複数の文字コードを変換できるエディタや、ツールを利用してもOK
    • ここではnkfコマンドでやってみます。
    • 環境がユニコードなら以下でOK
    • 作成したファイル名は ken_all_utf.csv
    nkf KEN_ALL.CSV > ken_all_utf.csv
    

    **

  5. 1行目に各列のデータ名をつける
    jisx,zipold,zip,prefecture,city,street,kprefecture,kcity,kstreet,c1,c2,c3,c4,c5,c6
    
    • echo と cat コマンドで行うと以下の感じ、普通のエディタで上の1行をデータの頭に入れてもOK
    echo "jisx,zipold,zip,prefecture,city,street,kprefecture,kcity,kstreet,c1,c2,c3,c4,c5,c6" > ken_all_head.csv
    cat ken_all_head.csv ken_all_utf.csv > ken_all_utf_wh.csv
    
  6. csvをjsonデータに変換
    • 他のツールを使ってもOK
    • 以下の内容のpythonファイルを作成し、convert.pyという名前で保存
    import csv
    import json
    
    with open('ken_all_utf_wh.csv', 'r') as f:
      r = csv.DictReader(f)
      c = 1
      a=[]
      for row in r:
        a.append({"model" : "zipcodes.zipcode", "pk" : c, 'fields' : row })
        c=c+1
      print(json.dumps(a, ensure_ascii=False, indent=2))
    
    • 以下を実行すると ken_all_utf_wh.csv(ヘッダー追加してユニコードにしたcsvファイル)をjson形式に変換し ken_all_utf_wh.json として保存
    python convert.py > ken_all_utf_wh.json
    

15.3 郵便番号検索アプリの作成手順

15.3.1 myzipcodes というアプリを作成し、その中にmyappというアプリケーションを作成

  • カスタムユーザーを最初に追加した方が後々カスタムユーザーを使う時に便利になりますが、説明上カスタムユーザーを追加せずに作って行きます。(前章を参考にカスタムユーザーを作っておく方が良い。)
django-admin startproject myzipcodes
cd myzipcodes
python manage.py startapp zipcodes

15.3.2 myzipcodes/settings.py 修正

  • INSTALLED_APPS に ‘zipcodes.apps.ZipcodesConfig’, を追加
... 省略
# Application definition

INSTALLED_APPS = [
    'zipcodes.apps.ZipcodesConfig',
    'mypage.apps.MypageConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

... 省略

15.3.3 myzipcodes/settings.py 修正2

  • ローカライズの修正も行います。(今回特に必用ないけど)
  • LANGUAGE_CODE を ‘en-us’ から ‘ja’に変更
  • TIME_ZONE を ‘UTC’ から ‘Asia/Tokyo’ に変更
... 省略
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
... 省略

15.3.4 zipcodes/models.py 修正

from django.db import models

class Zipcode(models.Model):
    jisx = models.CharField(max_length=20)
    zipold = models.CharField(max_length=10)
    zip = models.CharField(max_length=10)
    prefecture = models.CharField(max_length=200)
    city = models.CharField(max_length=200)
    street = models.CharField(max_length=200)
    kprefecture = models.CharField(max_length=200)
    kcity = models.CharField(max_length=200)
    kstreet = models.CharField(max_length=200)
    c1 = models.CharField(max_length=10)
    c2 = models.CharField(max_length=10)
    c3 = models.CharField(max_length=10)
    c4 = models.CharField(max_length=10)
    c5 = models.CharField(max_length=10)
    c6 = models.CharField(max_length=10)

15.3.5 zipcodes/admin.py 修正

  • Zipcodeを管理サイトに登録(これで管理メニューでzipcodeをいじれます)
from django.contrib import admin

from .models import Zipcode

admin.site.register(Zipcode)

15.3.6 migration作成、適用

python manage.py makemigrations
python manage.py migrate

15.3.7 郵便局から得たcsvファイルを前節で加工して生成した ken_all_utf_wh.json ファイルをデータベースに読み込み

python manage.py loaddata ../ken_all_utf_wh.json

15.3.8 zipcodes/views.py を以下に修正

from django.shortcuts import render, get_object_or_404, get_list_or_404
from .models import Zipcode
from django.db.models import Q

def getAddress(request,zipcode):
  d=get_list_or_404(Zipcode, zip__regex=zipcode)
  return render(request, 'zipcodes/getAddress.html', {'zipcode':zipcode, 'd':d})

def getZipcodesStreet(request,s):
  d=get_list_or_404(Zipcode, kstreet__regex=s)
  return render(request, 'zipcodes/getZipcodes.html', {'s':s, 'x':"町域名", 'd':d})

def getZipcodesCity(request,s):
  d=get_list_or_404(Zipcode, kcity__regex=s)
  return render(request, 'zipcodes/getZipcodes.html', {'s':s, 'x':"市区町村名", 'd':d})

def getZipcodesPrefecture(request,s):
  d=get_list_or_404(Zipcode, kprefecture__regex=s)
  return render(request, 'zipcodes/getZipcodes.html', {'s':s, 'x':"都道府県名", 'd':d})

def getZipcodesOr(request,s):
  d=get_list_or_404(Zipcode, Q(kprefecture__regex=s)|Q(kcity__regex=s)|Q(kstreet__regex=s))
  return render(request, 'zipcodes/getZipcodes.html', {'s':s, 'x':"都道>府県名あるいは市区町村名あるいは町域名", 'd':d})

15.3.9 zipcodes/templates/zipcodes/getAddress.html を作成

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>郵便番号{{zipcode}}を含む住所</title>
</head>
<body>
<h1>郵便番号{{zipcode}}を含む住所</h1>
{% for a in d %}
    <li>{{ a.zip }} {{ a.kprefecture}} {{ a.kcity }} {{ a.kstreet }}</a></li>
{% endfor %}
</body>
</html>

15.3.10 zipcodes/templates/zipcodes/getZipcodes.html を作成

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>"{{s}}"を"{{x}}"に含む住所</title>
</head>
<body>
<h1>"{{s}}"を"{{x}}"に含む住所</h1>
{% for a in d %}
    <li>{{ a.zip }} {{ a.kprefecture}} {{ a.kcity }} {{ a.kstreet }}</a></li>
{% endfor %}
</body>
</html>

15.3.11 myzipcodes/urls.py を以下に修正

from django.contrib import admin
from django.urls import path, re_path
from zipcodes import views as zviews

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^zip/(?P<zipcode>\d{1,7})$', zviews.getAddress, name="getAddress"),
    re_path(r'^street/(?P<s>.+)$', zviews.getZipcodesStreet, name="getZipcodesStreet"),
    re_path(r'^city/(?P<s>.+)$', zviews.getZipcodesCity, name="getZipcodesCity"),
    re_path(r'^prefecture/(?P<s>.+)$', zviews.getZipcodesPrefecture, name="getZipcodesPrefecture"),
    re_path(r'^address/(?P<s>.+)$', zviews.getZipcodesOr, name="getZipcodesPrefecture"),
]

15.4 この章でのまとめ

  • データベースとの連携機能の利用
  • データベースへのデータのインポート
  • 今までの章でやってきたテクの利用

16 djangoのshellの活用

16.1 以下の操作を行っている動画


16.2 動画で出てくる操作

  • 操作は一つ前の章で使った郵便番号検索アプリで行ってます

16.2.1 dangoのシェルの起動

  • 郵便番号アプリのプロジェクトのベースディレクトリ(manage.pyがあるディレクトリ)にはいってから
python manage.py shell
  • 現状やると以下の画面に
$ python manage.py shell
Python 3.7.5 (default, Nov 20 2019, 09:21:52) 
Type "copyright", "credits" or "license" for more information.

IPython 5.8.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: 

16.2.2 モデルクラスのimport

  • 郵便番号検索アプリの場合は Zipcode クラスをimport
  • import のfromの部分は、アプリ名とmodelsを . で結合し、郵便番号アプリのZipcodeの場合は zipcodes.models になる
from zipcodes.models import Zipcode

16.2.3 モデルクラスで検索

  1. 全部のデータを取得し、個数を表示
    • dに検索結果を入れる
    d=Zipcode.objects.all()
    len(d)
    
  2. pk=1のデータっを取得し、zipcodeや色々なデータを表示
    • dに検索結果を入れる
    d=Zipcode.objects.filter(pk=1)
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    
  3. 県名が”大阪府”で、市名が”大阪市”のデータを取得し数を数え、一番最初のデータの表示
    • And条件
    d=Zipcode.objects.filter(kprefecture="大阪府",kcity__startswith="大阪市")
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    
  4. Qオブジェクトを利用してORとかAnd
    • Q()で条件をくるんで
    • ANDは&で結合
    • ORは|で結合
    • “府”で終わる都道府県名で、市区町村名に”区”が含まれる And条件だと
    from django.db.models import Q
    d=Zipcode.objects.filter(Q(kprefecture__endswith="府")&Q(kcity__contains="区"))
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    
    • Qオブジェクトを利用しないANDケースだと
    d=Zipcode.objects.filter(kprefecture__endswith="府",kcity__contains="区")
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    
    • “府”で終わる都道府県名か、市区町村名に”区”が含まれる OR条件だと
    d=Zipcode.objects.filter(Q(kprefecture__endswith="府")|Q(kcity__contains="区"))
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    

16.2.4 ここまでの操作の画面のコピー

$ python manage.py shell
Python 3.7.5 (default, Nov 20 2019, 09:21:52) 
Type "copyright", "credits" or "license" for more information.

IPython 5.8.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from zipcodes.models import Zipcode
   ...: 

In [2]: d=Zipcode.objects.all()
   ...: len(d)
   ...: 
Out[2]: 124340

In [3]: d=Zipcode.objects.filter(pk=1)
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[1, '0600000', '北海道', '札幌市中央区', '以下に掲載がない場合']

In [4]: d=Zipcode.objects.filter(kprefecture="大阪府",kcity__startswith=
   ...: "大阪市")
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[966, '5340000', '大阪府', '大阪市都島区', '以下に掲載がない場合']

In [5]: from django.db.models import Q
   ...: d=Zipcode.objects.filter(Q(kprefecture__endswith="府")&Q(kcity__
   ...: contains="区"))
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[6214, '6030000', '京都府', '京都市北区', '以下に掲載がない場合']

In [6]: d=Zipcode.objects.filter(kprefecture__endswith="府",kcity__conta
   ...: ins="区")
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[6214, '6030000', '京都府', '京都市北区', '以下に掲載がない場合']

In [7]: d=Zipcode.objects.filter(Q(kprefecture__endswith="府")|Q(kcity__
   ...: contains="区"))
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[24247, '0600000', '北海道', '札幌市中央区', '以下に掲載がない場合']

In [8]: 

16.3 この章でのまとめ

  • shell機能を活用することで、インタラクティブに動作確認や機能の確認を行うことができる(デバッグや、アプリ構築の際に非常に便利)

17 MySQLの利用

17.1 以下の操作を行っている動画


17.2 MySQLのインストール

17.2.1 Ubuntuの場合

sudo apt install mysql-client mysql-server

17.2.2 WindowsやMacの場合

  • 本家のGetting Startや、「OS名 MySQL インストール」などでネット検索かけて頂ければ、インストール方法みつかります

17.3 MySQLへの最初のログイン

  • 最初結構はまりました、/usr/share/doc/mysql-clientにも文書なかったし
  • Ubuntuの19.10の場合、インストール時にrootパスワードの設定がでてきません
  • 以下のコマンドを実行して、パスワードを設定してください。
  • 残りの質問は全部デフォルト(エンターのみ)でOK
sudo mysql_secure_installation
  • 設定してる画面は以下の感じ

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No: 
Please set the password for root here.

New password: 

Re-enter new password: 
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : 

 ... skipping.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : 

 ... skipping.
By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : 

 ... skipping.
Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : 

 ... skipping.
All done! 

17.4 MySQLへの最初の接続

  • 以下のコマンドを実行し、上で設定したパスワードを入力するとプロンプトが表示されます
sudo mysql -u root -p
  • ログインが上手くいくと以下の感じに
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 18
Server version: 8.0.18-0ubuntu0.19.10.1 (Ubuntu)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

17.5 django用のデータベース作成

  • database_nameは好きなデータベース名に
CREATE DATABASE database_name;
  • 例えばデータベース名を testdb とすると
CREATE DATABASE testdb;

17.6 ユーザーの作成

  • username は希望するユーザー名に
  • host はホスト名,ローカルならlocalhost
  • passward は 希望するパスワードに
CREATE USER 'user_name'@'host' IDENTIFIED BY 'password';
  • 例えば、ユーザー名をtester, ホスト名を localhost, パスワードを how7OjIcus にするなら
CREATE USER 'tester'@'localhost' IDENTIFIED BY 'how7OjIcus';

17.7 ユーザーに権限を付与

  • ‘tester’@’localhost’ にdatabase_nameがtestdb への操作を全部許可する場合
GRANT ALL PRIVILEGES ON testdb.* TO 'tester'@'localhost';
FLUSH PRIVILEGES;

17.8 データベース一覧表示

SHOW DATABASES;

17.9 作成したユーザーで作成したデータベースにアクセスできるか確認

  • パスワードをきかれたら、設定したパスワードを答える
mysql  -u tester -p testdb

17.10 python3-mysqldb(mysqlclient)のインストール

  • sudo apt install python3-mysqldb だと以下になってバージョン古すぎメッセージが出て使えないので、pipでインストール
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 1.3.10.
  • mysql_configが必用になるため、mysql_configを含む libmysqlclient-dev をインストール
sudo apt install libmysqlclient-dev
  • Ubuntuでローカルにインストールする場合
  • windowsとかMacなど他なら、pip3をpipにして -t 以降無しで実行 ( pip install mysqlclient )
pip3 install mysqlclient -t ~/python3/dist-packages
  • これらをアップグレードする場合
pip3 install --upgrade Django -t ~/python3/dist-packages
pip3 install --upgrade mysqlclient -t ~/python3/dist-packages

17.10.1 UbuntuなどDebian系以外の場合(windowsやmacや他の系統のLinux)

  • ローカルじゃなく普通にインストールする場合
pip install mysqlclient

17.11 MySQLをプロジェクトで利用

17.11.1 プロジェクトの作成

  • カスタムユーザーアプリの作成と、カスタムユーザーアプリの初期設定は省略します。通常の場合、後でカスタムユーザー追加は操作が大変なので最初に必ず追加しましょう。
django-admin startproject mytest007
cd mytest007

17.11.2 プロジェクト名/setting.pyを変更

  • 作成したデータベース名 testdb
  • ログインユーザー名が tester@localhost の場合
  • 日本語設定や、タイムゾーンの設定は省略します。
DATABASES = {
    #    'default': {
    #        'ENGINE': 'django.db.backends.sqlite3',
    #        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    #    }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'testdb',
        'USER': 'tester',
        'PASSWORD': 'how7OjIcus', 
        'HOST': '',
        'PORT': '',
    }
}

17.11.3 データベースとの連携

python manage.py makemigrations
python manage.py migrate

17.11.4 作成されたtableを確認

$ mysql -u tester -p testdb
Enter password: 
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 8.0.18-0ubuntu0.19.10.1 (Ubuntu)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show tables;
+------------------------------+
| Tables_in_testdb           |
+------------------------------+
| auth_group                   |
| auth_group_permissions       |
| auth_permission              |
| django_admin_log             |
| django_content_type          |
| django_migrations            |
| django_session               |
+------------------------------+
11 rows in set (0.01 sec)

mysql> quit
Bye

17.11.5 作成したデータベースのバックアップ

  • ユーザー名 tester
  • パスワードが how7OjIcus
  • データベース名が testdb
  • バックアップファイル名が dump.sqlの場合
mysqldump -u tester -phow7OjIcus testdb > dump.sql

17.12 この章について

  • デフォルトのSqlite3ではなくMySQLをデータベースとして使う方法
  • 他MySQLを使うにあたっての最低限の操作例
  • ユーザー名やパスワードは同じものを使わないで!

18 実際裏で実行されてるSQLの確認方法

18.1 以下の操作を行っている動画


18.2 裏で実行されてるSQLの確認方法例

  • 以前この文書で作成した 郵便番号検索アプリ を利用して試してみます
  • djangoのshell機能を利用します
  • DEBUG=Trueの時のみこの方法可能だそうです

18.2.1 djangoのshell起動

  • 郵便番号アプリのmanage.pyがあるディレクトリで以下を実行
python manage.py shell

18.2.2 モデルのクラスをimport

from zipcodes.models import Zipcode
from django.db.models import Q

18.2.3 実際に調べてみる1

d1=Zipcode.objects.filter(Q(kprefecture__endswith="府")|Q(kcity__contains="区"))
str(d1.query)

18.2.4 実際に調べてみる2

d2=Zipcode.objects.filter(Q(kprefecture__endswith="府")&Q(kcity__contains="区"))
str(d2.query)

18.2.5 実際に調べてみる3

d3=Zipcode.objects.filter(kprefecture__endswith="府",kcity__contains="区")
str(d3.query)

18.2.6 実際に調べてみる4

from django.db import connection
connection.queries

18.2.7 実際に操作したときのスクショ

$ python manage.py shell
Python 3.7.5 (default, Nov 20 2019, 09:21:52) 
Type "copyright", "credits" or "license" for more information.

IPython 5.8.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from zipcodes.models import Zipcode

In [2]: from django.db.models import Q

In [3]: d1=Zipcode.objects.filter(Q(kprefecture__endswith="府")|Q(kcity_
   ...: _contains="区"))

In [4]: str(d1.query)
Out[4]: 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE %府 ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE %区% ESCAPE \'\\\')'

In [5]: from django.db import connection

In [6]: connection.queries
Out[6]: []

In [7]: len(d1)
Out[7]: 24247

In [8]: connection.queries
Out[8]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'}]

In [9]: d2=Zipcode.objects.filter(Q(kprefecture__endswith="府")&Q(kcity_
   ...: _contains="区"))
   ...: str(d2.query)
   ...: 
Out[9]: 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE %府 ESCAPE \'\\\' AND "zipcodes_zipcode"."kcity" LIKE %区% ESCAPE \'\\\')'

In [10]: connection.queries
Out[10]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'}]

In [11]: len(d2)
Out[11]: 6214

In [12]: connection.queries
Out[12]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.030'}]

In [13]: d3=Zipcode.objects.filter(kprefecture__endswith="府",kcity__con
    ...: tains="区")
    ...: str(d3.query)
    ...: 
Out[13]: 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kcity" LIKE %区% ESCAPE \'\\\' AND "zipcodes_zipcode"."kprefecture" LIKE %府 ESCAPE \'\\\')'

In [14]: len(d3)
Out[14]: 6214

In [15]: connection.queries
Out[15]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.030'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\')',
  'time': '0.036'}]

In [16]: connection.queries4
------------------------------------------------------------------------
AttributeError                         Traceback (most recent call last)
<ipython-input-16-7e2a5aa2c672> in <module>()
----> 1 connection.queries4

/home/pano/python3/dist-packages/django/db/__init__.py in __getattr__(self, item)
     26     """
     27     def __getattr__(self, item):
---> 28         return getattr(connections[DEFAULT_DB_ALIAS], item)
     29 
     30     def __setattr__(self, name, value):

AttributeError: 'DatabaseWrapper' object has no attribute 'queries4'

In [17]: connection.queries
Out[17]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.030'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\')',
  'time': '0.036'}]

In [18]: quit

18.3 この章のまとめ

  • モデルを利用した時に裏で実行されているSQL文の確認方法を試してみた
  • 実際SQL文が実行されるタイミングは必用となってからのケースもある

19 この文書のチェンジログ

  • 2019/11/10 初版
  • 2019/11/10 修正および、テンプレートファイルを利用する章の追加
  • 2019/11/10 足し算アプリ作成する章の追加
  • 2019/11/11 一部修正及び、ファイルアップロードする章の追加
  • 2019/11/11 urlからパラメーター取り出しする章の追加
  • 2019/11/11 カスタムフィルタ作成利用の章追加
  • 2019/11/16 セッションの利用の章を追加
  • 2019/11/16 python3 と入力していた命令を python に変更(Ubuntu以外はpythonであるため) Ubuntuの場合上で.bash_rcで設定しておらず、python2系と3系をインストールしているならpythonとコマンド入力している部分はpython3と入力すべきかも
  • 2019/11/17 カスタムユーザー(AbstractUserを継承タイプ)の章追加
  • 2019/11/18 カスタムユーザー(AbstractBaseUserを継承タイプ)の章追加、最初の概要変更
  • 2019/11/27 郵便番号検索アプリ作成の章追加
  • 2019/11/28 djangoのシェル機能の章追加
  • 2019/11/28 MySQLの利用の章追加
  • 2019/11/28 実際裏で実行されてるSQLの確認方法の章追加

20 続きを追記してきます。

著者: NM Max

Created: 2019-11-28 木 07:43

Validate

コメントを残す

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