【 Docker＋Nginx＋Django＋RDS】WEBアプリができるまで⑥ユーザーごとのデータ登録できるようにする〜CRU編

# 前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
　①[Djangoのようこそページへたどり着くまで](https://qiita.com/quantity82/items/e8c2392bf982efb55f80)
　②[NginxでDjangoのようこそページへたどり着くまで](https://qiita.com/quantity82/items/88a6348135e25c661feb)
　③[カスタムユーザーを作ってadminにたどり着く](https://qiita.com/quantity82/items/b3cd6ded5bbb5f3726f3)
　④[ログインログアウトをしよう](https://qiita.com/quantity82/items/8e4b7ce2ef56e60ad392)
　⑤[ユーザー登録（サインイン）機能を作ろう](https://qiita.com/quantity82/items/bbfc7e72d4a65c71e24c)
　⑥[ユーザーごとのデータ登録できるようにする〜CRU編](https://qiita.com/quantity82/items/4d019a4ee475208a5a26)　<--ここです
　⑦[ユーザーごとのデータ登録できるようにする〜削除編](https://qiita.com/quantity82/items/c48fee71b5f8eb766dae)
　⑧[画像ファイルのアップロード](https://qiita.com/quantity82/items/9bcbd25902f6878ee325)
　⑨[身長体重を記録する＠一括削除機能つき](https://qiita.com/quantity82/items/c8e58742eeaa8c0fc954)
　⑩[成長曲線グラフを描いてみよう](https://qiita.com/quantity82/items/619f5ed2a6cd45f7f592)
　⑪[本番環境へデプロイ＋色々手直し](https://qiita.com/quantity82/items/6e91b7ee7a2d7e0fab32)
# Goal
ユーザーごとのデータを登録できるようにする。CRUDのうちCRUまでいきます。
作るのは、ユーザーごとに子供の情報を登録できるようにします。

# Model
名前、性別に加えて、あとで身長体重の成長曲線が書けるよう、生年月日を持ちます。
Modelを直したら、マイグレーションを実施。（コマンドは省略）

```python:models.py
from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.base_user import BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.utils import timezone

class UserManager(BaseUserManager):
（略）

class User(AbstractBaseUser, PermissionsMixin):
（略）

class KidsProfile(models.Model):
    GENDER_CHOICES = (
        ('1', '女性'),
        ('2', '男性'),
    )
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    name = models.CharField(max_length=20, blank=False)
    gender = gender = models.CharField(max_length=2, choices=GENDER_CHOICES, blank=False)
    birthday = models.DateField(null=False, blank=False)

    def __str__(self):
        title = self.name
        return title
```
モデルを追加したら、adminで操作できるようにしましょう。

```admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.utils.translation import ugettext_lazy as _
from .models import User, KidsProfile #モデル追加


class MyUserChangeForm(UserChangeForm):
（略）


class MyUserCreationForm(UserCreationForm):
（略）

class MyUserAdmin(UserAdmin):
（略）

admin.site.register(User, MyUserAdmin)
admin.site.register(KidsProfile) #追加
```


# Form
pythonのDateFieldはyyyy-mm-dd形式なので、入力時のバリデーションが面倒。
GUI的に入力しやすくするため、datetimepickerを入れます。

このフォームを入力するのはログインしてからなので、
Modelでは保有しているuser情報はFormでは見せないようにしてます。
（user情報はviewの中でシステム的に持ち回る）

```python:forms.py
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import UserCreationForm
from .models import KidsProfile
import bootstrap_datepicker_plus as datetimepicker

User = get_user_model()

class SignUpForm(UserCreationForm):
（略）

class LoginForm(AuthenticationForm):
（略）

class KidsProfileForm(forms.ModelForm):
    class Meta:
        model = KidsProfile
        fields = ('name', 'gender', 'birthday')
        widgets = {
            'birthday': datetimepicker.DatePickerInput(
                           format='%Y-%m-%d'),
        }

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', '')
        super(KidsProfileForm, self).__init__(*args, **kwargs)
```
```requirement.txt
Django==2.2.2
psycopg2==2.8.4
uwsgi==2.0.17
django-bootstrap4==1.1.1
django-bootstrap-datepicker-plus==3.0.5 #追加
```
```python:setting.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users.apps.UsersConfig',
    'bootstrap4',
    'bootstrap_datepicker_plus', #追加
]
```

# View
kids_profile_add関数では、POSTで受け取ったformに加えて
request.userで取得したユーザー情報を付与して保存してます。

kids_profile_edit関数では、それに加えて変更したい情報を事前に表示しておきたいので、
データのIDをURL経由で受け取ります。

```users/views.py
#子供情報追加
@login_required
def kids_profile_add(request):
    user_name = request.user
    if request.method == 'POST':
      form = KidsProfileForm(request.POST, user = user_name)
      if form.is_valid():
        data = form.save(commit=False)
        data.user = user_name
        data.save()
        return redirect('users:mypage')
    else:
      form = KidsProfileForm(user = user_name)
    return render(request, 'users/kids_profile_add.html', {'form': form})

#子供情報編集
@login_required
def kids_profile_edit(request, kidsProfileId):
    kidsProfileData = KidsProfile.objects.get(pk=kidsProfileId)
    if request.method == 'POST':
      form = KidsProfileForm(request.POST)
      if form.is_valid():
        data = form.save(commit=False)
        data.id = kidsProfileData.id
        data.user = request.user
        data.save()
        return redirect('users:mypage')
    else:
      form = KidsProfileForm(
        {
          'name' : kidsProfileData.name ,
          'gender' : kidsProfileData.gender ,
          'birthday' : kidsProfileData.birthday
        },
      )
    return render(request, 'users/kids_profile_edit.html', {'form': form})
```

# HTML
_addも_editも、テンプレートはほぼ一緒。
{{ form.media }}を書かないと、datetimepickerが動かないので書く。

```kids_profile_add.html
{% extends 'base.html' %}

{% block content %}

<!--日付入力フォーマット用-->
{{ form.media }}

<div class="col-md-12 col-lg-5">
    <h2>New data</h2>
    <form method="POST" class="post-form">{% csrf_token %}
        {% bootstrap_form form %}
        <button type="submit" class="save btn btn-primary">Save</button>
    </form>
</div>
{% endblock %}

```

```kids_profile_edit.html
{% extends 'base.html' %}

{% block content %}

<!--日付入力フォーマット用-->
{{ form.media }}

<div class="col-md-12 col-lg-5">
    <h2>Data Edit</h2>
    <form method="POST" class="post-form">{% csrf_token %}
        {% bootstrap_form form %}
        <button type="submit" class="save btn btn-primary">Save</button>
    </form>
</div>
{% endblock %}
```

ユーザーに紐づく子供の情報をTable形式で表示します。
子供の情報は複数人分が登録されるので、for文でレコード分だけ表示します。

```mypage.html
{% extends 'base.html' %}

{% block content %}

<div class="col-md-12 col-lg-5">
    <h2>My Page</h2>

    <br>
    <br>

    <table class = "table">
      <tr>
        <td>ユーザー名</td>
        <td>{{ user }}</td>
      </tr>
    </table>

    <table class = "table table-hover">
      <thead class = "thead-dark">
        <tr>
          <th>名前</th>
          <th>性別</th>
          <th>誕生日</th>
          <th></th>
          <th></th>
        </tr>
      </thead>
      {% for kids_profile in kidsProfiles %}
      <tr>
        <td>{{ kids_profile.name }}</td>
        <td>{{ kids_profile.get_gender_display }}</td>
        <td>{{ kids_profile.birthday }}</td>
        <td><a href="{% url 'users:kids_profile_edit' kids_profile.id %}" class="btn btn-success btn-sm">編集</a></td>
        <td><button type="button" class="btn btn-danger btn-sm del_confirm">削除</button></td>
      </tr>

      {% endfor%}
      <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td><a href="{% url 'users:kids_profile_add' %}" class="btn btn-primary btn-sm" role="button">追加</a></td>
      </tr>
    </table>

</div>

{% endblock content %}
```

# urls
新しいページを作ったので、URLのパスを通す。
kidsProfileIdをurlsで指定することで、URLを使ってHTMLとPythonで変数を受け渡す。

```urls.py
app_name = 'users'

urlpatterns = [
    path('', views.mypage, name='mypage'),
    path('signup/', views.signup, name='signup'),
    path('mypage/', views.mypage, name='mypage'),
    path('login/', views.login_mypage.as_view(), name='login'),
    path('logout/', views.logout.as_view(), name='logout'),
    path('kids_profile_add/', views.kids_profile_add, name='kids_profile_add'), #追加
    path('kids_profile_edit/<kidsProfileId>', views.kids_profile_edit, name='kids_profile_edit'),#追加
]
```

# 動かす！
登録がない状態。
<img width="1440" alt="スクリーンショット 2020-02-11 22.20.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547363/40f12879-31c4-7475-a799-ecd31e89da75.png">
登録画面。datetimepickerだと、こんな感じで表示される。
ちなみにスマホでやると、ちょっとやり辛い。解消方法はあるようだが、試してはいない。
<img width="1440" alt="スクリーンショット 2020-02-11 22.21.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547363/cc8a14a2-b001-75e5-be97-0620d1653328.png">
編集すると、こんな感じ。各項目、元々の情報が入った状態で、変更可能なフォームが表示される。
<img width="1440" alt="スクリーンショット 2020-02-11 22.22.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547363/6914b3ab-864f-79e7-1109-be5a721e4132.png">
登録されると、こんな感じ。この時点では削除ボタンはダミー。
<img width="1440" alt="スクリーンショット 2020-02-11 22.22.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547363/9f0c6d0a-53a8-1693-83c9-c304f9680a29.png">