[Django3.0]ForeignKeyを使ったモデルとviews.pyの保存処理の書き方

見積登録ページを作成するため、hasManyのモデルをmigrationした後にModelFormを作っていきます。

models.py
-> ForeignKeyでClients ModelのhasManyを設定しています。

class Estimates(models.Model):
	client = models.ForeignKey(Clients, null=True, blank=True, on_delete=models.PROTECT)
	estimate_date = models.DateField()
	position = models.CharField(max_length=50, null=True, blank=True)
	name = models.CharField(max_length=255, null=True, blank=True)
	title = models.CharField(max_length=255)
	classification1 = models.CharField(max_length=255, null=True, blank=True)
	// 省略
	total = models.IntegerField(null=True, blank=True)
	remark = models.TextField(max_length=300, null=True, blank=True)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)

	def __str__(self):
		return self.title

forms.py

class EstimatesForm(forms.ModelForm):
	class Meta:
		model = Estimates
		fields = ['client', 'estimate_date', 'position', 'name', 'title', 'classification1', 'classification2', 'classification3', 'classification4', 'classification5', 'classification6', 'classification7', 'classification8', 'classification9', 'classification10', 'type1', 'type2', 'type3', 'type4', 'type5', 'type6', 'type7', 'type8', 'type9', 'type10', 'name1', 'name2', 'name3', 'name4', 'name5', 'name6', 'name7', 'name8', 'name9', 'name10', 'size1', 'size2', 'size3', 'size4', 'size5', 'size6', 'size7', 'size8', 'size9', 'size10', 'unit1', 'unit2', 'unit3', 'unit4', 'unit5', 'unit6', 'unit7', 'unit8', 'unit9', 'unit10', 'price1', 'price2', 'price3', 'price4', 'price5', 'price6', 'price7', 'price8', 'price9', 'price10', 'total', 'remark']

続いて、views.pyでestimate_input関数を書いていきます。

views.py

from .models import Estimates
from .forms import EstimatesForm

def estimate_input(request):
	params = {
		'form': EstimatesForm()
	}
	return render(request, 'sales/estimate_input.html')

## view
### Select文でリレーションの呼び出し
– viewはwidget_tweaksを使っています。
– form.${親テーブル} とするだけでselect文を作ってくれます。

estimate_input.html

<div class="form-group">
						<label for="client">得意先(選択)</label>
						{% render_field form.client class="form-control" %}
					</div>

– 見積の合計金額は、vue.jsでcomputedします。

var app = new Vue({
			el: '#app',
			data: {
				tax:10,
				price_1:'',
				// 省略
			},
			computed: {
				add1: function(){
					return (this.price_1 * this.unit_1 + this.price_2 * this.unit_2 + this.price_3 * this.unit_3 + this.price_4 * this.unit_4 + this.price_5 * this.unit_5 + this.price_6 * this.unit_6 + this.price_7 * this.unit_7 + this.price_8 * this.unit_8 + this.price_9 * this.unit_9 + this.price_10 * this.unit_10 ) * (100 + this.tax) / 100;

				}
			}

### views.py
formで飛ばして、最後に保存します

def estimate_complete(request):
	if(request.method == 'POST'):
		data = EstimatesForm(request.POST)
		if data.is_valid():
			client = request.POST['client']
			estimate_date = request.POST['estimate_date']
			position = request.POST['position']
			name = request.POST['name']
			title = request.POST['title']
			classification1 = request.POST['classification1']
			// 省略
			total = request.POST['total']
			remark = request.POST['remark']
			estimates = Estimates(client_id=client, estimate_date=estimate_date, position=position, name=name, title=title, classification1=classification1, classification2=classification2, classification3=classification3, classification4=classification4, classification5=classification5, classification6=classification6, classification7=classification7, 
				classification8=classification8, classification9=classification9, classification10=classification10, type1=type1, type2=type2, type3=type3, type4=type4, type5=type5, type6=type6, type7=type7, type8=type8, type9=type9, type10=type10, name1=name1, name2=name2, name3=name3, name4=name4, name5=name5, name6=name6, name7=name7, name8=name8, 
				name9=name9, name10=name10, size1=size1, size2=size2, size3=size3, size4=size4, size5=size5, size6=size6, size7=size7, size8=size8, size9=size9, size10=size10, unit1=unit1, unit2=unit2, unit3=unit3, unit4=unit4, unit5=unit5, unit6=unit6, unit7=unit7, unit8=unit8, unit9=unit9, unit10=unit10, price1=price1, price2=price2, price3=price3,
				price4=price4, price5=price5, price6=price6, price7=price7, price8=price8, price9=price9, price10=price10, total=total, remark=remark)
			estimates.save()
			return render(request, 'sales/estimate_complete.html')
		else:
			params = {
				'form': EstimatesForm(request.POST),
			}
			return render(request, 'sales/estimate_input.html', params)
	return render(request, 'sales/estimate_complete.html')

### 結合テスト
修正しまくって、上手く動くようになるまで数日かかりました。

mysql側も確認し、ちゃんと入っています。
client_idには親テーブルのidが入っています。

mysql> select * from sales_estimates;
+—-+—————+———-+————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+——————+——–+——–+——-+——-+——-+——-+——-+——-+——-+——–+———————–+—————–+——-+——-+——-+——-+——-+——-+——-+——–+———–+——-+——-+——-+——-+——-+——-+——-+——-+——–+——-+——-+——-+——-+——-+——-+——-+——-+——-+——–+——–+——–+——–+——–+——–+——–+——–+——–+——–+———+——–+——–+—————————-+—————————-+———–+
| id | estimate_date | position | name | title | classification1 | classification2 | classification3 | classification4 | classification5 | classification6 | classification7 | classification8 | classification9 | classification10 | type1 | type2 | type3 | type4 | type5 | type6 | type7 | type8 | type9 | type10 | name1 | name2 | name3 | name4 | name5 | name6 | name7 | name8 | name9 | name10 | size1 | size2 | size3 | size4 | size5 | size6 | size7 | size8 | size9 | size10 | unit1 | unit2 | unit3 | unit4 | unit5 | unit6 | unit7 | unit8 | unit9 | unit10 | price1 | price2 | price3 | price4 | price5 | price6 | price7 | price8 | price9 | price10 | total | remark | created_at | updated_at | client_id |
+—-+—————+———-+————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+——————+——–+——–+——-+——-+——-+——-+——-+——-+——-+——–+———————–+—————–+——-+——-+——-+——-+——-+——-+——-+——–+———–+——-+——-+——-+——-+——-+——-+——-+——-+——–+——-+——-+——-+——-+——-+——-+——-+——-+——-+——–+——–+——–+——–+——–+——–+——–+——–+——–+——–+———+——–+——–+—————————-+—————————-+———–+
| 2 | 2020-09-19 | | 山田太郎 | AWS構築費用 | サーバ | サーバ | | | | | | | | | 設計 | 構築 | | | | | | | | | 基本設計書作成 | EC2初期構築 | | | | | | | | | 設計書 | | | | | | | | | | 1 | 1 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 50000 | 300000 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 385000 | | 2020-09-19 13:00:45.342104 | 2020-09-19 13:00:45.342128 | 19 |
+—-+—————+———-+————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+—————–+——————+——–+——–+——-+——-+——-+——-+——-+——-+——-+——–+———————–+—————–+——-+——-+——-+——-+——-+——-+——-+——–+———–+——-+——-+——-+——-+——-+——-+——-+——-+——–+——-+——-+——-+——-+——-+——-+——-+——-+——-+——–+——–+——–+——–+——–+——–+——–+——–+——–+——–+———+——–+——–+—————————-+—————————-+———–+
1 row in set (0.00 sec)

これ実装するのにすごい時間かかったけど、ここまで来たら、大分山を超えた感があります。後は基本的なところはauthとメールなど細かな機能でしょう。デバッグする際に、form.errors.itemsで画面にエラーメッセージを表示させてからスピードが上がりました。