Help us understand the problem. What is going on with this article?

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

前置き

独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
 ①Djangoのようこそページへたどり着くまで
 ②NginxでDjangoのようこそページへたどり着くまで
 ③カスタムユーザーを作ってadminにたどり着く
 ④ログインログアウトをしよう
 ⑤ユーザー登録(サインイン)機能を作ろう
 ⑥ユーザーごとのデータ登録できるようにする〜CRU編 <--ここです
 ⑦ユーザーごとのデータ登録できるようにする〜削除編
 ⑧画像ファイルのアップロード
 ⑨身長体重を記録する@一括削除機能つき
 ⑩成長曲線グラフを描いてみよう
 ⑪本番環境へデプロイ+色々手直し

Goal

ユーザーごとのデータを登録できるようにする。CRUDのうちCRUまでいきます。
作るのは、ユーザーごとに子供の情報を登録できるようにします。

Model

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

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の中でシステム的に持ち回る)

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 #追加
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'),#追加
]

動かす!

登録がない状態。
スクリーンショット 2020-02-11 22.20.53.png
登録画面。datetimepickerだと、こんな感じで表示される。
ちなみにスマホでやると、ちょっとやり辛い。解消方法はあるようだが、試してはいない。
スクリーンショット 2020-02-11 22.21.55.png
編集すると、こんな感じ。各項目、元々の情報が入った状態で、変更可能なフォームが表示される。
スクリーンショット 2020-02-11 22.22.27.png
登録されると、こんな感じ。この時点では削除ボタンはダミー。
スクリーンショット 2020-02-11 22.22.33.png

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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした