2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-02-11

前置き

独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
 ①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

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?