Djangoを使ってwebアプリを作成する

## gitリポジトリ
作成したものをgithubにあげています。参考にしてください。

```
https://github.com/sigma7641/nutrient_calculator.git
```

## 作成するもの

「登録したものの栄養素を計算するwebアプリ」を作成します。基本的には以下の機能を有します。

- ユーザーが栄養価を計算したい食品の情報（名称、カロリー、タンパク質、脂質、炭水化物など）を入力するフォームを提供する。
- ユーザーがフォームに情報を入力して「登録」ボタンを押すと、その情報がデータベースに保存される。
- ユーザーが保存した食品情報を一覧表示するページを提供する。
- ユーザーが一覧ページから個別の食品情報を選択すると、その食品の栄養価を表示するページを提供する。

以上が基本的な機能です。実装においては、Djangoの各機能（フォーム、ビュー、テンプレート、モデル、データベースなど）を適切に使い、この機能を実装します。

## 作業手順

1. Djangoプロジェクトの作成
1. アプリの作成
1. データモデルの作成
1. DBへの接続
1. データベースのマイグレーション
1. フォームの作成
1. ビューの作成
1. URLパターンの設定
1. テンプレートファイルの作成
1. アプリのインストール
1. テンプレートファイルの設置
1. CSSの配置
1. DBの設定
1. 動作確認

筆者が同様の手順で設定していますが、非効率な部分や間違えがあるかと思います。
不自然に思った場合などは、Django公式ドキュメントやチュートリアルなどの教材を参考にしてください。
また、コメントで修正を促してくれると嬉しいです。

## Djangoプロジェクトの作成

初めにDjangoのプロジェクトを作成します。

```bash
django-admin startproject nutrient_calculator
```

Djangoにおけるプロジェクトは、Djangoアプリケーションの開発において、複数のアプリケーションを管理するためのルートとなるものです。プロジェクトは、Djangoアプリケーションの設定、URLルーティング、データベース接続、静的ファイルの管理などの機能を提供します。

通常、プロジェクトは複数のアプリケーションで構成されます。例えば、Webサイトを構築する場合、ブログ、フォーラム、ユーザー認証などの機能を提供するそれぞれのアプリケーションがあります。これらのアプリケーションは、プロジェクト内に含まれ、共通の設定を共有しながら独立して開発されます。

また、Djangoのプロジェクトは、DjangoアプリケーションをホスティングするためのWebサーバーとしても使用することができます。Djangoは、WSGI(Web Server Gateway Interface)に対応しており、プロジェクトをWebサーバーにデプロイすることで、リクエストを処理し、レスポンスを返すことができます。

## アプリの作成

作成したプロジェクトに対して、栄養素計算用のアプリを作成します。

```bash
cd nutrient_calculator
python manage.py startapp nutrition
```

Djangoにおけるアプリは、Webアプリケーションの機能や部品を含む、Djangoアプリケーションの構成要素です。アプリは、Webアプリケーション内で機能をグループ化し、アプリごとに機能を別々に開発・テスト・メンテナンスできるように設計されています。

Djangoのアプリは、一般的にはWebアプリケーション内の1つの機能を実行するために、ビュー、モデル、テンプレート、静的ファイルなどの様々なコンポーネントを含みます。アプリは、機能に基づいて分割され、単独で再利用可能なものでなければなりません。

また、Djangoアプリは独自の設定、テンプレート、静的ファイル、データベースマイグレーション、URLパターンなどを持つことができます。アプリは、Djangoプロジェクト内で複数のアプリを作成して、Webアプリケーション全体を構成することができます。

## データモデルの作成

栄養素のデータを保存するためのデータモデルを作成します。栄養素の種類、カロリー、タンパク質、脂質、炭水化物の値を保存できます。

```python:nutrition/models.py
from django.db import models

class Nutrient(models.Model):
    name = models.CharField(max_length=255)
    calories = models.IntegerField()
    protein = models.IntegerField()
    fat = models.IntegerField()
    carbohydrate = models.IntegerField()

    def __str__(self):
        return self.name
    class Meta:
        app_label = 'nutrition'

```

Djangoでは、データベースのテーブルをPythonのクラスで定義することができます。これを「モデル」と呼びます。

モデルは、Djangoのmodels.Modelクラスを継承して定義します。属性としてフィールドを定義し、そのフィールドにはデータ型を指定します。データベースに保存されるテーブルの列として表現されます。
モデルを作成することで、データベースに対してテーブルを作成することができます。また、モデルを通じて、データベースとのやりとりができるようになります。

## DBへの接続

DBへ接続する設定をする必要があります。今回はDBの準備ができているという前提です。

```python:nutrient_calculator/settings.py
DATABASES = {
    'default': {
        'ENGINE': '【自分の設定を入れてください】',
        'NAME': '【自分の設定を入れてください】',
        'USER': '【自分の設定を入れてください】',
        'PASSWORD': '【自分の設定を入れてください】',
        'HOST': '【自分の設定を入れてください】',
        'PORT': '【自分の設定を入れてください】'
    }
}
```

## データベースのマイグレーション

作成したデータモデルをデータベースに反映させます。これは`nutrient_calculator`ディレクトリ(`manage.py`があるディレクトリ)で行う必要があります。

```bash
python manage.py makemigrations
python manage.py migrate
```

Djangoにおけるデータベースのマイグレーションとは、データベースの構造を変更するための手順です。例えば、モデルの追加や削除、フィールドの変更などを行った場合、その変更内容をデータベースに反映させるためにマイグレーションを行う必要があります。

マイグレーションを行うには、まずマイグレーションファイルを生成する必要があります。このファイルには、どのような変更を行うのかが記述されています。マイグレーションファイルを生成したら、マイグレーションを実行することでデータベースに変更を反映させることができます。

マイグレーションファイルの生成やマイグレーションの実行は、Djangoのコマンドラインツールで行うことができます。具体的な手順は以下のとおりです。

```bash
# マイグレーションファイルの生成
python manage.py makemigrations アプリ名
# マイグレーションの実行
python manage.py migrate アプリ名
```

アプリ名を省略すると、全てのアプリに対してマイグレーションを行うので、今回はこれを使用しています。

## フォームの作成
栄養素を入力するフォームを作成します。フォームには、各栄養素を入力するためのテキストボックスを設置し、送信ボタンをクリックすることで計算結果を表示するようにします。また、カロリーは入力された値から計算します。

```python:nutrition/forms.py
from django import forms
from .models import Nutrient

class NutrientForm(forms.ModelForm):
    name = forms.CharField(label='食品名', max_length=255, required=True)
    calories = forms.IntegerField(label='カロリー', widget=forms.HiddenInput, required=False)
    protein = forms.IntegerField(label='タンパク質', required=True)
    fat = forms.IntegerField(label='脂質', required=True)
    carbohydrate = forms.IntegerField(label='炭水化物', required=True)

    class Meta:
        model = Nutrient
        fields = ['name', 'calories', 'protein', 'fat', 'carbohydrate']

    def clean(self):
        cleaned_data = super().clean()
        protein = cleaned_data.get('protein')
        fat = cleaned_data.get('fat')
        carbohydrate = cleaned_data.get('carbohydrate')
        if protein is None or fat is None or carbohydrate is None:
            raise forms.ValidationError('タンパク質、脂質、炭水化物は必須です。')
        else:
            calories = protein * 4 + fat * 9 + carbohydrate * 4
            cleaned_data['calories'] = calories
        return cleaned_data

```

Djangoにおけるフォームは、Webアプリケーションにおいてユーザーが入力する情報を受け取るためのものです。フォームには、フォームの表示、フォームのバリデーション、フォームの保存などの機能があります。

フォームを作成するには、`forms.py`ファイルにフォームの定義を記述します。フォームの定義には、Djangoが提供する`forms`モジュールを使用します。

フォームの定義には、ModelFormを継承し、Metaクラスの中でモデルとフィールドを指定します。

また、フォームをテンプレートで表示するために、views.pyファイルでフォームを生成し、テンプレートに渡す必要があります。

## ビューの作成
フォームから送信された栄養素の値を受け取りるビューを作成します。

```python:nutrition/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from .models import Nutrient
from .forms import NutrientForm


def index(request):
    nutrients = Nutrient.objects.all()
    return render(request, 'nutrition/index.html', {'nutrients': nutrients})

def create(request):
    if request.method == 'POST':
        form = NutrientForm(request.POST or None)
        if form.is_valid():
            nutrient = Nutrient(
                name=form.cleaned_data['name'],
                calories=form.cleaned_data['calories'],
                protein=form.cleaned_data['protein'],
                fat=form.cleaned_data['fat'],
                carbohydrate=form.cleaned_data['carbohydrate'],
            )
            nutrient.save()
            return redirect(reverse('nutrition:index'))
    else:
        form = NutrientForm()

    context = {
        'form': form
    }
    return render(request, 'nutrition/form.html', {'form': form})

def detail(request, pk):
    nutrient = get_object_or_404(Nutrient, pk=pk)
    return render(request, 'nutrition/detail.html', {'nutrient': nutrient})

def update(request, pk):
    nutrient = get_object_or_404(Nutrient, pk=pk)
    form = NutrientForm(request.POST or None, instance=nutrient)
    if form.is_valid():
        form.save()
        return redirect('nutrition:detail', pk=nutrient.pk)
    return render(request, 'nutrition/form.html', {'form': form})

def delete(request, pk):
    nutrient = get_object_or_404(Nutrient, pk=pk)
    nutrient.delete()
    return redirect('nutrition:index')
```

Djangoにおけるビューは、HTTPリクエストを受け取り、それに対するレスポンスを返すための関数やメソッドのことです。ビューはMVC（Model-View-Controller）のうちのViewに相当します。

NutrientFormを使ってPOSTされたデータを処理し、新しいNutrientオブジェクトを作成しています。最後に、新しく作成されたNutrientオブジェクトの詳細ページにリダイレクトしています。また、GETリクエストが送信された場合は、空のフォームを表示するようにしています。

`redirect`関数の引数で、`nutrition:index`にリダイレクトするように指定しています。これは、indexビューがリストビューであるため、削除後にリダイレクトされるとすぐに栄養素のリストが表示されることを意味します。

ビュー関数は、`urls.py`で設定されたURLパターンにマッチするように設定する必要があります。

## URLパターンの設定

Djangoでは、URLリクエストを受け取って、適切なビュー関数を呼び出すためにURLパターンを設定する必要があります。通常は`nutrient_calculator/urls.py`に`nutrition/urls.py`を読み込ませてアプリ毎にファイルを分割します。

```python:nutrition/urls.py
from django.contrib import admin
from django.urls import path
from nutrition import views

app_name = 'nutrition'

urlpatterns = [
    path('', views.index, name='index'),
    path('create/', views.create, name='create'),
    path('<int:pk>/', views.detail, name='detail'),
    path('<int:pk>/update/', views.update, name='update'),
    path('<int:pk>/delete/', views.delete, name='delete'),
]

```

```python:nutrient_calculator/urls.py
from django.urls import include, path

urlpatterns = [
    path('/', include('nutrition.urls', namespace='nutrition')),
]
```

## テンプレートファイルの作成

テンプレートファイルを置くためのディレクトリを作成します。`nutrient_calculator`で行ってください。
```bash
mkdir templates
```

### `base.html`

```html:templates/base.html
{% load static %}
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>{% block title %}{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'style.css' %}">
  </head>
  <body>
   <header class="header">
      <h1 class="header">Nutrient Calculator</h1>
      <nav class="nav">
        <ul>
          <li><a href="{% url 'nutrition:index'%}">List</a></li>
          <li><a href="{% url 'nutrition:create'%}">Create</a></li>
        </ul>
      </nav>
    </header>
    <div class="allWrapper">
    <div class="container">
      {% block content %}{% endblock %}
    </div>
    </div>
    <footer>
      <div class="container">
	  <p>&copy; 2023 sigma7641. All Rights Reserved.</p>
      </div>
      <div class="container">
          <p class="sentences">
            サイト上のコンテンツの無断転載・複製を禁じます。
            このサイトに掲載されている全ての文章・画像・音声などの著作物は、著作権法によって保護されています。
            当サイトは、リンク先で起こったトラブルについて一切責任を負いません。
          </p>
      </div>
    </footer>
</html>
```

### `base_nutrition.html`

アプリ用のテンプレートファイルも作成します。`templates`で

```bash
mkdir nutrition
```
を実行して`nutrition`用のディレクトリを作成してください。

```html:templates/nutrition/base_nutrition.html
{% extends "base.html" %}
{% load static %}
{% block content %} {% endblock %}
```


### `index.html`

```html:templates/nutrition/index.html
{% extends 'nutrition/base_nutrition.html' %}

{% block content %}
<h1>Nutrients</h1>
<table>
  <thead>
    <tr>
      <th>食材名</th>
      <th>カロリー</th>
      <th>タンパク質</th>
      <th>脂質</th>
      <th>炭水化物</th>
      <th>アクション</th>
    </tr>
  </thead>
  <tbody>
    {% for nutrient in nutrients %}
    <tr>
      <td>{{ nutrient.name }}</td>
      <td>{{ nutrient.calories }}</td>
      <td>{{ nutrient.protein }}</td>
      <td>{{ nutrient.fat }}</td>
      <td>{{ nutrient.carbohydrate }}</td>
      <td>
	<a href="{% url 'nutrition:detail' nutrient.pk %}">Detail</a>
	<a href="{% url 'nutrition:update' nutrient.pk %}">Update</a>
	<a href="{% url 'nutrition:delete' nutrient.pk %}">Delete</a>
      </td>
    </tr>
    {% endfor %}
  </tbody>
</table>
{% endblock %}
```

テンプレートファイルはHTMLやCSSなどのファイルで、ビューから送信されたデータをレンダリングして、ブラウザに表示するために使用されます。Djangoにおいて、テンプレートファイルはアプリケーションのテンプレートディレクトリに配置されます。

ここでは、base_nutrition.htmlを継承し、`{% block %}`タグでcontentブロックを定義しています。そして、`{% for %}`タグを使って、Nutrientモデルのオブジェクトを取得し、テーブルの各行に表示しています。また、各行のアクションとして、detailビュー、updateビュー、deleteビューへのリンクを作成しています。`{% url %}`タグは、URLパターンの名前を使用してリンクを作成するために使用されます。

### `create.html`

```html:templates/nutrition/create.html
{% extends 'nutrition/base_nutrition.html' %}

{% block content %}

  <h1>Create Nutrient</h1>
  <table>
    <form method="post">
      {% csrf_token %}
      {% for field in form %}
      <tr>
        <th>{{ field.label_tag }}</th>
        <td>{{ field }}</td>
      </tr>
      {% endfor %}
      <tr>
        <td colspan="2"><button type="submit">Save</button></td>
      </tr>
    </form>
  </table>
{% endblock %}

```

`base_nutrition.html` を継承し、content ブロックにフォームを表示するテンプレートとなっています。`form.as_p` は、フォームを `<p>` 要素でラップしたものを表示するショートカットです。また、`{% csrf_token %}`は Django のクロスサイトリクエストフォージェリ保護機能を有効にするためのものです。

### `detail.html`

```html:templates/nutrition/detail.html
{% extends 'nutrition/base_nutrition.html' %}

{% block content %}
  <h1>Nutrient Detail</h1>
  <table>
    <tr>
      <th>食材名:</th>
      <td>{{ nutrient.name }}</td>
    </tr>
    <tr>
      <th>カロリー:</th>
      <td>{{ nutrient.calories }}</td>
    </tr>
    <tr>
      <th>タンパク質:</th>
      <td>{{ nutrient.protein }}</td>
    </tr>
    <tr>
      <th>炭水化物:</th>
      <td>{{ nutrient.carbohydrate }}</td>
    </tr>
    <tr>
      <th>脂質:</th>
      <td>{{ nutrient.fat }}</td>
    </tr>
  </table>
  <p><a href="{% url 'nutrition:index' %}">Back to index</a></p>
  <p><a href="{% url 'nutrition:update' pk=nutrient.pk %}">Update</a></p>
  <form action="{% url 'nutrition:delete' pk=nutrient.pk %}" method="POST">
    {% csrf_token %}
    <input type="submit" value="Delete">
  </form>
{% endblock %}
```

### `update.html`

```html:templates/nutrition/update.html
{% extends 'nutrition/base_nutrition.html' %}

{% block content %}

  <h1>Update Nutrient</h1>
  <table>
    <form method="post">
      {% csrf_token %}
      {% for field in form %}
      <tr>
        <th>{{ field.label_tag }}</th>
        <td>{{ field }}</td>
      </tr>
      {% endfor %}
      <tr>
        <td colspan="2"><input type="submit" value="Update"></td>
      </tr>
    </form>
  </table>
{% endblock %}
```

### `delete.html`

```html:templates/nutrition/delete.html
{% extends 'nutrition/base_nutrition.html' %}

{% block content %}
  <h2>Delete Nutrient</h2>
  <p>Are you sure you want to delete "{{ nutrient.name }}"?</p>

  <form method="post">
    {% csrf_token %}
    <input type="submit" value="Delete">
    <a href="{% url 'nutrition:detail' nutrient.pk %}">Cancel</a>
  </form>
{% endblock %}
```

## `form.html`

```html:templates/nutrition/form.html
{% extends 'nutrition/base_nutrition.html' %}

{% block content %}

  <h1>Create Nutrient</h1>
  <table class="excel-like">
    <form method="post">
      {% csrf_token %}
      <tr>
        <th><label for="{{ form.name.id_for_label }}">食材名</label></th>
        <td>{{ form.name }}</td>
      </tr>
      <tr>
        <th><label for="{{ form.protein.id_for_label }}">タンパク質</label></th>
        <td>{{ form.protein }}</td>
      </tr>
      <tr>
        <th><label for="{{ form.fat.id_for_label }}">脂質</label></th>
        <td>{{ form.fat }}</td>
      </tr>
      <tr>
        <th><label for="{{ form.carbohydrate.id_for_label }}">炭水化物</label></th>
        <td>{{ form.carbohydrate }}</td>
      </tr>
      <tr>
        <td colspan="2"><button type="submit" class="btn btn-primary">Save</button></td>
      </tr>
    </form>
  </table>
{% endblock %}
```

## アプリのインストール

`settings.py`に今回のアプリをインストールします。

```python:nutrient_calculator/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'nutrition', #この部分を追加
]
```


## テンプレートファイルの設置

Djangoでテンプレートファイルを使用するには、まずDjangoプロジェクトのsettings.pyファイルで、テンプレートが保存されているディレクトリを指定する必要があります。以下の内容を追記してください。

```python:nutrient_calculator/settings.py
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Templete

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
                os.path.join(BASE_DIR, 'templates'),
                os.path.join(BASE_DIR, 'templates', 'nutrition'),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.media',
                'django.template.context_processors.static',
                'django.template.context_processors.tz',
            ],
        },
    }
]
```
プロジェクトのルートディレクトリに"templates"ディレクトリを作成し、その中にテンプレートファイルを配置しています。


`BASE_DIR`は、プロジェクトのルートディレクトリのパスを表します。
このコードは、Pythonスクリプトが存在するディレクトリの親ディレクトリを取得するために使用されます。この例では、__file__はスクリプトのファイル名を含む現在のファイルへのパスを表します。このパスにos.path.abspath関数を適用することで、パスを絶対パスに変換します。そして、os.path.dirname関数を2回適用することで、親ディレクトリのパスを取得します。

# CSSの設定

CSSファイルのサンプルは簡単にすませます。必要な内容を記してください。
`nutrition`ディレクトリの配下に`static`ディレクトリを作成して配置してください。

```bash
mkdir static
```

```CSS:nutrition/static/style.css
body {
  background-color: lightblue;
  color: white;
}
```
`static`に配置した`CSS`ファイルを読み込めるように`settings.py`に追加します。

```python:/nutrient_calculator/settings.py
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'nutrition', 'static'),
]

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
```

`BASE_DIR`は、templateファイルの設置の時の変数を再利用しています。
`STATIC_URL`は、ブラウザから静的ファイルにアクセスするためのURLを指定します。通常、`/static/`を設定します。
`STATICFILES_DIRS`は、アプリケーションごとに用意した静的ファイルの場所を指定します。上記の例では、プロジェクトルートディレクトリ内の`staticディレクトリ`に置かれた静的ファイルを指定しています。
`STATIC_ROOT`は、静的ファイルのコレクション先のディレクトリを指定します。通常、プロジェクトルートディレクトリ内に`staticfilesディレクトリ`を作成し、そのパスを指定します。このディレクトリは、`collectstatic`コマンドを実行することで生成されます。

この変数を設定すると、
```bash
python manage.py collectstatic
```
コマンドを使用して、静的ファイルを1つの場所に収集できます。収集したファイルは、Webサーバーで提供することができます。

## 動作確認

```bash
python manage.py runserver 0.0.0.0:8000
```

`http://localhost:8000/nutrition`にアクセスするとwebアプリが利用できます。

Djangoプロジェクトを開発サーバー上で起動するためのコマンドです。

このコマンドを実行することで、DjangoはHTTPリクエストを待ち受けるWebサーバーを起動します。 `0.0.0.0`は、サーバーがすべてのネットワークインターフェイスで接続を受け入れることを指定します。 `8000`は、Webサーバーが待ち受けるポート番号を指定します。

このコマンドを実行すると、デフォルトでDjangoの開発サーバーが起動し、http://localhost:8000でアクセスできるようになります。また、サーバーは同じネットワーク上の他のコンピューターからもアクセス可能です。