ファイル CRUD(クラスベースビュー) - Django

urls

urls

pythonApp\class_crud\class_crud\books\urls.py

from django.urls import path
from . import views

app_name = 'books'

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

pythonApp\class_crud\class_crud\class_crud\urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', include('books.urls')),
]

templates

templates

pythonApp\class_crud\class_crud\books\templates\books\base.html

{% load static %}
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="{% static 'books\css\style.css' %}">
    <script src="{% static 'books/javascripts/script.js' %}"></script>
    <title>{% block title %}{% endblock %}</title>
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>

pythonApp\class_crud\class_crud\books\templates\books\create.html

{% extends "books/base.html" %}
{% block title %}books/create view{% endblock %}

{% block content %}
<h1>books/create</h1>

<table>
  <form method="post" novalidate>
    {% csrf_token %}
    
    {% for field in form %}
    <div class="field">{{field.label_tag}}{{field}}</div>
      {% for error in field.errors%}
      <p style="color: red;">{{error}}</p>
      {% endfor%}
    {% endfor %}
    <p><input type="submit" value="Save"></p>
  </form>
  <p>{{ request.session.title }}</p>
</table>
{% endblock %}

pythonApp\class_crud\class_crud\books\templates\books\delete.html

{% extends "books/base.html" %}
{% block title %}books/delete view{% endblock %}

{% block content %}
<h1>books/delete</h1>
<form method="post">
  {% csrf_token %}
  <p>データ "{{ object }}" を本当に削除しますか?</p>
  <input type="submit" value="確定">
</form>
{% endblock %}

pythonApp\class_crud\class_crud\books\templates\books\index.html

{% extends "books/base.html" %}
{% block title %}books/index{% endblock %}

{% block content %}
<h1>books/index</h1>
{% endblock %}

pythonApp\class_crud\class_crud\books\templates\books\list.html

{% extends "books/base.html" %}
{% block title %}books/list view{% endblock %}

{% block content %}
<h1>books/list</h1>
<p><a href="{% url 'books:create' %}">Books Add</a></p>
<table>
  <form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <tr>
      <th></th>
      <td><input type="submit" value="click"></td>
    </tr>
  </form>
</table>
<table>
  <thead>
      <th>ID</th>
      <th>タイトル</th>
      <th>body</th>
      <th></th>
  </thead>
  <tbody>
      {% for obj in books_list %}
      <tr>
          <td>{{ obj.id }}</td>
          <td>{{ obj.title }}</td>
          <td>{{ obj.body }}</td>
          <td>
            <a href="{% url 'books:show' obj.pk %}">Show</a>
            <a href="{% url 'books:update' obj.pk %}">Edit</a>
            <a href="{% url 'books:delete' obj.pk %}">Delete</a>
          </td>
      </tr>
      {% endfor %}
  </tbody>
</table>
<div class="pagination">
  <ul class="clearfix">
    {% if page_obj.has_previous %}
    <li><a href="?page=1">first</a></li>
    <li><a href="?page={{page_obj.previous_page_number}}">prev</a></li>
    {% endif %}

    {% if not page_obj.has_previous %}
    <li>first</li>
    <li>prev</li>
    {% endif %}

    <li><span class="current">[ {{page_obj.number}}/{{page_obj.paginator.num_pages}} ]</span></li>

    {% if page_obj.has_next %}
    <li><a href="?page={{page_obj.next_page_number}}">next</a></li>
    <li><a href="?page={{page_obj.paginator.num_pages}}">last</a></li>
    {% endif %}

    {% if not page_obj.has_next %}
    <li>next</li>
    <li>last</li>
    {% endif %}
  </ul>
</div><!-- /div.pagination -->
{% endblock %}

pythonApp\class_crud\class_crud\books\templates\books\show.html

{% extends "books/base.html" %}
{% block title %}books/show view{% endblock %}

{% block content %}
<h1>books/show</h1>
<table>
  <thead>
    <tr>
      <th>id</th>
      <th>title</th>
      <th>body</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>{{ object.id }}</td>
      <td>{{ object.title }}</td>
      <td>{{ object.body }}</td>
    </tr>
  </tbody>
</table>
{% endblock %}

pythonApp\class_crud\class_crud\books\templates\books\update.html

{% extends "books/base.html" %}
{% block title %}books/update view{% endblock %}

{% block content %}
<h1>books/update</h1>
<p>{{ object.id }}</p>
<table>
  <form method="post">
    {% csrf_token %}
    {{ form.as_table }}
    <tr>
      <td><input type="submit" value="Save"></td>
      <td></td>
    </tr>
    
  </form>
</table>
{% endblock %}

バリデーションの表示方法

エラー表示をまとめて表示するには次のようにします。

{% for field in form %}
  {{ field.errors }}
{% endfor %}

{{ form.errors }} と記述すると、フォームの属性名とエラーメッセージを表示します。

エラー表示をまとめて表示

pythonApp\class_crud\class_crud\books\templates\books\create.html

{% extends "books/base.html" %}
{% block title %}books/create view{% endblock %}

{% block content %}
<h1>books/create</h1>
<div>
{% for field in form %}
  {{ field.errors }}
{% endfor %}
</div>
<table>
  <form method="post" novalidate>
    {% csrf_token %}
    
    {% for field in form %}
    <div class="field">{{field.label_tag}}{{field}}</div>
      {% for error in field.errors%}
      <p style="color: red;">{{error}}</p>
      {% endfor%}
    {% endfor %}
    <p><input type="submit" value="Save"></p>
  </form>
  <p>{{ request.session.title }}</p>
</table>
{% endblock %}

{{ form.errors }} の表示

pythonApp\class_crud\class_crud\books\templates\books\create.html

{% extends "books/base.html" %}
{% block title %}books/create view{% endblock %}

{% block content %}
<h1>books/create</h1>
<div>
{{ form.errors }}
</div>
<table>
  <form method="post" novalidate>
    {% csrf_token %}
    
    {% for field in form %}
    <div class="field">{{field.label_tag}}{{field}}</div>
      {% for error in field.errors%}
      <p style="color: red;">{{error}}</p>
      {% endfor%}
    {% endfor %}
    <p><input type="submit" value="Save"></p>
  </form>
  <p>{{ request.session.title }}</p>
</table>
{% endblock %}

モデル・フォーム

モデル・フォーム

pythonApp\class_crud\class_crud\books\models.py

from django.db import models

class Books(models.Model):
  title = models.CharField(max_length=20)
  body = models.TextField(max_length=4000)

  def __str__(self):
    return str(self.id) + ":" + self.title 

pythonApp\class_crud\class_crud\books\forms.py

from django import forms
from .models import Books
from django.urls import reverse_lazy

class BooksForm(forms.ModelForm):
  class Meta:
    model = Books
    fields = ['title', 'body']

    error_messages = {
      "title": {
        "required": "タイトルが入力されていません",
      },
      "body": {
        "required": "Body が入力されていません"
      }
    }

class BooksSearchForm(forms.Form):
  find = forms.CharField(label='Find', required=False)

ルーティング

ルーティング

pythonApp\class_crud\class_crud\class_crud\urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', include('books.urls')),
]

pythonApp\class_crud\class_crud\books\urls.py

from django.urls import path
from . import views
from books.views import BooksListView, BooksCreateView, BooksDetailView, BooksUpdateview, BooksDeleteView, BooksSearchView

app_name = 'books'

urlpatterns = [
    # path("", views.IndexView.as_view(), name="index"),
    path("", BooksListView.as_view(), name="list"),
    path("create", BooksCreateView.as_view(), name="create"),
    path('show/', BooksDetailView.as_view(), name='show'),
    path('update/', BooksUpdateview.as_view(), name='update'),
    path('delete/', BooksDeleteView.as_view(), name='delete'),
    path('search', BooksSearchView.as_view(), name='search'),
]

pythonApp\class_crud\class_crud\books\views.py

from django.views.generic import TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView, FormView
from django.views.generic.edit import ModelFormMixin
from .models import Books
from .forms import BooksForm
from .forms import BooksSearchForm
from django.urls import reverse_lazy
from django.shortcuts import render
from django.shortcuts import render

class IndexView(TemplateView):
  template_name = "books/index.html"

class BooksListView(ListView):
  model = Books
  template_name = 'books/list.html'
  form_class = BooksSearchForm
  success_url = reverse_lazy('books:list')
  paginate_by = 5
  
  def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs) # はじめに継承元のメソッドを呼び出す
    context["form"] = BooksSearchForm
    return context
  
  def post(self, request):
    txt = request.POST['find']
    params = {
      'form': BooksSearchForm,
      'books_list': Books.objects.filter(title=txt)
    }
    return render(request, 'books/list.html', params)
  
  def get(self, request, *args, **kwargs):
    self.object = None
    return super().get(request, *args, **kwargs)
  
class BooksCreateView(CreateView):
  model = Books
  form_class = BooksForm
  template_name = 'books/create.html'
  success_url = reverse_lazy('books:list')

class BooksDetailView(DetailView):
  model = Books
  template_name = 'books/show.html'

class BooksUpdateview(UpdateView):
  model = Books
  form_class = BooksForm
  template_name = 'books/update.html'
  success_url = reverse_lazy('books:list')

class BooksDeleteView(DeleteView):
  model = Books
  template_name = 'books/delete.html'
  success_url = reverse_lazy('books:list')

class BooksSearchView(FormView):
  template_name = 'books/list.html'
  paginate_by = 5

  def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['form'] = BooksSearchForm(self.request.GET)
    return context

セッティング

セッティング

pythonApp\class_crud\class_crud\class_crud\settings.py

...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'books',
]

...

DATABASES = {
  'default': {
      'ENGINE': 'django.db.backends.mysql',
      'NAME': 'class_crud',
      'USER': 'root', # 自身の環境データベースのユーザー名
      'PASSWORD': 'password', # 自身の環境データベースのパスワード
      'HOST': '127.0.0.1', # 自身の環境データベースのホスト名など
      'PORT': '3306' # 自身の環境データベースのポート番号
  }
}

...

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

pythonApp\class_crud\class_crud\books\admin.py

from django.contrib import admin
from .models import Books

admin.site.register(Books)