本棚アプリケーションの作成②【5-4 ログイン機能の追加】

ログイン機能の実装

Djangoのauthアプリケーションの中のurls.pyファイル呼び出せるようにするためにurls.pyファイルを編集していきます。

bookproject/bookproject/urls.py

from django.contrib import admin
from django.urls import path, include    #コード追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('book.urls')),      #コード追加
]

from django.contrib import admin
from django.urls import path, include    #コード追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('django.contrib.auth.urls')),
    path('', include('book.urls')),      #コード追加
]

に編集。

これで127.0.0.1:8000/accounts/というURLをrequestすることでauthアプリケーションのurls.pyファイルを呼び出すことができるようになりました。

サーバーを立ち上げてhttp://127.0.0.1:8000/accounts/login/にアクセスします

(venv) $ python manage.py runserver 

エラーが出ました。これはtemplateが存在しないと表示されています。このエラーを解決するためにログイン画面を表示するhtmlファイルを作成します。

以下のコマンドを実行していきます。(この時manage.pyファイルがあるディレクトリでコードを実行します)

(venv) $ mkdir templates/registration
(venv) $ touch templates/registration/login.html

そして作成したlogin.htmlファイルに以下のコードを書き込んでいきます。

bookproject/templates/registration/login.html

{% extends 'base.html' %}

{% block content %}
    <h1>ログイン</h1>
    <form method="post" class="p-4 m-4 bg-light border border-success rounded form-group">
        {% csrf_token %}
        {% for error in form.errors.values %}
            {{ error }}
        {% endfor %}
        <label>
            ユーザ ID
        </label>
        <input class="form-control" name="username">
        <label>
            パスワード
        </label>
        <input type="password" class="form-control" name="password">
        <button type="submit" class="btn btn-success mt-4">ログインする</button>
    </form>
{% endblock %}

そして再度

サーバーを立ち上げてhttp://127.0.0.1:8000/accounts/login/にアクセスします

(venv) $ python manage.py runserver 

無事にログイン画面ができました。

ここで第4章で作成したユーザーIDを入力してログインしてみましょう

そしたらエラーが出ました。これはaccpunts/profile/というURLにアクセスする設定になっているにも関わらず、そのURLを設定していないために発生したエラーです。ログイン自体はできています。このエラーを解決するためにsetting.pyファイルに以下のコードを追加します。

bookproject/bookproject/setting.py

LOGIN_REDIRECT_URL = 'list-book'

再度ログインしましょう。

無事ログインできました。

 

 

 

本棚アプリケーションの作成②【5-3 index_viewの実装】

function-based viewの理解を深めよう

ここまで、function-based viewの最低限の流れについて理解してきました。

次に、viewとModelを連携させましょう

views.pyファイルにコードを追記します

bookproject/book/views.py

・・・省略・・・
def index_view(request):
    print('index_view is called')
    return render(request, 'book/index.html',{'somedata':100})
・・・省略・・・

・・・省略・・・
def index_view(request):
    object_list = Book.objects.all()
    return render(request, 'book/index.html',{'object_list':object_list})
・・・省略・・・

に編集

次にブラウザ上でデータを表示させるため、index.htmlファイルにコードを書いていきましょう。

bookproject/book/templates/book/index.html

{{ somedata }}

{% extends 'base.html' %}

{% block title %}書籍一覧{% endblock %}
{% block h1 %}書籍一覧{% endblock %}

{% block content %}
    {% for item in object_list %}
    <div class="p-4 m-4 bg-light border boorder-success rounded">
        <h2 class="text-success">{{ item.title }}</h2>
        <h6>カテゴリ:{{ item.category }}</h6>
        <div class="mt-3">
            <a href="{% url 'detail-book' item.pk %}">詳細へ</a>
        </div>
    </div>
    {% endfor %}
{% endblock content %}

に編集

サーバーを立ち上げてhttp://127.0.0.1:8000/にアクセスします

(venv) $ python manage.py runserver 

index_viewとListBookViewの表示が同じであることを確認しましょう。

ここからfunction-baseb viewの腕の見せ所です。データを表示させる順番を変えていきます。

まずは、管理画面からデータを一つ追加します。サーバーを立ち上げ、管理画面(127.0.0.1:8000/admin/)にアクセスします。

追加するデータは次の通りです。

 

Title:ビジネス最前線

Text:最新のビジネス

Category:ビジネス

 

 

3つ本のカテゴリは上から「business」「life」「business」となっています。

これらの、カテゴリごとに表示させるためにカスタマイズをします。

views.pyファイルを編集します。

bookproject/book/views.py

・・・省略・・・
def index_view(request):
    object_list = Book.objects.all()
    return render(request, 'book/index.html',{'object_list':object_list})
・・・省略・・・

・・・省略・・・
def index_view(request):
    object_list = Book.objects.order_by('category')
    return render(request, 'book/index.html',{'object_list':object_list})
・・・省略・・・

に編集

改めてサーバーを立ち上げてhttp://127.0.0.1:8000/にアクセスします。

(venv) $ python manage.py runserver 

上から2つが「business」に関する本、そして、一番下が「life」に関する本となっています。つまり亜k手ごりごとに並び替えができました。

 

このように、function-based view を使うことによって、シンプルなコードで直観的に実装をすることができます。

本棚アプリケーションの作成②【5-2 トップページの作成】

はじめに

トップページを作成していきます。

まずはurls.pyとviews.pyファイルを編集していきます。

bookproject/book/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('book/', views.ListBookView.as_view(),
name='list-book'),
    path('book/<int:pk>/detail/', views.DetailBookView.as_view(),
name='detail-book'),
    path('book/create/', views.CreateBookView.as_view(),
name='create-book'),
    path('book/<int:pk>/delete/', views.DeleteBookView.as_view(),
name='delete-book'),
    path('book/<int:pk>/update/', views.UpdateBookView.as_view(),
name='update-book')
]

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index_view, name='index'),
    path('book/', views.ListBookView.as_view(), name='list-book'),
    path('book/<int:pk>/detail/', views.DetailBookView.as_view(),
name='detail-book'),
    path('book/create/', views.CreateBookView.as_view(), name='create-book'),
    path('book/<int:pk>/delete/', views.DeleteBookView.as_view(),
name='delete-book'),
    path('book/<int:pk>/update/', views.UpdateBookView.as_view(),
name='update-book')
]

に編集

bookproject/book/views.py

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import (ListView, DetailView, CreateView, DeleteView,
UpdateView)
from .models import Book

class ListBookView(ListView):
    template_name = 'book/book_list.html'
    model = Book

class DetailBookView(DetailView):
    template_name = 'book/book_detail.html'
    model = Book

class CreateBookView(CreateView):
    template_name = 'book/book_create.html'
    model = Book
    fields = ('title','text','category')
    success_url = reverse_lazy('list-book')

class DeleteBookView(DeleteView):
    template_name = 'book/book_confirm_delete.html'
    model = Book
    success_url = reverse_lazy('list-book')

class UpdateBookView(UpdateView):
    model = Book
    fields = (['title','text','category'])
    template_name = 'book/book_update.html'
    success_url = reverse_lazy('list-book')

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import (ListView, DetailView, CreateView, DeleteView,
UpdateView)
from .models import Book

class ListBookView(ListView):
    template_name = 'book/book_list.html'
    model = Book

class DetailBookView(DetailView):
    template_name = 'book/book_detail.html'
    model = Book

class CreateBookView(CreateView):
    template_name = 'book/book_create.html'
    model = Book
    fields = ('title','text','category')
    success_url = reverse_lazy('list-book')

class DeleteBookView(DeleteView):
    template_name = 'book/book_confirm_delete.html'
    model = Book
    success_url = reverse_lazy('list-book')

class UpdateBookView(UpdateView):
    model = Book
    fields = (['title','text','category'])
    template_name = 'book/book_update.html'
    success_url = reverse_lazy('list-book')

def index_view(request):
    return render(request, 'book/index.html',{'somedata':100})

に編集

renderについて

index.htmlファイルを作成していきます。

(venv) $ touch book/templates/book/index.html

そして以下のコードを書き込んでいきます。

bookproject/book/templates/book/index.html

{{ somedata }}

そしてサーバーを立ち上げてhttp://127.0.0.1:8000/にアクセスします。

(venv) $ python manage.py runserver 

ターミナルに表示させる

function-based viewにおけるDjangoの内部処理についてより詳しく見ていきます。

views.pyファイルに次のコードを追記します。

bookproject/book/views.py

・・・省略・・・
def index_view(request):
    return render(request, 'book/index.html',{'somedata':100})
・・・省略・・・

・・・省略・・・
def index_view(request):
    print('index_view is called')
    return render(request, 'book/index.html',{'somedata':100})
・・・省略・・・

に編集

そして再度サーバーを立ち上げてhttp://127.0.0.1:8000/にアクセスします。

(venv) $ python manage.py runserver 

そしてwebサイトのほうではなく、ターミナルを見ると、いままでサーバーを立ち上げたときのターミナルは下の画像で

今回サーバーを立ち上げたときのターミナルはこちらになります。



 

 

 

 

 

本棚アプリケーションの作成【4-13 レイアウト等の調整】

見た目を整えていく

base.htmlを編集していきます。

bookproject/templates/base.html

<!doctype html>
<html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Bootstrap CSS -->
bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvK
uhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

        <title>{% block title %}{% endblock title %}| 本棚アプリ</title>
    </head>
    <body>
        {% block content %}{% endblock content %}
    </body>
</html>

<!doctype html>
<html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Bootstrap CSS -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/
bootstrap.min.css"
rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvK
uhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous">

        <title>{% block title %}{% endblock title %}| 本棚アプリ</title>
    </head>
    <body>
        <nav class="navbar navbar-dark bg-success sticky-top">
            <div class="navbar-nav d-flex flex-row">
                <a class="nav-link mx-3"
href="{% url 'list-book' %}">書籍一覧</a>
                <a class="nav-link mx-3"
href="{% url 'create-book' %}">書籍登録</a>
            </div>
        </nav>
        <div class="p-4">
            <h1>{% block h1 %}{% endblock %}</h1>
        {% block content %}{% endblock content %}
    </body>
</html>

次にbook_list.htmlファイルの修正を行っていきます。

bookproject/book/templates/book/book_list.html

<!doctype html>
<html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Bootstrap CSS -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/
bootstrap.min.css"
rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvK
uhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous">

        <title>本棚アプリ</title>
    </head>
    <body>
    {% extends 'base.html' %}

    {% block title %}書籍一覧{% endblock %}

    {% block content %}
        {% for item in object_list %}
        <div class="card">
            <h5 class="card-header">{{ item.title }}</h5>
            <div class="card-body">
                <p class="card-text">{{ item.text }}</p>
                <a href="{% url 'detail-book' item.pk %}"
class="btn btn-primary">詳細へ</a>
                <h6 class="card-title">{{ item.category }}</h6>
            </div>
        </div>
        {% endfor %}
    {% endblock content %}
    </body>
</html>

<!doctype html>
<html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Bootstrap CSS -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/
bootstrap.min.css"
rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvK
uhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous">

        <title>本棚アプリ</title>
    </head>
    <body>
    {% extends 'base.html' %}

    {% block title %}書籍一覧{% endblock %}
    {% block h1 %}書籍一覧{% endblock %}

    {% block content %}
        {% for item in object_list %}
        <div class="p-4 m-4 bg-light border border-success rounded">
            <h2 class="text-success">{{ item.title }}</h2>
            <h6>カテゴリ:{{ item.category }}</h6>
            <div class="mt-3">
                <a href="{% url 'detail-book' item.pk %}">詳細へ</a>
            </div>
        </div>
        {% endfor %}
    {% endblock content %}
    </body>
</html>

に編集
サーバーを立ち上げてhttp://127.0.0.1:8000/book/にアクセスします。

(venv) $ python manage.py runserver 

上の画像のようにレイアウトがかわり整えられました。

最後にDatailBookViewのレイアウトを変更していきます

bookproject/book/templates/book/book_detail.html

{% extends 'base.html' %}

{% block title %}書籍詳細{% endblock %}

{% block content %}
    <div class="card">
        <h5 class="card-header">{{ object.title }}</h5>
        <div class="card-body">
            <p class="card-text">{{ object.text }}</p>
            <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a>
            <a href="{% url 'update-book' object.pk %}"
class="btn btn-primary">編集する</a>
            <a href="{% url 'delete-book' object.pk %}"
class="btn btn-primary">削除する</a>
            <h6 class="card-title">{{ object.category }}</h6>
        </div>
    </div>
{% endblock content %}

{% extends 'base.html' %}
{% block title %}{{ object.title }}{% endblock %}
{% block h1 %}書籍詳細{% endblock %}

{% block content %}
    <div class="p-4 m-4 bg-light border border-success rounded">
        <h2 class="text-success">{{ object.title }}</h2>
            <p>{{ object.text }}</p>
            <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a>
            <a href="{% url 'update-book' object.pk %}" class="btn btn-primary">
編集する</a>
            <a href="{% url 'delete-book' object.pk %}" class="btn btn-primary">
削除する</a>
            <h6 class="card-title">{{ object.category }}</h6>
    </div>
{% endblock content %}

に編集

 

 

 

本棚アプリケーションの作成【4-12 リンク設定】

ブラウザ上でページを遷移させていく

ここまで作成したアプリでは、ブラウザ上でリンクが作成されておらず、URLに直接入力するといった形で個別のURLをブラウザに直接打ち込んで画面を遷移させていました。これを解決するためにボタンをクリックすることによって画面を遷移する形にします。

bookproject/book/templates/book/book_list.html

<!doctype html>
<html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Bootstrap CSS -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/
bootstrap.min.css"
rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvK
uhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous">

        <title>本棚アプリ</title>
    </head>
    <body>
    {% extends 'base.html' %}

    {% block title %}書籍一覧{% endblock %}

    {% block content %}
        {% for item in object_list %}
        <div class="card">
            <h5 class="card-header">{{ item.title }}</h5>
            <div class="card-body">
                <p class="card-text">{{ item.text }}</p>
                <a href="#" class="btn btn-primary">Go somewhere</a>
                <h6 class="card-title">{{ item.category }}</h6>
            </div>
        </div>
        {% endfor %}
    {% endblock content %}
    </body>
</html>

<!doctype html>
<html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Bootstrap CSS -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/
bootstrap.min.css"
rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvK
uhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

        <title>本棚アプリ</title>
    </head>
    <body>
    {% extends 'base.html' %}

    {% block title %}書籍一覧{% endblock %}

    {% block content %}
        {% for item in object_list %}
        <div class="card">
            <h5 class="card-header">{{ item.title }}</h5>
            <div class="card-body">
                <p class="card-text">{{ item.text }}</p>
                <a href="{% url 'detail-book' item.pk %}"
class="btn btn-primary">詳細へ</a>
                <h6 class="card-title">{{ item.category }}</h6>
            </div>
        </div>
        {% endfor %}
    {% endblock content %}
    </body>
</html>

に編集

bookproject/book/templates/book/book_detail.html

{% extends 'base.html' %}

{% block title %}書籍詳細{% endblock %}

{% block content %}
    <div class="card">
        <h5 class="card-header">{{ object.title }}</h5>
        <div class="card-body">
            <p class="card-text">{{ object.text }}</p>
            <a href="#" class="btn btn-primary">ボタン</a>
            <h6 class="card-title">{{ object.category }}</h6>
        </div>
    </div>
{% endblock content %}

{% extends 'base.html' %}

{% block title %}書籍詳細{% endblock %}

{% block content %}
    <div class="card">
        <h5 class="card-header">{{ object.title }}</h5>
        <div class="card-body">
            <p class="card-text">{{ object.text }}</p>
            <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a>
            <a href="{% url 'update-book' object.pk %}"
class="btn btn-primary">編集する</a>
            <a href="{% url 'delete-book' object.pk %}"
class="btn btn-primary">削除する</a>
            <h6 class="card-title">{{ object.category }}</h6>
        </div>
    </div>
{% endblock content %}

に編集する

そしてサーバーを立ち上げてhttp://127.0.0.1:8000/book/1/detail/ にアクセスする

(venv) $ python manage.py runserver 

するとこのような画面になる

本棚アプリケーションの作成【4-11】

データを更新する仕組みを作っていく

UpdateViewを作成していきます。

bookproject/book/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('book/', views.ListBookView.as_view(), name='list-book'),
    path('book/<int:pk>/detail/', views.DetailBookView.as_view(),
name='detail-book'),
    path('book/create/', views.CreateBookView.as_view(), name='create-book'),
    path('book/<int:pk>/delete/', views.DeleteBookView.as_view(),
name='delete-book')
]

from django.urls import path
from . import views

urlpatterns = [
    path('book/', views.ListBookView.as_view(), name='list-book'),
    path('book/<int:pk>/detail/', views.DetailBookView.as_view(),name='detail-book'),
    path('book/create/', views.CreateBookView.as_view(), name='create-book'),
    path('book/<int:pk>/delete/', views.DeleteBookView.as_view(),name='delete-book'),
    path('book/<int:pk>/update/', views.UpdateBookView.as_view(),name='update-book')
]

に編集。
bookproject/book/views.py

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, DeleteView
from .models import Book

class ListBookView(ListView):
    template_name = 'book/book_list.html'
    model = Book

class DetailBookView(DetailView):
    template_name = 'book/book_detail.html'
    model = Book

class CreateBookView(CreateView):
    template_name = 'book/book_create.html'
    model = Book
    fields = ('title','text','category')
    success_url = reverse_lazy('list-book')

class DeleteBookView(DeleteView):
    template_name = 'book/book_confirm_delete.html'
    model = Book
    success_url = reverse_lazy('list-book')

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import (ListView, DetailView, CreateView, DeleteView,
UpdateView)
from .models import Book

class ListBookView(ListView):
    template_name = 'book/book_list.html'
    model = Book

class DetailBookView(DetailView):
    template_name = 'book/book_detail.html'
    model = Book

class CreateBookView(CreateView):
    template_name = 'book/book_create.html'
    model = Book
    fields = ('title','text','category')
    success_url = reverse_lazy('list-book')

class DeleteBookView(DeleteView):
    template_name = 'book/book_confirm_delete.html'
    model = Book
    success_url = reverse_lazy('list-book')

class UpdateBookView(UpdateView):
    model = Book
    fields = (['title','text','category'])
    template_name = 'book/book_update.html'
    success_url = reverse_lazy('list-book')

に編集。

book_update.htmlファイルを作成します

(venv) $ touch book/templates/book/book_update.html

bookproject/book/templates/book/book_update.html

{% extends 'base.html' %}

{% block title %}書籍修正{% endblock %}

{% block content %}
    <form method='POST'>{% csrf_token %}
        {{ form.as_p }}
        <button type="submit">修正する</button>
    </form>
{% endblock %}

これでそれぞれのviewを作成することができました。

 

 

 

 

 

 

本棚アプリケーションの作成【4-10 DeleteViewを作成してブラウザ上でデータを削除できるようにしよう】

データを削除するための画面を作成する

DeleteViewを作成します。これはデータを削除する際に使われます

実際にコードを書いていきます

bookproject/book/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('book/', views.ListBookView.as_view(), name='list-book'),
    path('book/<int:pk>/detail/', views.DetailBookView.as_view(),
name='detail-book'),
    path('book/create/', views.CreateBookView.as_view(), name='create-book'),
]

from django.urls import path
from . import views

urlpatterns = [
    path('book/', views.ListBookView.as_view(), name='list-book'),
    path('book/<int:pk>/detail/', views.DetailBookView.as_view(),
name='detail-book'),
    path('book/create/', views.CreateBookView.as_view(), name='create-book'),
    path('book/<int:pk>/delete/', views.DeleteBookView.as_view(),
name='delete-book')
]

に編集

bookproject/book/views.py

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView
from .models import Book

class ListBookView(ListView):
    template_name = 'book/book_list.html'
    model = Book

class DetailBookView(DetailView):
    template_name = 'book/book_detail.html'
    model = Book

class CreateBookView(CreateView):
    template_name = 'book/book_create.html'
    model = Book
    fields = ('title','text','category')
    success_url = reverse_lazy('list-book')

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, DeleteView
from .models import Book

class ListBookView(ListView):
    template_name = 'book/book_list.html'
    model = Book

class DetailBookView(DetailView):
    template_name = 'book/book_detail.html'
    model = Book

class CreateBookView(CreateView):
    template_name = 'book/book_create.html'
    model = Book
    fields = ('title','text','category')
    success_url = reverse_lazy('list-book')

class DeleteBookView(DeleteView):
    template_name = 'book/book_confirm_delete.html'
    model = Book
    success_url = reverse_lazy('list-book')

に編集

book_confirm_delete.htmlファイルを作成していきます。

(venv) $ touch book/templates/book/book_confirm_delete.html

bookproject/book/templates/book/book_confirm_delete.html

{% extends 'base.html' %}

{% block title %}書籍排除{% endblock %}

{% block content %}
    <form method='POST'>{% csrf_token %}
        <button type="sybmit">{{ object.title }}を削除する</button>
    </form>
{% endblock %}

これでDeleteBookViewの完成です