Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

「画像でゴミ分類!」アプリ作成日誌day6~ディレクトリ構成の修正~

はじめに

「画像でゴミ分類!」アプリ作成日誌6日目の今日はディレクトリ構成を修正します。今日は忙しかったこともあり軽めです。

<記事一覧>

前回までのあらすじ

前回までの記事では画像認識アプリを実装してDjangoに載せたうえで、Bootstrapを使ってフロントエンドを整えるところまでやりました。現時点での問題点の一つ目はindexとresultのtemplateファイルで重複した記述が存在していることであり、これはbaseファイルを作成して対応します。2つ目の問題は画像ファイルをHTMLから相対パスで参照してしまっているので、それをstaticフォルダから読めるようにしたいと思います。

base.htmlの作成

indexとresultで重複した記述をbaseに移行します。これは、今後デザインを変更した際にいちいち二つのファイルをいじるのが非効率だからです。

garbage/templates/garbage/base.html
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>画像でゴミ分類!</title>
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css" integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous">
    <link rel="stylesheet" type="text/css" href="{% static 'garbage/css/bootstrap.css' %}" />
    <link rel="stylesheet" type="text/css" href="{% static 'garbage/css/style.css' %}" />
</head>
<body>
    <div id="wrapper">
        <button class="toggler d-md-none" type="button" id="toggler" data-toggle="collapse-side" data-target=".side-collapse">
            <i class="fas fa-bars fa-2x"></i>
        </button>
        <img src="{% static 'garbage/media/images/title.png' %}" alt="画像でゴミ分類!" class="m-4 pb-3" id="title">

        <div class="container row">
            <div class="card col-md-4 py-4 px-0 d-none d-md-block h-100" id="sidebar">
                <p role="button" class="mb-2 btn border-dark rounded-0 btn-secondary">外部リンク集</p>
                <a href="https://manage.delight-system.com/threeR/web/bunbetsu?menu=bunbetsu&jichitaiId=kashiwashi&areaId=22125&areaName=%2F&lang=ja&benriCateId=7&bunbetsuCateId=7&faqCateId=%2F&howToCateId=&search=%E3%83%9A%E3%83%83%E3%83%88%E3%83%9C%E3%83%88%E3%83%AB&dummy=" class="btn btn-default border-dark mb-1 rounded-0" role="button" target="_blank" rel="noopener noreferrer">分別検索</a>
                <a href="https://manage.delight-system.com/threeR/web/benri?menu=benri&jichitaiId=kashiwashi&areaId=22125&benriCateId=7&bunbetsuCateId=7&faqCateId=%2f&lang=ja" class="btn btn-default border-dark mb-1 rounded-0" role="button" target="_blank" rel="noopener noreferrer">ごみの分け方・出し方</a>
            </div>

            {% block main %}{% endblock main %}

        </div>

        {% block second %}{% endblock second %}

        <footer>
            <p id="copyright" class="mb-0">Copyright &copy; 2020 eycjur All Rights Reserved.</p>
        </footer>
    </div>
</body>
</html>

冒頭の記述やサイドバーが共通しているもののため、baseにします。{% block main %}{% endblock main %}のところに各ファイルごとの記述が入ります。

では、各ファイルが実際にどうなるかを見てみると、

garbage/templates/garbage/index.html
{% extends "garbage/base.html" %}
{% load static %}

{% block main %}
<div class="col-md-8">
    <div class="container card p-4 h-100">
        <p>分類を調べたい画像を入力してください</p>
        <form action="{% url "garbage:result" %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            {{ form }}
            <br>
            <button type="submit" class="mt-3 wait-disappear">調べる!</button>
        </form>
    </div>
</div>
{% endblock main %}

{% block second %}
<h4>既存の画像を利用する</h4>
<div class="container row">
    <div class="col-md-6 p-3">
        <a href='{% url "garbage:result_num" num=1 %}'>
            <img src="{% static 'garbage/media/images/temp1.jpg' %}" alt="画像1" class="sample-img">
        </a>
    </div>
    <div class="col-md-6 p-3">
        <a href='{% url "garbage:result_num" num=2 %}'>
            <img src="{% static 'garbage/media/images/temp2.jpg' %}" alt="画像2" class="sample-img">
        </a>
    </div>
</div>
{% endblock second %}

最低限の部分だけを書くことで、このようにだいぶすっきりしたのが伝わるかと思います。

画像ファイルの配置の修正

完成イメージ

まず、どのようなディレクトリ構成にするかを決めておきます。
今回は以下のような構成にします。

garbage_proj
├garbage_proj
│ └settings.pyなど
├garbage
│ └views.pyなど
└static
  └garbage
   ├css
   │ └style.css
   └media
    └images
    └image.pngなど

staticとmediaのパス

まず、staticとmediaのパスを設定しておきましょう。

garbage_proj/setting.py
STATIC_URL = '/static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)

MEDIA_ROOT = os.path.join(BASE_DIR, 'static/garbage/media')
MEDIA_URL = '/media/'

いま、BASE_DIRは大元のgarbage_projフォルダなので、static,mediaフォルダをそれぞれ指定できていることがわかります。
こうすると、staticディレクトリを読み込むことで利用できるようになるので、templateファイルは<img src="{% static 'garbage/media/images/title.png' %}">のように画像を指定できます。

これをもとに記述してきます。

garbage/templates/garbage/result.html
{% extends "garbage/base.html" %}
{% load static %}

{% block main %}
<div class="col-md-8">
    <div class="container card p-4 px-5">
        <h2 class="m-3">分類結果</h2>
        <img src="{% static 'garbage/media/images/image.png' %}" alt="画像" id="result-img">
        <div class="container">
            <table class="table">
                <tr><th>分類</th><td>確率</td><td>収集曜日</td></tr>
                {% for key, value, day in pred %}
                <tr><th>{{ key }}</th><td>{{ value }}%</td><td>{{ day }}</td></tr>
                {% endfor %}
            </table>
            <a href="{% url "garbage:index" %}">Topへ戻る</a>
        </div>
    </div>
</div>
{% endblock main %}

このように書くと相対パスで記述する必要がなくなるので、ルーティングを統一できます。

garbage/templates/garbage/index.html
{% extends "garbage/base.html" %}
{% load static %}

{% block main %}
<div class="col-md-8">
    <div class="container card p-4 h-100">
        <p>分類を調べたい画像を入力してください</p>
        <form action="{% url "garbage:result" %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            {{ form }}
            <br>
            <button type="submit" class="mt-3 wait-disappear">調べる!</button>
        </form>
    </div>
</div>
{% endblock main %}

{% block second %}
<h4>既存の画像を利用する</h4>
<div class="container row">
    <div class="col-md-6 p-3">
        <a href='{% url "garbage:result_num" num=1 %}'>
            <img src="{% static 'garbage/media/images/temp1.jpg' %}" alt="画像1" class="sample-img">
        </a>
    </div>
    <div class="col-md-6 p-3">
        <a href='{% url "garbage:result_num" num=2 %}'>
            <img src="{% static 'garbage/media/images/temp2.jpg' %}" alt="画像2" class="sample-img">
        </a>
    </div>
</div>
{% endblock second %}

garbageアプリのresult_numに対してパラメーターnumを指定して渡しています。また、これを処理するurls.pyは以下のようになっています。

garbage/urls.py
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from . import views

app_name="garbage"
urlpatterns = [
    path("", views.index, name="index"),
    path("result", views.result, name="result"),
    path("result/<int:num>", views.result, name="result_num"),
]

result_numに対してはパラメーターを渡しつつ、"result"という同じ関数を割り当てています。

garbage/views.py(一部略)
def result(request, num=0):
    if num:
        img = "./static/garbage/media/images/" + ["temp1.jpg", "temp2.jpg"][num-1]

    else:
        form = UploadPictureForm(request.POST, request.FILES)
        if form.is_valid():
            img = form.cleaned_data["img"]
        else:
            params = {
                "form":UploadPictureForm()
            }
            return render(request, "garbage/index.html", params)

    pred = predict(img)

    params = {
        "img":img,
        "pred":pred
    }
    return render(request, "garbage/result.html", params)

サンプルが選択されたときは既存の画像を利用するようにすることができました。

さいごに

以上のことで、だいぶファイル構造がわかりやすくなったかと思います。今後の開発がだいぶ楽になりそうです!
ちなみに、今回記事を書いていてコードブロックにdjangoという言語の指定ができることを初めて知りました。HTMLのシンタックスハイライトを引き継いだうえでdjango templateも見やすくなっているので非常に便利ですね。

<記事一覧>

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
1
Help us understand the problem. What are the problem?