2
2

Django を始めたい方へ

Posted at

はじめに

※随時更新予定です (※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の繋込みを行う

urls.py
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の繋ぎ込みをする

urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('hello/', views.helloworldfunction),
]

[8]. アプリの 「view.py」を編集する

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ディレクトリ作成イメージ

index.html
<!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
  • ビュー作成方法

    • クラスベースビュー (実装が容易だから基本こっち)
    • 関数ベースビュー (細かい制御をしたいとき)

クラスベースの書き方

views.py
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に記述する

.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管理サイトでは、あらかじめ登録されているモデルに対してのみ、データの追加や削除、編集ができる
.py
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

.py
@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 は必須
views.py

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() メソッドによって取得される
urls.py
path('member/', views.MemberList.as_view(), name='member')

ListView

データを表示される際に使用

views.py
from django.views.generic import ListView
from todoapp.models import TestModel
 
class ShowView(ListView):
    model = TestModel
    template_name = 'member_list.html'

※最低限modelのみ設定でOK
※ListViewを継承することによって色んな機能を使えるようになる

urls.py
urlpatterns = [
    url('member/', ShowView.as_view(), name='member'),
]

as_view()というメソッドを持っている

index.html
<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で指定することによりデータを取り出す
views.py
class DetailView(DetailView):
    model = TestModel 
    template_name = 'detail.html'

URLを作成する

urls.py
urlpatterns = [
    path('detail/<int:pk>', DetailView.as_view(), name='detail')
]

<int:pk>が肝

detail.html
{% 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にレコード追加)
  • タグでフォームをつくることで送信ボタンを押すと簡単にデータ作成可能
views.py
class CreateData(CreateView):
    template_name = 'create.html' #使用するテンプレート
    model = TestModel #使用するモデルクラス
    field= ('age', 'name') #モデルから引っ張ってくるカラム
    success_url=reverse_lazy('list') #成功したときの遷移先

fieldはユーザーが入力する項目。すべて入力させるなら __all__を設定
※ success_urlは投稿完了時に遷移するページを指定する
※ ここはフォームで入力をするイメージ

.create.html
<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 
settings.py
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はデータベースの保存などの機能がとても豊富
serializer.py
# 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の動き全てに対応可
view.py
# 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
querysetserializer_classを設定する
※ queryset :
※ serializer_class : 利用するシリアライズ

URL Pattern

[Project]/urls.py
# 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)),
]
[app]/urls.py
# 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

参考資料

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2