このドキュメントでは、Django の Class Based View について、 CRUD の実装方法について具体的に説明します。Djangoは、Webアプリケーションの開発に使用されるPythonのフレームワークの1つであり、Class Based Viewを使用することで、効率的にWebアプリケーションを開発することができます。
Contents
class based view での CRUD とは
CRUDとは、Create(作成)、Read(読み取り)、Update(更新)、Delete(削除)の頭文字を取ったもので、Webアプリケーションにおいて、データの作成、読み取り、更新、削除の機能を提供することを指します。
class based view で CRUDの実装方法
以下の手順に従って、CRUDを実装することができます。
models.py
にモデルを定義する。forms.py
にフォームを定義する。views.py
にCreate View、Retrieve View、Update View、Delete Viewを定義する。urls.py
にURLを設定する。
1. models.py
にモデルを定義する
まずは、CRUDで使用するモデルを定義します。モデルは、データベースのテーブルに対応するPythonのクラスです。例えば、以下のようなmodels.py
ファイルを作成します。
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
published_date = models.DateField()
この例では、Book
というモデルを定義しています。Book
モデルは、書籍のタイトル、著者、出版日を表す3つのフィールドを持っています。
2. forms.py
にフォームを定義する
次に、CRUDで使用するフォームを定義します。フォームは、WebページのHTMLフォームと対応するPythonのクラスであり、ユーザーが入力したデータを受け取り、バリデーションを行います。例えば、以下のようなforms.py
ファイルを作成します。
from django import forms
from .models import Book
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title', 'author', 'published_date']
この例では、BookForm
というフォームを定義しています。BookForm
は、Book
モデルと対応するフォームであり、title
、author
、published_date
の3つのフィールドを持ちます。
3. views.py
にCreate View、Retrieve View、Update View、Delete Viewを定義する
次に、Create View、Retrieve View、Update View、Delete Viewを定義します。それぞれのViewには、CreateView
、DetailView
、UpdateView
、DeleteView
というクラスを継承し、model
、form_class
、success_url
などの属性を指定します。
Create View
Create Viewは、データの作成を行うためのViewです。例えば、以下のようなviews.py
ファイルを作成します。
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .models import Book
from .forms import BookForm
class BookCreateView(CreateView):
model = Book
form_class = BookForm
success_url = reverse_lazy('book_list')
この例では、BookCreateView
というCreate Viewを定義しています。BookCreateView
では、Book
モデルとBookForm
フォームを使用し、データの保存が成功した場合にbook_list
というURLに遷移します。
Retrieve View
Retrieve Viewは、データの読み取りを行うためのViewです。例えば、以下のようなviews.py
ファイルを作成します。
from django.views.generic.detail import DetailView
from .models import Book
class BookDetailView(DetailView):
model = Book
この例では、BookDetailView
というRetrieve Viewを定義しています。BookDetailView
では、Book
モデルを使用し、book_detail.html
というテンプレートを表示します。
Update View
Update Viewは、データの更新を行うためのViewです。例えば、以下のようなviews.py
ファイルを作成します。
from django.views.generic.edit import UpdateView
from django.urls import reverse_lazy
from .models import Book
from .forms import BookForm
class BookUpdateView(UpdateView):
model = Book
form_class = BookForm
success_url = reverse_lazy('book_list')
この例では、BookUpdateView
というUpdate Viewを定義しています。BookUpdateView
では、Book
モデルとBookForm
フォームを使用し、データの保存が成功した場合にbook_list
というURLに遷移します。
Delete View
Delete Viewは、データの削除を行うためのViewです。例えば、以下のようなviews.py
ファイルを作成します。
from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy
from .models import Book
class BookDeleteView(DeleteView):
model = Book
success_url = reverse_lazy('book_list')
この例では、BookDeleteView
というDelete Viewを定義しています。BookDeleteView
では、Book
モデルを使用し、データの削除が成功した場合にbook_list
というURLに遷移します。
4. urls.py
にURLを設定する
最後に、urls.py
で、各Viewに対応するURLを設定します。例えば、以下のようなurls.py
ファイルを作成します。
from django.urls import path
from .views import BookListView, BookCreateView, BookDetailView, BookUpdateView, BookDeleteView
urlpatterns = [
path('', BookListView.as_view(), name='book_list'),
path('create/', BookCreateView.as_view(), name='book_create'),
path('<int:pk>/', BookDetailView.as_view(), name='book_detail'),
path('<int:pk>/update/', BookUpdateView.as_view(), name='book_update'),
path('<int:pk>/delete/', BookDeleteView.as_view(), name='book_delete'),
]
この例では、urlpatterns
に、BookListView
、BookCreateView
、BookDetailView
、BookUpdateView
、BookDeleteView
に対応するURLを設定しています。
これで、CRUDの実装が完了しました。
テンプレートの作成
テンプレートのディレクトリ階層については、Djangoの公式ドキュメントにおいても特に明確に定められていません。しかし、一般的には以下のようなディレクトリ階層を採用することが多いです。
myproject/
myproject/
settings.py
urls.py
...
myapp/
templates/
myapp/
base.html
book_list.html
book_detail.html
book_form.html
...
views.py
models.py
...
...
このように、アプリケーションごとにtemplates
ディレクトリを作成し、その中にテンプレートを格納することが一般的です。さらに、templates
ディレクトリ内に、各アプリケーションごとにサブディレクトリを作成し、その中にテンプレートを格納することが推奨されています。これにより、複数のアプリケーションを持つプロジェクトでも、テンプレートの管理が容易になります。
また、base.html
など、共通で使用するテンプレートは、アプリケーションごとに1つのファイルにまとめることが多いです。これにより、重複するコードを減らし、テンプレートの保守性を高めることができます。
テンプレートの継承
Djangoアプリケーションでは、各Viewで表示するHTMLのテンプレートを作成する必要があります。テンプレートは、Webページの構成要素を定義し、Viewが生成したデータを表示するためのものです。
Djangoにおけるテンプレートは、HTMLを拡張したものであり、テンプレートエンジンによって処理されます。テンプレートエンジンは、テンプレート内のタグや変数を解釈して、HTMLに変換します。Djangoには、デフォルトでdjango.template.backends.django.DjangoTemplates
というテンプレートエンジンが用意されており、柔軟なテンプレート処理を実現しています。
テンプレートを作成するには、まずテンプレートを継承するベーステンプレートを定義します。これにより、各Viewで表示するHTMLの共通部分をまとめることができます。例えば、以下のようなbase.html
テンプレートを作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
テンプレートの定義
次に、各Viewで表示するHTMLのテンプレートを定義します。例えば、以下のようなbook_list.html
テンプレートを作成します。
{% extends 'base.html' %}
{% block title %}書籍一覧{% endblock %}
{% block content %}
<h1>書籍一覧</h1>
<ul>
{% for book in object_list %}
<li><a href="{% url 'book_detail' book.pk %}">{{ book.title }}</a></li>
{% empty %}
<li>No books yet.</li>
{% endfor %}
</ul>
<a href="{% url 'book_create' %}">新規作成</a>
{% endblock %}
この例では、{% extends 'base.html' %}
によって、base.html
を継承しています。{% block title %}
と{% endblock %}
で囲まれた部分は、ページのタイトルを定義するための部分になります。{% block content %}
と{% endblock %}
で囲まれた部分が、各Viewで表示するコンテンツになります。{% for %}
と{% endfor %}
で囲まれた部分は、BookListView
で表示する書籍一覧の部分になります。{% empty %}
は、書籍が存在しない場合に表示される文言です。{% url %}
は、URLを動的に生成するためのテンプレートタグです。
BookDetailView
で表示するテンプレートも同様に定義します。例えば、以下のようなbook_detail.html
テンプレートを作成します。
{% extends 'base.html' %}
{% block title %}{{ object.title }}{% endblock %}
{% block content %}
<h1>{{ object.title }}</h1>
<p><strong>著者:</strong> {{ object.author }}</p>
<p><strong>出版日:</strong> {{ object.published_date }}</p>
<a href="{% url 'book_update' object.pk %}">編集</a>
<form action="{% url 'book_delete' object.pk %}" method="post">
{% csrf_token %}
<input type="submit" value="削除">
</form>
{% endblock %}
この例では、{% extends 'base.html' %}
によって、base.html
を継承しています。{% block title %}
と{% endblock %}
で囲まれた部分は、ページのタイトルを定義するための部分になります。{% block content %}
と{% endblock %}
で囲まれた部分が、各Viewで表示するコンテンツになります。{{ object }}
は、BookDetailView
で指定したmodel
のインスタンスになります。{% url 'book_update' object.pk %}
は、BookUpdateView
に対応するURLを動的に生成するためのテンプレートタグです。<form>
タグは、書籍の削除を行うためのフォームです。{% csrf_token %}
は、セキュリティ対策のために必要なテンプレートタグです。
BookCreateView
とBookUpdateView
で表示するテンプレートも同様に定義します。例えば、以下のようなbook_form.html
テンプレートを作成します。
{% extends 'base.html' %}
{% block title %}{{ form_title }}{% endblock %}
{% block content %}
<h1>{{ form_title }}</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="保存">
</form>
{% endblock %}
この例では、{% extends 'base.html' %}
によって、base.html
を継承しています。{% block title %}
と{% endblock %}
で囲まれた部分は、ページのタイトルを定義するための部分になります。{% block content %}
と{% endblock %}
で囲まれた部分が、各Viewで表示するコンテンツになります。{{ form_title }}
は、フォームのタイトルを表します。{{ form.as_p }}
は、フォームのフィールドを<p>
タグで囲んで表示するためのテンプレートタグです。<form>
タグは、フォームの送信を行うためのものです。
BookDeleteView
では、テンプレートを定義する必要はありません。
フォームのバリデーション
Djangoのフォームは、クライアントサイドとサーバーサイドの両方でバリデーションを行うことができます。クライアントサイドのバリデーションは、JavaScriptを用いてブラウザ上で行われます。一方、サーバーサイドのバリデーションは、View内で行われます。
フォームのバリデーションは、forms.py
内で行います。例えば、以下のようなBookForm
を定義します。
from django import forms
from .models import Book
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ('title', 'author', 'published_date')
def clean_title(self):
title = self.cleaned_data['title']
if len(title) < 3:
raise forms.ValidationError('タイトルは3文字以上で入力してください。')
return title
この例では、BookForm
というフォームを定義しています。Meta
クラスには、フォームで使用するモデルとフィールドを指定します。clean_title
メソッドは、title
フィールドのバリデーションを行うためのメソッドです。self.cleaned_data
には、フォームから送信されたデータが含まれています。len(title)
でタイトルの文字数を取得し、3文字未満の場合は、forms.ValidationError
でエラーを発生させます。
Viewでは、以下のようにバリデーションを行います。
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Book
from .forms import BookForm
class BookCreateView(CreateView):
model = Book
form_class = BookForm
template_name = 'book_form.html'
success_url = reverse_lazy('book_list')
class BookUpdateView(UpdateView):
model = Book
form_class = BookForm
template_name = 'book_form.html'
success_url = reverse_lazy('book_list')
class BookDeleteView(DeleteView):
model = Book
success_url = reverse_lazy('book_list')
この例では、BookCreateView
とBookUpdateView
で使用するフォームとして、BookForm
を指定しています。フォームのバリデーションは、BookForm
のclean_title
メソッドで実行されます。バリデーションに失敗した場合は、エラーメッセージが表示されます。
フォームのバリデーションは、データの正当性を確認するために非常に重要な処理です。クライアントサイドのバリデーションだけでなく、サーバーサイドのバリデーションも行うことで、より信頼性の高いWebアプリケーションを開発することができます。
class based view のクラスのカスタマイズ
DjangoのClass-Based View(以下、CBV)の中でも、よく使われるビューには以下の4つがあります。
- CreateView
- UpdateView
- DeleteView
- DetailView
それぞれのビューについて、カスタマイズ方法を説明します。
CreateViewのカスタマイズ
CreateView
は、モデルに新しいレコードを追加するためのビューです。フォームを提供し、フォームの入力値をモデルの新しいレコードとして保存します。
CreateView
をカスタマイズするには、以下の手順を踏みます。
CreateView
を継承する- ビューで利用するフォームクラスを指定する
- フォームの入力値を保存するためのメソッドをオーバーライドする
例えば、以下のようにします。
from django.views.generic.edit import CreateView
from .models import Book
from .forms import BookForm
class BookCreateView(CreateView):
model = Book
form_class = BookForm
template_name = 'book_create.html'
def form_valid(self, form):
# フォームの入力値を保存する処理を実装する
return super().form_valid(form)
この例では、BookCreateView
というクラスを定義しています。CreateView
を継承しているため、model
属性にBook
モデルを、form_class
属性にはフォームクラスを指定することで、フォームを提供し、フォームの入力値をBook
モデルの新しいレコードとして保存するViewとして利用することができます。template_name
属性には、テンプレートファイルのパスを指定します。
form_valid
メソッドをオーバーライドすることで、フォームの入力値を保存するための処理をカスタマイズすることができます。例えば、以下のように書くことで、フォームの入力値を保存する前に、ユーザー名をログに出力することができます。
def form_valid(self, form):
logger.info('User {} created a new book'.format(self.request.user.username))
return super().form_valid(form)
UpdateViewのカスタマイズ
UpdateView
は、モデルの既存のレコードを更新するためのビューです。フォームを提供し、フォームの入力値をモデルの既存のレコードとして保存します。
UpdateView
をカスタマイズするには、以下の手順を踏みます。
UpdateView
を継承する- ビューで利用するフォームクラスを指定する
- フォームの入力値を保存するためのメソッドをオーバーライドする
例えば、以下のようにします。
from django.views.generic.edit import UpdateView
from .models import Book
from .forms import BookForm
class BookUpdateView(UpdateView):
model = Book
form_class = BookForm
template_name = 'book_update.html'
def form_valid(self, form):
# フォームの入力値を保存する処理を実装する
return super().form_valid(form)
この例では、BookUpdateView
というクラスを定義しています。UpdateView
を継承しているため、model
属性にBook
モデルを、form_class
属性にはフォームクラスを指定することで、フォームを提供し、フォームの入力値をBook
モデルの既存のレコードとして保存するViewとして利用することができます。template_name
属性には、テンプレートファイルのパスを指定します。
form_valid
メソッドをオーバーライドすることで、フォームの入力値を保存するための処理をカスタマイズすることができます。
DeleteViewのカスタマイズ
DeleteView
は、モデルの既存のレコードを削除するためのビューです。確認画面を提供し、モデルの既存のレコードを削除します。
DeleteView
をカスタマイズするには、以下の手順を踏みます。
DeleteView
を継承する- ビューで利用するモデルを指定する
- モデルの既存のレコードを削除するためのメソッドをオーバーライドする
例えば、以下のようにします。
from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy
from .models import Book
class BookDeleteView(DeleteView):
model = Book
template_name = 'book_delete.html'
success_url = reverse_lazy('book_list')
def delete(self, request, *args, **kwargs):
# モデルの既存のレコードを削除する処理を実装する
return super().delete(request, *args, **kwargs)
この例では、BookDeleteView
というクラスを定義しています。DeleteView
を継承しているため、model
属性にBook
モデルを指定することで、モデルの既存のレコードを削除するViewとして利用することができます。template_name
属性には、テンプレートファイルのパスを指定します。success_url
属性には、レコードの削除に成功した場合に遷移するURLを指定します。
delete
メソッドをオーバーライドすることで、モデルの既存のレコードを削除するための処理をカスタマイズすることができます。
DetailViewのカスタマイズ
DetailView
は、モデルの既存のレコードの詳細を表示するためのビューです。
DetailView
をカスタマイズするには、以下の手順を踏みます。
DetailView
を継承する- ビューで利用するモデルを指定する
- テンプレートに渡すコンテキストデータをカスタマイズするためのメソッドをオーバーライドする
例えば、以下のようにします。
from django.views.generic import DetailView
from .models import Book
class BookDetailView(DetailView):
model = Book
template_name = 'book_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# テンプレートに渡す変数を追加する処理を実装する
return context
この例では、BookDetailView
というクラスを定義しています。DetailView
を継承しているため、model
属性にBook
モデルを指定することで、モデルの既存のレコードの詳細を表示するViewとして利用することができます。template_name
属性には、テンプレートファイルのパスを指定します。
get_context_data
メソッドをオーバーライドすることで、テンプレートに渡すコンテキストデータをカスタマイズすることができます。例えば、以下のように書くことで、BookDetailView
にbook
という変数を追加することができます。
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
book = Book.objects.get(pk=self.kwargs['pk'])
context['book'] = book
return context
Class based viewの利点と欠点まとめ
DjangoのClass-Based View(以下、CBV)は、Djangoアプリケーションの開発において、より抽象化された方法でViewを定義することができる機能です。CBVを使用すると、標準的なViewよりも多くの機能を提供することができます。
CBVの利点には、以下のようなものがあります。
- コードの再利用性が高い
- 共通のコードをまとめることができる
- クラス継承を使用することで、複雑なViewを簡単に実装できる
- 柔軟性が高い
一方、CBVの欠点としては、以下のようなものがあります。
- Viewの機能が増えることで、初心者には理解しにくくなる
- Viewの動作がオーバーライドされることがあるため、デバッグが難しくなることがある
- テンプレートの変数の渡し方が複雑になることがある
CBVは、Django開発の効率化に大きく貢献している機能の一つです。CBVを使用することで、より簡潔で柔軟性の高いアプリケーションを開発することができます。