前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしよう
⑤ユーザー登録(サインイン)機能を作ろう
⑥ユーザーごとのデータ登録できるようにする〜CRU編
⑦ユーザーごとのデータ登録できるようにする〜削除編
⑧画像ファイルのアップロード
⑨身長体重を記録する@一括削除機能つき <--ここです
⑩成長曲線グラフを描いてみよう
⑪本番環境へデプロイ+色々手直し
Goal
身長と体重を記録できるようにする。(次の章でグラフ化するデータを登録)
やることとポイントのまとめ
この章で実装するのは、基本的に⑧で作ったもの構成と同じ。
1つ違うのは、レコードの一括削除機能をつけているところ。
Template→Viewへ、削除対象レコードのIDを渡すところが前回と違う。
あとは、Djangoの基本に則って、、、
①新しいAppを作る→setting.pyとurls.pyにAppを作ったことを知らせる
②Modelを作ろう→Migrationする。admin.pyにmodelを追加する
③Templateを作ろう→urls.pyでURLの体系を指定しよう
④Viewを作ろう→Modelを扱いやすいようFormも要るね
①APP作成〜プロジェクト内の指定
docker-compose run web python ./manage.py startapp phys
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users.apps.UsersConfig',
'shoes.apps.ShoesConfig',
'phys.apps.PhysConfig', #追加
'bootstrap4',
'bootstrap_datepicker_plus',
'django_cleanup.apps.CleanupConfig',
]
from django.contrib import admin
from django.urls import path, include
from . import settings
from django.contrib.staticfiles.urls import static
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('users.urls')),
path('',include('shoes.urls')),
path('',include('phys.urls')), #追加
]
urlpatterns += staticfiles_urlpatterns()
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
②Model作成
from django.db import models
from django.conf import settings
from django.utils import timezone
from users.models import KidsProfile
class PhysData(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
kidsProfile = models.ForeignKey(KidsProfile, on_delete=models.CASCADE)
weight = models.DecimalField(
max_digits=6,
decimal_places=3
)
height = models.DecimalField(
max_digits=5,
decimal_places=2
)
date = models.DateField(default=timezone.now)
def __str__(self):
return self.kidsProfile.name + str(self.date)
from django.contrib import admin
from .models import PhysData
admin.site.register(PhysData)
③テンプレート〜URL設計
phys_data_addとphys_data_editは省略。
前回の章で作ったものと、変わらないです。
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-md-12 col-lg-2">
<div class="list-group">
{% for kidsProfile in kidsProfiles %}
<a href="/phys/list/{{kidsProfile.id}}" class="list-group-item list-group-item-action">{{kidsProfile.name}}</a>
{% endfor %}
</div>
</div>
<div class="col-md-12 col-lg-10 overflow-auto">
<form method="post" action="{% url 'phys:phys_data_delete' %}">
<table class="table table-hover">
<thead class="thead-dark">
<tr>
<th>名前</th>
<th>日付</th>
<th>体重(kg)</th>
<th>身長(cm)</th>
<th>修正</th>
<th>削除</th>
</tr>
</thead>
{% for data_post in data_posts %}
<tr>
<td class="text-center">{{data_post.kidsProfile.name }}</td class="text-center">
<td class="text-center">{{data_post.date }}</td>
<td class="text-center">{{data_post.weight }}</td>
<td class="text-center">{{data_post.height }}</td>
<td class="text-center"><a href="{% url 'phys:phys_data_edit' data_post.pk %}" class="btn btn-success btn-sm" role="button">編集</a></td>
<td class="text-center"><input type="checkbox" name="delete_ids" value="{{ data_post.pk }}"></input></td>
</tr>
{% endfor %}
<tr>
<td>
<a href="{% url 'phys:phys_data_add' %}" role="button" class="btn btn-primary mr-auto">追加</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="text-center">
{% csrf_token %}
<input type="submit" value="一括削除" class="btn btn-danger float-right">
</td>
</tr>
</table>
</form>
</div>
{% endblock %}
from django.urls import path
from . import views
app_name = 'phys'
urlpatterns = [
path('phys/list/', views.phys_data_list, name='phys_data_list'),
path('phys/list/<kidsProfileId>', views.phys_data_list, name='phys_data_list'),
path('phys/data_add/', views.phys_data_add, name='phys_data_add'),
path('phys/data_edit/<DataPostId>', views.phys_data_edit, name='phys_data_edit'),
path('phys/data_delete/', views.phys_data_delete, name='phys_data_delete'),
]
④ViewとModel
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import PhysData
from users.models import KidsProfile
from .forms import PhysDataForm, PhysDataEditForm
#身長体重データを昇順で返却
@login_required
def phys_data_list(request, **kwargs):
user_name = request.user
if len(kwargs) > 0:
kidsProfileId = kwargs["kidsProfileId"]
else:
kidsProfileId = KidsProfile.objects.filter(user=user_name).order_by('id')[0]
kids_profiles = KidsProfile.objects.filter(user=user_name)
data_posts = PhysData.objects.filter(user=user_name, kidsProfile=kidsProfileId).order_by('date')
params = {
'data_posts': data_posts,
'kidsProfiles' : kids_profiles,
}
return render(request, 'phys/phys_data_list.html', params)
#身長体重データを新規登録する
@login_required
def phys_data_add(request):
user_name = request.user
if request.method == 'POST':
form = PhysDataForm(request.POST, user = user_name)
if form.is_valid():
data = form.save(commit=False)
data.user = user_name
data.save()
return redirect('phys:phys_data_list')
else:
form = PhysDataForm(user = user_name)
return render(request, 'phys/phys_data_add.html', {'form': form})
#身長体重データを編集する
@login_required
def phys_data_edit(request, DataPostId):
physData = PhysData.objects.get(pk=DataPostId)
if request.method == 'POST':
form = PhysDataEditForm(request.POST)
if form.is_valid():
data = form.save(commit=False)
data.user = request.user
data.kidsProfile = physData.kidsProfile
data.id = physData.id
data.save()
return redirect('phys:phys_data_list')
else:
form = PhysDataEditForm(
{
'weight' : physData.weight ,
'height' : physData.height ,
'date' : physData.date
},
user = request.user
)
return render(request, 'phys/phys_data_edit.html', {'form': form})
#身長体重データを削除する
@login_required
def phys_data_delete(request):
delete_ids = request.POST.getlist('delete_ids')
PhysData.objects.filter(pk__in=delete_ids).delete()
return redirect('phys:phys_data_list')
from django import forms
from .models import PhysData
from users.models import KidsProfile
import bootstrap_datepicker_plus as datetimepicker
class PhysDataForm(forms.ModelForm):
class Meta:
model = PhysData
fields = ('kidsProfile', 'weight', 'height', 'date',)
widgets = {
'date': datetimepicker.DatePickerInput(
format='%Y-%m-%d'),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user','')
super(PhysDataForm, self).__init__(*args, **kwargs)
self.fields['kidsProfile'] = forms.ModelChoiceField(queryset=KidsProfile.objects.filter(user=user))
class PhysDataEditForm(forms.ModelForm):
class Meta:
model = PhysData
fields = ('weight', 'height', 'date',)
widgets = {
'date': datetimepicker.DatePickerInput(
format='%Y-%m-%d',
),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user','')
kidsProfileId = kwargs.pop('kidsProfileId','')
super(PhysDataEditForm, self).__init__(*args, **kwargs)
できあがり
こんな形になります。削除レコードはチェックボックス形式で纏めて指定&削除可能。
登録画面は、普通。編集画面も、ほぼ同様。(子供の名前を選ばない程度)