はじめに
※随時更新予定です (※2024/09/16)
PythonでWebアプリケーションを実装していくことを考える、FlaskやFastAPIなどが選択肢としてありますがやはりDjangoの人気は高いと思っています。ただ、Djangoは他にフレームワークと比べると少し難易度は高い気がします。今回はそのDjangoをなるべくハードル低くと始められるように記事を書いてみました。初心者の勉強に役立ててもらえたらと思います。
手順
Starter
[1]. プロジェクト用のディレクトリ作成
$ mkdir todo
[2]. 仮想環境の構築
$ python -m venv venv
$ . venv/bin/activate
[3]. プロジェクトの立ち上げ
(venv)$ pip install django
(venv)$ cd todo
(venv)$ django-admin startproject todoproject .
※ 「startproject」 : 必要なファイルをコピーして持ってくる
Djangoでは、Webアプリの各種の設定情報を統括・管理するために仕組みでプロジェクトという概念がある。そのプロジェクト配下にアプリを1つまたは複数作成して開発していく。
[4]. アプリケーション作成
(venv)$ python manage.py startapp todoapp
ここまでのディレクトリ構造は以下のようになります
.
├── manage.py
├── todoapp
│ ├── __init__.py #初期化
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── todoproject
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-38.pyc
│ └── settings.cpython-38.pyc
├── asgi.py
├── settings.py #プロジェクト全体の設定情報を保存
├── urls.py #ルーティング記載
└── wsgi.py #WeebアプリとWebサーバーを繋ぐ役割
-
__init__.py
- モジュールのインポートができるようにするための処理
- モジュール自体の処理関数
[5]. settings.pyの編集
#プロジェクトのアプリを作成したことを伝える
IMSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
'todoapp', #アプリ追加
]
TEMPLATES = [
{
'DIRS' : [BASE_DIR / 'templates'] # 追加 django v.3
},
]
'DIRS' : [BASE_DIR, 'templates'] # 追加 django 2
- IMSTALLED_APPSの追加文は、apps.pyで定義されているConfigクラスの部分です
[6]. プロジェクト(todoproject)の「urls.py」でURLの繋込みを行う
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todoapp.urls'))
]
-
path(route, view, kwargs=None, name=None)
- route : ルートディレクトリ
- view : ビューまたはas_view()で返されるビュー
- path関数を利用して「リクエストされたページに対して特定のビューを呼び出すためのURLパターンを生成する」
-
importに 「include」の追加
-
include(module, namespace=None)
- module : URLconfモジュールの名前空間を指定する
[7]. アプリケーション(todoapp)に新規で「urls.py」を作成しURLの繋ぎ込みをする
from django.urls import path
from . import views
urlpatterns = [
path('hello/', views.helloworldfunction),
]
[8]. アプリの 「view.py」を編集する
from django.shortcuts import render
#追加
def helloworldfunction(request):
return render(request, 'index.html')
[9]. index.htmlを作成
todo/templates/index.html
※「manage.py」と同じ階層にtemplatesディレクトリ作成イメージ
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
[10]. migrate
Djangoのマイグレーション : 管理画面が使う上表をデータベースに自動的に作成・管理する機能
※データベースマイグレーションとは、DBのデータを保持したままDBのテーブルやカラム等の構成を変更することです。Djangoには、DDLを直接書かずとも構成を変更してくれるマイグレーション機能が備わっています。
# ($ python manage.py makemigrations)
(venv) $ python manage.py migrate
[11]. アプリの起動
Djangoに搭載されたサーバーを起動し、Webアプリを実行 (開発用サーバーの起動)
$ python manage.py runserver
-
settings.pyの深堀
- BASE_DIR
- DEBUG
- ALLOWED_HOSTS = []
- INSTALLED_APPS = []
- MIDDLEWARE = []
- ROOT_URLCONF = []
- WSGI_APPLICATION = []
- DATABASES = []
- AUTH_PASSWORD_VALIDATORS = []
ビュー
-
Djangoではビューを作成するときのために、様々なタイプのスーパークラスを用意
-
テンプレートをレンダリングするだけなら、「TemplateViewクラス」または「RedirectViewクラス」が最適
- TemplateView : テンプレートをレンダリングする時に使う
- django.views.generic.base.TemplateView
- RedirectView : 任意のURLにリダイレクトするときに使う
- django.views.generic.base.View
- TemplateView : テンプレートをレンダリングする時に使う
-
ビュー作成方法
- クラスベースビュー (実装が容易だから基本こっち)
- 関数ベースビュー (細かい制御をしたいとき)
クラスベースの書き方
from django.shortcuts import render
from django.views.generic.base import TemplateView
class IndexView(TemplateView):
template_name = 'index.html'
※template_nameに指定するのはファイル名のみ。templatesフォルダは自動で検出
流れとしては
プロジェクトのURLconf
↓
アプリのURLconf
↓
IndexViewによるindex.htmlのレンダリング
関数ベースビュー
- アプリのURLconf
from django.urls import path
from . import views
app_name = 'blogapp'
urlpatterns = [
path('', views.index_view, name='index')
]
- アプリのビュー
from django.shortcuts import render
def index_view(request):
return render(request,'index.html')
- 関数ベースビューでは「HTTPRequestオブジェクトを受け取るパラメーターを設定する」
静的ファイル
- Djangoでは静的ファイルは「static」というフォルダにおく
- 例) CSS, JS, 画像
Djangoが静的ファイルを参照する仕組み
テンプレートタグでstaticロード
{% load static %}
Django独自のテンプレートタグ
- load
- url
- for
- if
モデル(models.py)の作成
- Djangoには、SQLite3というデータベースが搭載されている
- settings.pyにDBの設定は記載
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
[1]. models.pyに記述する
from django.db import models
# Create your models here.
class TestModel(models.Model):
age = models.IntegerField()
name = models.CharField(max_length=10)
def __str__(self):
return self.name
[2]. マイグレーションファイルの作成
2STEPによってテーブル作成する
- モデルの定義に基づいてSQLを発行するためのマイグレーションファイル(Pythonモジュール)を自動生成
- 生成したマイグレーションファイルのコードを実行してDBにテーブルを作成
マイグレーションファイル生成(フィールド定義に従ってSQLを発行し、DBテーブルに反映させるためのコード)
$ python manage.py makemigrations [アプリ名]
[3]. マイグレーションファイルをDBに反映
- 生成されたマイグレーションを実行し、DBの更新(テーブルの作成)を行う
$ python mamage.py migrate
[4]. ユーザー登録
- Django管理サイトはDBの操作を行う重要なものなので、事前に登録されたsuperuserのみがログイン使う仕組み
$ python manage.py createsuperuser
*ユーザー登録していないとadminに入れない
[5]. admin.pyの編集
- Django管理サイトでは、あらかじめ登録されているモデルに対してのみ、データの追加や削除、編集ができる
from django.contrib import admin
from .models import TestModel
# Register your models here.
admin.site.register(TestModel)
*この追加をしないと管理者画面で作成したモデルが確認できない
[6]. DBにデータ登録
$ python manage.py runserver
-
http://127.0.0.1:8000/admin/
にアクセスしてDBにデータ追加可能 - 「+追加」からデータ追加
別のアプローチ !!!(shellを起動して登録)
$ python manage.py shell
>> from todoapp.models import TestModel
>> c = TestModel(age=23, name='taro')
>> c.save()
>> TestModel.objects.create(age=33,name='jiro')
>> TestModel.objects.all()
>> TestModel.objects.filter(name='taro')
>> TestModel.objects.order_by('name')
>> TestModel.objects.order_by('-name')
>> TestModel.objects.filter(id=2).update(name='jun')
ビューの作成方法
① Function-Based View (FBV) : リクエスストを引数として、戻り値としてhtmlレスポンスを返す
② Class-Based View (CBV): 親クラスを継承して、ビュークラスを作成してそれを使う
① FBV
@login_required
def listfunc(request):
object_list = BoardModel.objects.all()
return render(request,'list.html', {'object_list': object_list} )
def detailfunc(request, pk):
object = get_object_or_404(BoardModel, pk=pk)
return render(request, 'detail.html', {'object':object})
② CBV
TemplateView
- モデルと関連付けずにデータを扱わないhtmlファイルをつくるとき
- URL内でキャプチャされたパラメータを含むコンテキストとともに与えられたテンプレートをレンダリングする
- TemplateViewでの値の引き渡し(get_context_data)
- TemplateResponseMixin, ContextMixin, Viewの3つから継承
-
template_name
は必須
from django.shortcuts import render
from django.views.generic import TemplateView
class MemberList(TemplateView):
template_name = 'member_list.html' #表示するテンプレート
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['message'] = context
return context
- この場合、html側で {{ message }}で受け取る
- contextは辞書型
- contextが自動で取得されるのは、class-based viewの時(as_view()が呼ばれたタイミングで)
- contextは、get_context_data() メソッドによって取得される
path('member/', views.MemberList.as_view(), name='member')
ListView
データを表示される際に使用
from django.views.generic import ListView
from todoapp.models import TestModel
class ShowView(ListView):
model = TestModel
template_name = 'member_list.html'
※最低限modelのみ設定でOK
※ListViewを継承することによって色んな機能を使えるようになる
urlpatterns = [
url('member/', ShowView.as_view(), name='member'),
]
※ as_view()
というメソッドを持っている
<ul>
{% for item in object_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
※ ListViewではデフォルトでオブジェクトのリストをコンテキスト内のobject_listという変数に保存
※ ListViewはデフォルトで全件取得する。分割で一覧を取得する場合は、paginate_by
を用いる
object_listという変数名を変えるには以下のように変更かける。
context_object_name = 'XXXX'
コンテキストを拡張
- ビューにある
get_context_data()
で拡張できる
from django.views.generic import ListView
from .models import Person
class PersonList(ListView):
model = Person
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['taro'] = Person.objects.filter(name='Taro').first()
return context
※ returnで context
を返却するのを忘れないように
クエリセットを指定
from django.views.generic import ListView
from .models import Person
class PersonList(ListView):
model = Person
queryset = Person.objects.order_by('-name')
-
setup()
-
dispatch()
-
http_method_not_allowed()
-
get_template_names()
-
get_queryset()
-
get_context_object_name()
-
get_context_data()
-
get()
-
render_to_response()
-
queryset
- リストで表示したいデータをいれる
- 設定されていないときはすべてのレコードを取得
Contextについて
- XX
- get_context_data()
- paginator, page_obj
- object_list
- orderby_records
- view
- context_object_name : querysetの名前を指定。(デフォは「modelNAME_list」)
DetailView
- DBに入っている個別データを取得する時
- 個別データに紐づいているprimary key をurlpatternsで指定することによりデータを取り出す
class DetailView(DetailView):
model = TestModel
template_name = 'detail.html'
URLを作成する
urlpatterns = [
path('detail/<int:pk>', DetailView.as_view(), name='detail')
]
※ <int:pk>
が肝
{% extends 'base.html' %}
{% block header %}
{% endblock header %}
{% block content %}
<p>タイトル: {{ object.name }}</p>
<p>タイトル: {{ testmodel.name }}</p>
{% endblock content %}
※ {{ object.XXX }} か {{ modelで指定した変数の小文字.XXX }} で値の取得可能
CreateView
- データ作成(DBにレコード追加)
- タグでフォームをつくることで送信ボタンを押すと簡単にデータ作成可能
class CreateData(CreateView):
template_name = 'create.html' #使用するテンプレート
model = TestModel #使用するモデルクラス
field= ('age', 'name') #モデルから引っ張ってくるカラム
success_url=reverse_lazy('list') #成功したときの遷移先
※ field
はユーザーが入力する項目。すべて入力させるなら __all__
を設定
※ success_urlは投稿完了時に遷移するページを指定する
※ ここはフォームで入力をするイメージ
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="create">
</form>
※ {{ form.as_p }}を記述することで自動で入力フォームが生成
※ form.as_p
は各項目をpタグでくくる
DeleteView
- 「CreateView」や「UpdateView」と似ている
- データベースのデータを削除することに特化したクラスベースビュー。データ削除時の確認画面を表示できる。
from django.views.generic.edit import DeleteView
class NippoDeleteView(DeleteView):
template_name = "nippo/nippo-delete.html"
model = NippoModel
success_url = reverse_lazy("list")
※ XX
urlpatterns = [
path("delete/<int:pk>/", TestDeleteView.as_view(), name="delete"),
]
<form method="post">
{% csrf_token %}
<p>このテーブルを削除しますか?</p>
<table class="table table-sm table-bordered table-responsive">
<tbody>
<tr>
<th>ID</th>
<td>{{ object.id }}</td>
</tr>
<tr>
<th>テーブル名</th>
<td>{{ object.name }}</td>
</tr>
</tbody>
</table>
<input class="btn btn-danger" type="submit" value="削除する">
</form>
UpdateView
- データベースのデータを更新することに特化したクラスベースビュー。既存データの修正が可能。
from django.views.generic.edit import UpdateView
class MemberUpdateView(UpdateView):
model = Member
fields = ('name', 'age')
template_name_suffix = '_update_form'
success_url = reverse_lazy('list')
※ model : データを登録する対象のモデル名
※ fields : データを登録するフィールドの一覧("all"を指定するとすべてのフィールドが対象)
※ template_name_suffix : 使用するテンプレートHTMLファイルの末尾
※ success_url : ポスト成功時のリダイレクト先
FormView
- フォーム作成に特化したクラスベースビュー。クラス変数form_classに設定したフォームの表示や、POST時のバリデーションなどが実装済み。
from django import forms
class MemberForm(forms.Form):
name = forms.CharField(max_length=100, label = '名前')
age = forms.IntegerField(label='年齢')
from django.views.generic.edit import FormView
from .forms import MemberForm
class MemberFormView(FormView):
# テンプレート名の設定
template_name = 'app/form.html'
# POST時の線先の設定
success_url = '/static/app/top.html'
# フォームの設定
form_class = MemberForm
ベーステンプレート
- XXX
ページネーション機能
API
必要なライブラリ
pip install django
pip install djangorestframework
pip install django-filter
INSTALLED_APPS = (
...
'appblog',
'rest_framework',
)
※ rest_framework
をINSTALLED_APPSに追加
REST APIを作成するには最低限以下の3つを定義する必要があります。
ここさえ抑えておけばOK.
- Serializer
- ViewSet
- URL pattern
Serializerは「Modelをどのようにシリアライズ(・デシリアライズ)するかを決めるためのもの」、ViewSetは「APIのクエリーをどう解釈するかを決めるためのもの」、そしてURL Patternは「DjangoにURLのパターンを教えるためのもの」です。これらをAPI化したいModelに対してそれぞれ定義していきます。
Serializer
- シリアライズ (モデルオブジェクトをJSONに変換)やデシリアライズ (JSONをモデルオブジェクトに変換) という工程
- Serializerはデータベースの保存などの機能がとても豊富
# coding: utf-8
from rest_framework import serializers
from .models import User, Entry
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('name', 'mail')
class EntrySerializer(serializers.ModelSerializer):
class Meta:
model = Entry
fields = ('title', 'body', 'created_at', 'status', 'author')
※ シリアライザクラスの条件は、ModelSerializer ,listSerializer ,Serializer のいずれかを継承する必要があります。
- ModelSerializer: Modelクラスで定義しているフィールドをJSON文字列と変換するシリアライザとして利用する。
- listSerializer: 複数のModelリソースを利用する。
- Serializer: Modelを利用せず自分で定義した形でJSON文字列と変換するシリアライザとして利用する。
View
- DjangoRESTframeworkの場合は、JSON形式 のリクエストを受け取り、JSON形式のレスポンスを返す役割
- DjangoRESTframeworkでViewクラスを作る時は、以下のいずれかを継承
-
views.APIView
: -
generics.APIView
: -
viewsets.ModelViewSet
: ひとつのクラスで(エンドポイントの切り替えて)CRUDの動き全てに対応可
-
# coding: utf-8
import django_filters
from rest_framework import viewsets, filters
from .models import User, Entry
from .serializer import UserSerializer, EntrySerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class EntryViewSet(viewsets.ModelViewSet):
queryset = Entry.objects.all()
serializer_class = EntrySerializer
※ viewsets.ModelViewSet
※ queryset
とserializer_class
を設定する
※ queryset :
※ serializer_class : 利用するシリアライズ
URL Pattern
# coding: utf-8
from django.conf.urls import url, include
from django.contrib import admin
from blog.urls import router as blog_router
urlpatterns = [
url('admin/', admin.site.urls),
# blog.urlsをincludeする
url('api/', include(blog_router.urls)),
]
# coding: utf-8
from rest_framework import routers
from .views import UserViewSet, EntryViewSet
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'entries', EntryViewSet)
※ ModelViewSetを継承するViewクラスの場合、ルーティングも簡素に記述する事が可能
※ DRFではAPIへのルーティングを行うためのRouterクラスを実装する必要があります。
urls.py に Router クラスをインスタンス化し、ViewSets オブジェクトをルーティングします。
関数ベース
from rest_framework.response import Response
from rest_framework.decorators import api_view
@api_view(['GET', 'POST'])
def hello_world(request):
if request.method == 'POST':
return Response({"message": "Got some data!", "data": request.data})
return Response({"message": "Hello, world!"})
※ 通常の Django は HttpResponse というクラスで返していたと思いますが、DRFでは Response というクラスで返却します。
※ クラス型のビューにはパーミッションやパーサなど複数の設定項目があるのですが、 関数型のビューには同じようにデコレータを使って設定することになります。
これがシンプルでいいかも
Docker
Docker ( Django + MySQL + Nginx )
AWS Fargate
参考資料