質問編集履歴

1

参考までにOtherモデルも追加しました。

2021/07/16 02:21

投稿

kawataku
kawataku

スコア3

test CHANGED
File without changes
test CHANGED
@@ -1,23 +1,23 @@
1
1
  ### 前提・実現したいこと
2
2
 
3
+ 実現したいこととしましては、`modelformset`を使用して、`Update`画面でレコードの編集や追加、削除を行えるようになりたいです。
4
+
5
+
6
+
3
7
  `Django`でwebアプリを作成しているのですが、`Update`の画面において、更新を上手く行えません。
4
8
 
5
9
 
6
10
 
7
- また、`can_delete`を`True`にしており、`template`内に、`{{spot.DELETE}}`を書いたのですが、チェックボックスが出るのみで削除ができませんでした。
8
-
9
-
10
-
11
- 実現したいこととしましては、
12
-
13
- `Update`画面でレコードの編集や追加、削除を行えるようになりたいです。
14
-
15
- よろしくお願い致します。
16
-
17
-
18
-
19
11
  ### 発生している問題・エラーメッセージ
20
12
 
13
+ `Other`と`Spot`というモデルがありまして、
14
+
15
+ * Otherは`edit`画面において、新しいレコードの追加はできますが、既存のレコードを編集することができません。
16
+
17
+ * Spotは`edit`画面において、新しいレコードの追加をしたら、エラーになり、既存のレコードは編集できない状態です。
18
+
19
+
20
+
21
21
 
22
22
 
23
23
  `jQuery`を使って動的にフォームを増減させているのですが、`Update`画面でフォームを追加して保存することも、既存のレコードを編集することもできません。
@@ -30,8 +30,6 @@
30
30
 
31
31
  ```
32
32
 
33
- このモデルとほぼ同じで、`DateTimeField`が無いモデルでは上手くいったのですが、今回のモデルでは上手くいきません。
34
-
35
33
 
36
34
 
37
35
  ### 該当のソースコード
@@ -42,6 +40,16 @@
42
40
 
43
41
  ```django
44
42
 
43
+ class Other(models.Model):
44
+
45
+ trip = models.ForeignKey(Trip, on_delete=models.CASCADE, related_name='extra')
46
+
47
+ extra_name = models.CharField(max_length=50)
48
+
49
+ extra_cost = models.IntegerField(validators=[MinValueValidator(0, '0以上で入力してください')])
50
+
51
+
52
+
45
53
  class Spot(models.Model):
46
54
 
47
55
  trip = models.ForeignKey(Trip, on_delete=models.CASCADE, related_name='spot')
@@ -52,12 +60,6 @@
52
60
 
53
61
  spot_cost = models.IntegerField(null=False, blank=False, validators=[MinValueValidator(0, '0以上で入力してください')])
54
62
 
55
-
56
-
57
- def __str__(self):
58
-
59
- return str(self.spot_name) + ' (' + str(self.spot_time) + '+' + str(self.spot_cost) + ')'
60
-
61
63
  ```
62
64
 
63
65
 
@@ -68,12 +70,46 @@
68
70
 
69
71
  ```django
70
72
 
73
+ class OtherForm(forms.ModelForm):
74
+
75
+ def __init__(self, *args, **kwargs):
76
+
77
+ super().__init__(*args, **kwargs)
78
+
79
+ self.fields['extra_name'].required = False
80
+
81
+ self.fields['extra_cost'].required = False
82
+
83
+
84
+
85
+ class Meta:
86
+
87
+ model = Other
88
+
89
+ fields = ('extra_name', 'extra_cost')
90
+
91
+ widgets = {
92
+
93
+ 'extra_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '追加の費用の項目', 'name': 'extra_menu'}),
94
+
95
+ 'extra_cost': forms.NumberInput(attrs={'class': 'form-control costs', 'placeholder': '追加の費用', 'name': 'extra_cost'})
96
+
97
+ }
98
+
99
+
100
+
71
101
  class SpotForm(forms.ModelForm):
72
102
 
73
103
  def __init__(self, *args, **kwargs):
74
104
 
75
105
  super().__init__(*args, **kwargs)
76
106
 
107
+ self.fields['transport_name'].required = False
108
+
109
+ self.fields['transport_fee'].required = False
110
+
111
+ self.fields['transport_time'].required = False
112
+
77
113
 
78
114
 
79
115
  class Meta:
@@ -106,27 +142,45 @@
106
142
 
107
143
  def test_edit(request, num):
108
144
 
145
+ OtherFormSet = forms.modelformset_factory(
146
+
147
+ Other, form=OtherForm, extra=0, can_delete=True
148
+
149
+ )
150
+
151
+ SpotFormSet = forms.modelformset_factory(
152
+
153
+ Spot, form=SpotForm, extra=0, can_delete=True
154
+
155
+ )
156
+
157
+ trip_model = Trip.objects.get(id=num)
158
+
159
+
160
+
161
+ other_model = Other.objects.filter(trip=trip_model)
162
+
109
163
  spot_model = Spot.objects.filter(trip=trip_model)
110
164
 
111
165
 
112
166
 
113
- SpotFormSet = forms.modelformset_factory(
114
-
115
- Spot, form=SpotForm, extra=0, can_delete=True
116
-
117
- )
118
-
119
- # idがnumのtripを取得する
120
-
121
167
  if request.method == 'POST':
122
168
 
123
- # 更新時は既に親モデルと繋がっている状態で渡されるから、いちいち外部キーを取得する必要はない
169
+ others = OtherFormSet(request.POST, queryset=other_model)
124
170
 
125
171
  spots = SpotFormSet(request.POST, queryset=spot_model)
126
172
 
173
+
174
+
175
+ for other in others:
176
+
177
+ if other.is_valid():
178
+
179
+ other_db = other.save(commit=False)
180
+
127
- print('postされました')
181
+ other_db.trip = trip_model
128
-
182
+
129
- # バリデーションのチェック
183
+ other_db.save()
130
184
 
131
185
 
132
186
 
@@ -146,16 +200,20 @@
146
200
 
147
201
 
148
202
 
149
- # Getアクセス時の処理
150
-
151
203
  else:
152
204
 
153
205
  spots = SpotFormSet(queryset=spot_model)
154
206
 
207
+ others = OtherFormSet(queryset=other_model)
208
+
209
+
210
+
155
211
 
156
212
 
157
213
  params = {
158
214
 
215
+ 'others': others,
216
+
159
217
  'spots': spots,
160
218
 
161
219
  'id': num
@@ -214,9 +272,45 @@
214
272
 
215
273
  <button type="button" id="delSpot">観光地を削除</button>
216
274
 
275
+ <div class="row mt-4" id="other_total">
276
+
277
+ <div class="col-12">
278
+
279
+ {{ others.management_form }}
280
+
281
+ <div id="other_formset">
282
+
283
+ {% for other in others %}
284
+
285
+ <div class="row mb-3" id="input-form-{{ forloop.counter0 }}">
286
+
287
+ <div class="col-6">{{ other.extra_name }}</div>
288
+
289
+ <div class="col-6">
290
+
291
+ {{ other.extra_cost }}
292
+
293
+ </div>
294
+
295
+ </div>
296
+
297
+ {% endfor %}
298
+
299
+ </div>
300
+
301
+ <div id="extra"></div>
302
+
303
+ </div>
304
+
305
+ </div>
306
+
307
+ <button class="btn btn-outline-dark mr-3" type="button" id="addExtra">費用を追加</button>
308
+
309
+ <button class="btn btn-outline-dark" type="button" id="delExtra">費用を削除</button>
310
+
217
311
  <button type="submit">完成!</button>
218
312
 
219
- </form>
313
+ </form>
220
314
 
221
315
  {% endblock %}
222
316
 
@@ -236,8 +330,6 @@
236
330
 
237
331
  format: 'Y-m-d',
238
332
 
239
- // numberOfMonths: 2
240
-
241
333
  });
242
334
 
243
335
  $('.smallDate').datetimepicker({
@@ -248,176 +340,132 @@
248
340
 
249
341
  });
250
342
 
343
+
344
+
345
+ var TotalManageElement = $('input#id_form-TOTAL_FORMS');
346
+
347
+ var currentCount = parseInt(TotalManageElement.val());
348
+
349
+ // spot用の処理
350
+
251
- $('.costs').on('focus', function(){
351
+ $('#addSpot').on('click', function(){
352
+
252
-
353
+ // var new_tranSpot = $('#tranSpot_hidden').html();
354
+
355
+ var new_tranSpot = `<div class="row mb-5" id="input-spot-form-0">
356
+
253
- var cost = Number($(this).val());
357
+ <div class="col-4">
358
+
254
-
359
+ <input type="text" name="form-0-spot_name" class="form-control" placeholder="観光地" maxlength="50" id="id_form-0-spot_name">
360
+
361
+ </div>
362
+
363
+ <div class="col-4">
364
+
365
+ <input type="text" name="form-0-spot_time" class="form-control smallDate" placeholder="到着時間" autocomplete="off" id="id_form-0-spot_time">
366
+
367
+ </div>
368
+
369
+ <div class="col-4">
370
+
371
+ <input type="number" name="form-0-spot_cost" class="form-control costs" placeholder="滞在料金" id="id_form-0-spot_cost">
372
+
373
+ </div>
374
+
375
+ </div>`
376
+
255
- if(cost != NaN){
377
+ currentCount += 1
378
+
256
-
379
+ new_tranSpot = new_tranSpot.replace(/form-0/g, 'form-'+currentCount);
380
+
381
+ $('#tranSpot').append(new_tranSpot);
382
+
383
+ $('input#id_form-'+ currentCount +'-spot_cost').removeClass('costs').addClass('tranSpot_costs');
384
+
257
- var costs = Number($('#all_money').text()) - cost
385
+ TotalManageElement.attr('value', currentCount);
258
-
386
+
387
+
388
+
259
- $('#all_money').text(costs);
389
+ $('.smallDate').datetimepicker({
390
+
391
+ step: 15,
392
+
393
+ format: 'Y-m-d H:i',
394
+
395
+ });
396
+
397
+ });
398
+
399
+ // フォームを削除する
400
+
401
+ $('#delSpot').on('click', function(){
402
+
403
+ if ($('#tranSpot').children().length){
404
+
405
+ console.log('world');
406
+
407
+ // 削除されるフォームの金額を消す
408
+
409
+ var money = Number($('#tranSpot').children('div:last').find('.tranSpot_costs').val());
410
+
411
+ $('#tranSpot').children('div:last').remove();
260
412
 
261
413
  }
262
414
 
263
415
  });
264
416
 
417
+ // other用の処理
418
+
265
- $('.costs').on('blur', function(){
419
+ $('#addExtra').on('click', function(){
420
+
266
-
421
+ var new_code = `<div class="row mb-3" id="input-form-0">
422
+
267
-
423
+ <div class="col-6"><input type="text" name="form-0-extra_name" class="form-control" placeholder="追加の費用の項目" maxlength="50" id="id_form-0-extra_name"></div>
424
+
268
-
425
+ <div class="col-6"><input type="number" name="form-0-extra_cost" class="form-control costs" placeholder="追加の費用" id="id_form-0-extra_cost"></div>
426
+
427
+ </div>`
428
+
269
- var cost = Number($('#all_money').text())+ Number($(this).val());
429
+ new_code = new_code.replace(/form-0/g, 'form-'+currentCount);
270
-
430
+
271
- $('#all_money').text(cost);
431
+ $('#extra').append(new_code);
432
+
433
+ $('input#id_form-'+ currentCount +'-extra_cost').removeClass('costs').addClass('extra_costs');
434
+
435
+ currentCount += 1
436
+
437
+ TotalManageElement.attr('value', currentCount);
272
438
 
273
439
  });
274
440
 
275
-
276
-
277
- var TotalManageElement = $('input#id_form-TOTAL_FORMS');
278
-
279
- var currentCount = parseInt(TotalManageElement.val());
280
-
281
- // spot用の処理
282
-
283
- $('#addSpot').on('click', function(){
441
+ $('#delExtra').on('click', function(){
442
+
284
-
443
+ // other_formsetかextraかをチェックする
444
+
445
+ if ($('#extra').children().length){
446
+
285
- // var new_tranSpot = $('#tranSpot_hidden').html();
447
+ $('#extra').children('div:last').remove();
286
-
287
- var new_tranSpot = `<div class="row mb-5" id="input-spot-form-0">
448
+
288
-
289
- <div class="col-4">
290
-
291
- <input type="text" name="form-0-spot_name" class="form-control" placeholder="観光地" maxlength="50" id="id_form-0-spot_name">
292
-
293
- </div>
294
-
295
- <div class="col-4">
296
-
297
- <input type="text" name="form-0-spot_time" class="form-control smallDate" placeholder="到着時間" autocomplete="off" id="id_form-0-spot_time">
298
-
299
- </div>
300
-
301
- <div class="col-4">
302
-
303
- <input type="number" name="form-0-spot_cost" class="form-control costs" placeholder="滞在料金" id="id_form-0-spot_cost">
304
-
305
- </div>
306
-
307
- </div>`
308
-
309
- currentCount += 1
449
+ currentCount -= 1
310
-
311
- new_tranSpot = new_tranSpot.replace(/form-0/g, 'form-'+currentCount);
450
+
312
-
313
- $('#tranSpot').append(new_tranSpot);
314
-
315
- $('input#id_form-'+ currentCount +'-spot_cost').removeClass('costs').addClass('tranSpot_costs');
316
-
317
- TotalManageElement.attr('value', currentCount);
451
+ TotalManageElement.attr('value', currentCount);
318
-
319
-
320
-
452
+
321
- $('.smallDate').datetimepicker({
453
+ }else if($('#other_formset').children().length == 1){
322
-
454
+
323
- step: 15,
455
+ return ;
456
+
324
-
457
+ }else{
458
+
459
+ $('#other_formset').children('div:last').remove();
460
+
461
+ currentCount -= 1
462
+
325
- format: 'Y-m-d H:i',
463
+ TotalManageElement.attr('value', currentCount);
326
-
464
+
327
- });
465
+ };
328
-
329
- $('.tranSpot_costs').on('focus', function(){
330
-
331
- var cost = Number($(this).val());
332
-
333
- if(cost != NaN){
334
-
335
- var costs = Number($('#all_money').text()) - cost
336
-
337
- $('#all_money').text(costs);
338
-
339
- }
340
-
341
- });
342
-
343
- $('.tranSpot_costs').on('blur', function(){
344
-
345
- var cost = Number($('#all_money').text())+ Number($(this).val());
346
-
347
- $('#all_money').text(cost);
348
-
349
- });
350
466
 
351
467
  });
352
468
 
353
- // フォームを削除する
354
-
355
- $('#delSpot').on('click', function(){
356
-
357
- if ($('#tranSpot').children().length){
358
-
359
- console.log('world');
360
-
361
- // 削除されるフォームの金額を消す
362
-
363
- var money = Number($('#tranSpot').children('div:last').find('.tranSpot_costs').val());
364
-
365
- $('#tranSpot').children('div:last').remove();
366
-
367
-
368
-
369
- // 削除されるフォームの金額分を差し引く
370
-
371
- if(money != NaN) {
372
-
373
- var costs = Number($('#all_money').text()) - money
374
-
375
- }else{
376
-
377
- var costs = Number($('#all_money').text())
378
-
379
- }
380
-
381
- // 合計金額が0を下回らないように一応書いておく
382
-
383
- if (costs <= 0) { $('#all_money').text('0'); }
384
-
385
- $('#all_money').text(costs);
386
-
387
- }else if($('#tranSpot_formset').children().length == 1){
388
-
389
- return ;
390
-
391
- }else{
392
-
393
- console.log('Hello');
394
-
395
- // 削除されるフォームの金額を消す
396
-
397
- var money = Number($('#tranSpot_formset').children('div:last').find('.costs').val());
398
-
399
- $('#tranSpot_formset').children('div:last').remove();
400
-
401
-
402
-
403
- // 削除されるフォームの金額分を差し引く
404
-
405
- console.log('money:', money);
406
-
407
- var costs = Number($('#all_money').text()) - money
408
-
409
- // 合計金額が0を下回らないように一応書いておく
410
-
411
- if (costs <= 0) { $('#all_money').text('0'); }
412
-
413
- $('#all_money').text(costs);
414
-
415
-
416
-
417
- }
418
-
419
- });
420
-
421
469
  });
422
470
 
423
471
  </script>
@@ -426,6 +474,10 @@
426
474
 
427
475
  ```
428
476
 
477
+
478
+
479
+
480
+
429
481
  template内で`formset`を回しております。
430
482
 
431
483
  また、`jQuery`を使用して、フォームの増減を行っております。
@@ -438,16 +490,30 @@
438
490
 
439
491
  ### 試したこと
440
492
 
493
+
494
+
441
- 英語で記事を探たりしてみたのですが、いまいち自分と同じような人わかりやすい解決方法を書いている方を見つけられなかっです
495
+ `DateTimeField`が問題かと思い、削除て試してみたのですが、上手くせんた。
442
-
443
-
444
-
496
+
497
+
498
+
445
- また、`DateTimeField`が無い他モデルではレコード追加のみは上手くいったので、`DateTimeField`に問題があるのかなと思い、探してみたのですが、解決策は見つけられませんでした。
499
+ また、`Create`のように`queryset=Spot.objects.none()`に変更してみたのですが、保存できませんでした。
500
+
501
+
502
+
503
+ 管理画面にて直接レコードを保存することはできましたが、editの時は上手くいきませんでした。
446
504
 
447
505
 
448
506
 
449
507
  ### 補足情報(FW/ツールのバージョンなど)
450
508
 
509
+ `request.POST`の中を見てみたのですが、その時は、入力情報を受け取ることはできていました。
510
+
511
+
512
+
513
+ `Spot`も`Other`も`Create`画面において、レコードの保存はできます。
514
+
515
+
516
+
451
517
  Python v3.7.7
452
518
 
453
519
  Visual Studio Code v1.53.2