【Django3 入門】クラスベースビューの書き方

Django

「views.py」の書き方には「関数ベース」と「クラスベース」がありますが、
今回はクラスベースでのビューの書き方をみていきます。

前提

・「my_app」というアプリを作成、「settings.py」に登録
・「manage.py」と同じ階層に「templates」「static」ディレクトリを作成し、読込先のディレクトリのパスをこちらに変更

.
.
.
from pathlib import Path
import os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates')
STATIC_DIR = os.path.join(BASE_DIR, 'static').
.
.
.
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [TEMPLATE_DIR,],
.
.
.
STATIC_URL = '/static/'
STATICFILES_DIRS = (
    STATIC_DIR,
)


・プロジェクトディレクトリの「urls.py」に以下のようにパスを登録

path('my_app/', include('my_app.urls')),

基本の利用法

get()メソッド

「views.py」の修正

from django.shortcuts import render
from django.views.generic.base import (
    View,
)

# Create your views here.
class IndexView(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'index.html')


「urls.py」の作成

from django.urls import path
from .views import IndexView

app_name = 'my_app'

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


クラス.as_view() と指定することで、
クラスの get()メソッドを呼び出すことができます。


HTMLファイルの作成

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
</head>
<body>
    <div class="container py-4">
        {% block content %}
        {% endblock %}
    </div>
</body>
</html>
{% extends 'base.html' %}
{% block content %}
<h1>Index</h1>
{% endblock %}


「/my_app/index」にアクセスしたときの表示↓

post()メソッド

モデルの作成

from django.db import models

# Create your models here.

class BaseModel(models.Model):
    created_at = models.DateTimeField()
    updated_at = models.DateTimeField()

    class Meta:
        abstract = True

class Book(BaseModel):
    name = models.CharField(max_length=255)
    description = models.CharField(max_length=1000)
    price = models.IntegerField()

    class Meta:
        db_table = 'books'


マイグレーションの実行

python manage.py makemigrations my_app
python manage.py migrate


「forms.py」の作成

from django import forms
from .models import Book
from datetime import datetime

class BookForm(forms.ModelForm):
    class Meta:
        model = Book
        fields = ['name', 'description', 'price']

    def save(self, *args, **kwargs):
        obj = super(BookForm, self).save(commit=False)
        obj.created_at = datetime.now()
        obj.updated_at = datetime.now()
        obj.save()
        return obj


「views.py」の修正

from django.shortcuts import render
from django.views.generic.base import (
    View,
)
from . import forms

# Create your views here.
class IndexView(View):
    def get(self, request, *args, **kwargs):
        book_form = forms.BookForm()
        return render(request, 'index.html', context={
            'book_form': book_form,
        })

    def post(self, request, *args, **kwargs):
        book_form = forms.BookForm(request.POST or None)
        if book_form.is_valid():
            book_form.save()
        return render(request, 'index.html', context={
            'book_form': book_form
        })


「index.html」の修正

{% extends 'base.html' %}
{% block content %}
<form method="POST">
    {% csrf_token %}
    {{ book_form.as_p }}
    <input type="submit" value="送信">
</form>
{% endblock %}


「/my_app/index」にアクセスし、任意の値を入力して送信します。


すると、正常にデータベースに登録できているのが確認できます。

TemplateView

HTMLファイルの作成

{% extends 'base.html' %}
{% block content %}
<h1>Home</h1>
{% endblock %}

単純に特定のファイルを表示させたい場合

パスの追加

from django.views.generic.base import TemplateView
.
.
.
urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    path('home', TemplateView.as_view(template_name='home.html'), name='home'),
]


「/my_app/home」にアクセスすると「Home」と表示されます。

テンプレートにデータを渡したい場合

「home.html」の修正

{% extends 'base.html' %}
{% block content %}
<h1>Home</h1>
<p>現在時刻: {{ time }}</p>
{% endblock %}



「views.py」の修正

from django.shortcuts import render
from django.views.generic.base import (
    View, TemplateView
)
from datetime import datetime
.
.
.
class HomeView(TemplateView):
    template_name = 'home.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['time'] = datetime.now()
        return context

パスの追加

from django.urls import path
from .views import IndexView, HomeView
# from django.views.generic.base import TemplateView

app_name = 'my_app'

urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    # path('home', TemplateView.as_view(template_name='home.html'), name='home'),
    path('home', HomeView.as_view(), name='home'),
]


「/my_app/home」にアクセスすると、以下のように表示されます。

URLパラメータの利用

パスの修正

from django.urls import path
from .views import IndexView, HomeView

app_name = 'my_app'

urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    path('home/<name>', HomeView.as_view(), name='home'),
]


「views.py」の修正

.
.
.
class HomeView(TemplateView):
    template_name = 'home.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['name'] = kwargs.get('name')
        context['time'] = datetime.now()
        return context


「home.html」の修正

{% extends 'base.html' %}
{% block content %}
<h1>Home</h1>
<p>こんにちは、{{ name }}さん</p>
<p>現在時刻: {{ time }}</p>
{% endblock %}


「/my_app/home/taro」にアクセスすると、URLパラメータを渡せていることが確認できます。

DetailView

「views.py」の修正

.
.
.
from .models import Book
from django.views.generic.detail import DetailView
.
.
.
class BookDetailView(DetailView):
    model = Book
    template_name = 'book.html'


パスの追加

from django.urls import path
from .views import BookDetailView, IndexView, HomeView

app_name = 'my_app'

urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    path('home/<name>', HomeView.as_view(), name='home'),
    path('detail_book/<int:pk>', BookDetailView.as_view(), name='detail_book'),
]


HTMLファイルの作成

{% extends 'base.html' %}
{% block content %}

<p>{{ object.name }}</p>
<p>{{ object.description }}</p>
<p>{{ object.price }}円</p>

{% endblock %}


「/my_app/detail_book/1」にアクセスしたときの表示

ListView

「views.py」の修正

.
.
.
from django.views.generic.list import ListView
.
.
.
class BookListView(ListView):
    model = Book
    template_name = 'book_list.html'


パスの追加

from django.urls import path
from .views import BookDetailView, BookListView, IndexView, HomeView

app_name = 'my_app'

urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    path('home/<name>', HomeView.as_view(), name='home'),
    path('detail_book/<int:pk>', BookDetailView.as_view(), name='detail_book'),
    path('list_books', BookListView.as_view(), name='list_books'),
]


HTMLファイルの作成

{% extends 'base.html' %}
{% block content %}

<table class="table">
    <tbody>
        {% for object in object_list %}
            <tr>
                <td>名前: <a href="{% url 'my_app:detail_book' object.id %}">{{ object.name }}</a></td>
                <td>金額: {{ object.price }}</td>
            </tr>
        {% endfor %}
    </tbody>
</table>

{% endblock %}


「/my_app/list_books」にアクセスしたときの表示

並び替え

金額順で並び替え

.
.
.
class BookListView(ListView):
    model = Book
    template_name = 'book_list.html'

    def get_queryset(self):
        qs = super(BookListView, self).get_queryset()
        qs = qs.order_by('price')

        return qs

絞り込み

.
.
.
class BookListView(ListView):
    model = Book
    template_name = 'book_list.html'

    def get_queryset(self):
        qs = super(BookListView, self).get_queryset()
        qs = qs.filter(name__startswith='ハリーポッター')
        qs = qs.order_by('price')

        return qs

CreateView

「views.py」の修正

.
.
.
from django.views.generic.edit import (
    CreateView
)
.
.
.
class BookCreateView(CreateView):
    model = Book
    fields = ['name', 'description', 'price']
    template_name = 'add_book.html'

    def form_valid(self, form):
        form.instance.created_at = datetime.now()
        form.instance.updated_at = datetime.now()
        return super(BookCreateView, self).form_valid(form)


パスの追加

from django.urls import path
from .views import BookCreateView, BookDetailView, BookListView, IndexView, HomeView

app_name = 'my_app'

urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    path('home/<name>', HomeView.as_view(), name='home'),
    path('detail_book/<int:pk>', BookDetailView.as_view(), name='detail_book'),
    path('list_books', BookListView.as_view(), name='list_books'),
    path('add_book', BookCreateView.as_view(), name='add_book'),
]


HTMLファイルの作成

{% extends 'base.html' %}
{% block content %}

<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="保存">
</form>

{% endblock %}

遷移先ページの指定

データを保存後に遷移するページの指定方法は2通りあります。

「views.py」で指定

.
.
.
from django.urls import reverse_lazy
.
.
.
class BookCreateView(CreateView):
    model = Book
    fields = ['name', 'description', 'price']
    template_name = 'add_book.html'
    success_url = reverse_lazy('my_app:list_books')

    def form_valid(self, form):
.
.
.

「models.py」で指定

from django.db import models
from django.urls import reverse_lazy
.
.
.
class Book(BaseModel):
.
.
.
    def get_absolute_url(self):
        return reverse_lazy('my_app:list_books')




「/my_app/add_book」にアクセスし、任意の値を入力して保存すると、一覧ページに遷移します。

from django.db import models
from django.urls import reverse_lazy
.
.
.
class Book(BaseModel):
.
.
.
    def get_absolute_url(self):
        return reverse_lazy('my_app:list_books')

初期値の設定

「views.py」の修正

.
.
.
class BookCreateView(CreateView):
.
.
.
    def get_initial(self, **kwargs):
        initial = super(BookCreateView, self).get_initial(**kwargs)
        initial['name'] = 'sample'
        return initial


「/my_app/add_book」にアクセスしたときの表示

UpdateView

「forms.py」の修正

.
.
.
class BookUpdateForm(forms.ModelForm):
    class Meta:
        model = Book
        fields = ['name', 'description', 'price']

    def save(self, *args, **kwargs):
        obj = super(BookUpdateForm, self).save(commit=False)
        obj.save()
        return obj


「views.py」の修正

.
.
.
from django.views.generic.edit import (
    CreateView,
    UpdateView
)
.
.
.
class BookUpdateView(UpdateView):
    model = Book
    template_name = 'update_book.html'
    form_class = forms.BookUpdateForm

    def get_success_url(self):
        return reverse_lazy('my_app:list_books')


パスの追加

from django.urls import path
from .views import BookCreateView, BookDetailView, BookListView, BookUpdateView, IndexView, HomeView

app_name = 'my_app'

urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    path('home/<name>', HomeView.as_view(), name='home'),
    path('detail_book/<int:pk>', BookDetailView.as_view(), name='detail_book'),
    path('list_books', BookListView.as_view(), name='list_books'),
    path('add_book', BookCreateView.as_view(), name='add_book'),
    path('edit_book/<int:pk>', BookUpdateView.as_view(), name='edit_book'),
]



HTMLファイルの作成

{% extends 'base.html' %}
{% block content %}

<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="更新">
</form>

{% endblock %}


「book_list.html」の修正

{% extends 'base.html' %}
{% block content %}

<p><a href="{% url 'my_app:add_book' %}">データ追加</a></p>
<table class="table">
    <tbody>
        {% for object in object_list %}
            <tr>
                <td>名前: <a href="{% url 'my_app:detail_book' object.id %}">{{ object.name }}</a></td>
                <td>金額: {{ object.price }}</td>
                <td><a href="{% url 'my_app:edit_book' object.id %}">編集</a></td>
            </tr>
        {% endfor %}
    </tbody>
</table>

{% endblock %}

DeleteView

「views.py」の修正

.
.
.
from django.views.generic.edit import (
    CreateView,
    DeleteView,
    UpdateView
)
.
.
.
class BookDeleteView(DeleteView):
    model = Book
    template_name = 'delete_book.html'
    success_url = reverse_lazy('my_app:list_books')


パスの追加

from django.urls import path
from .views import BookCreateView, BookDeleteView, BookDetailView, BookListView, BookUpdateView, IndexView, HomeView

app_name = 'my_app'

urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    path('home/<name>', HomeView.as_view(), name='home'),
    path('detail_book/<int:pk>', BookDetailView.as_view(), name='detail_book'),
    path('list_books', BookListView.as_view(), name='list_books'),
    path('add_book', BookCreateView.as_view(), name='add_book'),
    path('edit_book/<int:pk>', BookUpdateView.as_view(), name='edit_book'),
    path('delete_book/<int:pk>', BookDeleteView.as_view(), name='delete_book'),
]


HTMLファイルの作成

{% extends 'base.html' %}
{% block content %}

<form method="POST">
    {% csrf_token %}
    <p>{{ object.name }}を削除しますか?</p>
    <input type="submit" value="削除">
</form>

{% endblock %}


「book_list.html」の修正

{% extends 'base.html' %}
{% block content %}

<p><a href="{% url 'my_app:add_book' %}">データ追加</a></p>
<table class="table">
    <tbody>
        {% for object in object_list %}
            <tr>
                <td>名前: <a href="{% url 'my_app:detail_book' object.id %}">{{ object.name }}</a></td>
                <td>金額: {{ object.price }}</td>
                <td><a href="{% url 'my_app:edit_book' object.id %}">編集</a></td>
                <td><a href="{% url 'my_app:delete_book' object.id %}">削除</a></td>
            </tr>
        {% endfor %}
    </tbody>
</table>

{% endblock %}

FormView

「views.py」の修正

.
.
.
from django.views.generic.edit import (
    CreateView,
    DeleteView,
    FormView,
    UpdateView
)
.
.
.
class BookFormView(FormView):
    template_name = 'form_book.html'
    form_class = forms.BookForm
    success_url = reverse_lazy('my_app:list_books')

    def form_valid(self, form):
        if form.is_valid():
            form.save()
        return super(BookFormView, self).form_valid(form)


パスの追加

from django.urls import path
from .views import BookCreateView, BookDeleteView, BookDetailView, BookFormView, BookListView, BookUpdateView, IndexView, HomeView

app_name = 'my_app'

urlpatterns = [
    path('index', IndexView.as_view(), name='index'),
    path('home/<name>', HomeView.as_view(), name='home'),
    path('detail_book/<int:pk>', BookDetailView.as_view(), name='detail_book'),
    path('list_books', BookListView.as_view(), name='list_books'),
    path('add_book', BookCreateView.as_view(), name='add_book'),
    path('edit_book/<int:pk>', BookUpdateView.as_view(), name='edit_book'),
    path('delete_book/<int:pk>', BookDeleteView.as_view(), name='delete_book'),
    path('book_form', BookFormView.as_view(), name='book_form'),
]


HTMLファイルの追加

{% extends 'base.html' %}
{% block content %}

<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="保存">
</form>

{% endblock %}


「/my_app/book_form」にアクセス後、値を入力して保存すると正常に反映されます。

RedirectView

基本的な使い方

「urls.py」で使用

「urls.py」の修正

.
.
.
from django.views.generic.base import RedirectView

urlpatterns = [
.
.
.
    path('google', RedirectView.as_view(url='https://google.co.jp'), name='google'),
]


「/my_app/google」にアクセスすると、グーグルのページにリダイレクトされます。

「views.py」で使用

「views.py」の修正

.
.
.
from django.views.generic.base import (
    RedirectView, View, TemplateView
)
.
.
.
class BookRedirectView(RedirectView):
    url = 'https://google.co.jp'


パスの追加

from django.urls import path
from .views import BookCreateView, BookDeleteView, BookDetailView, BookFormView, BookListView, BookRedirectView, BookUpdateView, IndexView, HomeView
.
.
.
urlpatterns = [
.
.
.
    path('book_redirect_view', BookRedirectView.as_view(), name='book_redirect_view'),
]


「/my_app/book_redirect_view」にアクセスすると、グーグルのページにリダイレクトされます。

URLパラメータの使用

「views.py」の修正

.
.
.
class BookRedirectView(RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        if 'pk' in kwargs:
            return reverse_lazy('my_app:detail_book', kwargs={'pk': kwargs['pk']})
        return reverse_lazy('my_app:list_books')


パスの追加

.
.
.
path('book_redirect_view', BookRedirectView.as_view(), name='book_redirect_view'),
path('book_redirect_view/<int:pk>', BookRedirectView.as_view(), name='book_redirect_view'),


「/my_app/book_redirect_view」にアクセスすると一覧ページに、
「/my_app/book_redirect_view/<id>」にアクセスすると詳細ページにリダイレクトされます。

SuccessMessageMixin

「update_book.html」の修正

{% extends 'base.html' %}
{% block content %}

{% if messages %}
    {% for message in messages %}
        {{ message.message }}
    {% endfor %}
{% endif %}

<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="更新">
</form>
<br>
<a href="{% url 'my_app:list_books' %}">一覧ページへ</a>

{% endblock %}

静的なメッセージ

「views.py」の修正

.
.
.
from django.contrib.messages.views import SuccessMessageMixin
.
.
.
class BookUpdateView(SuccessMessageMixin, UpdateView):
    model = Book
    template_name = 'update_book.html'
    form_class = forms.BookUpdateForm
    success_message = '更新しました'

    def get_success_url(self):
        return reverse_lazy('my_app:edit_book', kwargs={'pk': self.object.id})
.
.
.

動的なメッセージ

「views.py」の修正

.
.
.
class BookUpdateView(SuccessMessageMixin, UpdateView):
.
.
.
    def get_success_message(self, cleaned_data):
        return cleaned_data.get('name') + 'を更新しました'
.
.
.





今回は以上になります。
ご覧いただきありがとうございました!

続きはこちら↓

コメント

コンタクトフォーム

    タイトルとURLをコピーしました