お世話になっております。
表題についてご質問があります。
現在、Laravel、Djangoどちらかを利用した【集計後に編集可、また、PDFに書き出しのできるアンケートフォーム(会員機能あり)】を作成しています。
前回、別でデータベースのテーブル設計(論理設計)についてでテーブル設計について質問をさせていただきいました。
今回は、そのテーブル設計を元にフォームを作成する場合でつまづいてしまったところがあります。
下記図のように今回のアンケートに必要なテーブルおよび関係は1対1となります。
テーブル設計は本を見ながら行いましたが、
- 1ユーザーにつき1pdfしか作成不可
- pdfテーブルはuser_idを外部キーとしてuserテーブルを参照。最初はuserテーブルにpdf_idを入れていましたが、会員登録直後はpdfテーブルの主キーでもあるpdf_idがNULLになる場合もあるため、userテーブルにはpdf_idを入れずuser_idで紐付けを行いました。
- pdfとquestion1やquestion2、question3は、それぞれpdfテーブルに対して1つずつのみの対応であるため、それぞれ1対1にしました。
上記のことを意識しながらテーブルを設計をしました。
そして、このテーブルを元にDjangoでソースコードを記述していきました。
ソース
python
1# views.py 2 3from django.db import models 4 5from users.models import User 6 7 8class Pdf(models.Model): 9 id = models.AutoField(primary_key = True) 10 user = models.OneToOneField(User, on_delete = models.CASCADE) 11 created_at = models.DateTimeField(auto_now_add = True) 12 update_at = models.DateTimeField(auto_now = True) 13 14 15class Question1(models.Model): 16 pdf = models.OneToOneField(Pdf, on_delete = models.CASCADE) 17 asking_1 = models.CharField(max_length = 500) 18 asking_2 = models.CharField(max_length = 500) 19 asking_3 = models.CharField(max_length = 500) 20 asking_4 = models.CharField(max_length = 500) 21 asking_5 = models.CharField(max_length = 500) 22 23 24class Question2(models.Model): 25 pdf = models.OneToOneField(Pdf, on_delete = models.CASCADE) 26 asking_1 = models.CharField(max_length = 500) 27 asking_2 = models.CharField(max_length = 500) 28 asking_3 = models.CharField(max_length = 500) 29 asking_4 = models.CharField(max_length = 500) 30 asking_5 = models.CharField(max_length = 500) 31 32 33class Question3(models.Model): 34 pdf = models.OneToOneField(Pdf, on_delete = models.CASCADE) 35 asking_1 = models.CharField(max_length = 500) 36 asking_2 = models.CharField(max_length = 500) 37 asking_3 = models.CharField(max_length = 500) 38 asking_4 = models.CharField(max_length = 500) 39 asking_5 = models.CharField(max_length = 500) 40
python
1# forms.py 2 3from django.contrib.auth import get_user_model 4from django.forms import ModelForm, inlineformset_factory 5 6from .models import (Pdf, Question1, Question2, Question3) 7 8 9class ProcedureForm(ModelForm): 10 class Meta: 11 model = Question1 12 fields = '__all__' 13 14 def __init__(self, *args, **kwargs): 15 super().__init__(*args, **kwargs) 16 for field in self.fields.values(): 17 field.widget.attrs['class'] = 'form-control' 18
python
1# views.py 2 3from django.urls import reverse_lazy, reverse 4from django.views.generic import TemplateView, CreateView, FormView 5 6from pdf.models import Pdf 7from .forms import ProcedureForm 8 9 10# *-----------------------------------------------------* 11# 作成フォーム 12# *-----------------------------------------------------* 13class ProcedureView1(FormView): 14 template_name = 'pdf/procedure_input.html' 15 form_class = ProcedureForm 16 success_url = reverse_lazy('pdf:confirm') 17 18 def get_form(self, form_class = None): 19 """Return an instance of the form to be used in this view.""" 20 21 if self.request.method == 'POST': 22 form_data1 = self.request.POST 23 else: 24 form_data1 = self.request.session.get('form_data1', None) 25 26 return self.form_class(form_data1) 27 28 # form_valid()は、postされた際、validationがOKだった場合に、システムからコールされる関数です。 29 # 保存処理や、後処理、リダイレクト先を設定するために利用します。 30 # 親のform_valid()の実行結果を戻すと、デフォルトのリダイレクト先(success_url)が設定されます。 31 def form_valid(self, form): 32 self.request.session['form_data1'] = self.request.POST 33 return super().form_valid(form) 34 35 36# *-----------------------------------------------------* 37# 確認画面 38# *-----------------------------------------------------* 39class ProcedureConfirm(TemplateView): 40 template_name = 'pdf/procedure_confirm.html' 41 42 def get_context_data(self, **kwargs): 43 context = super().get_context_data(**kwargs) 44 form_data1 = self.request.session.get('form_data1', None) 45 context['form1'] = ProcedureForm(form_data1) 46 return context 47 48 49# *-----------------------------------------------------* 50# 作成完了時処理 51# *-----------------------------------------------------* 52class ProcedureComplete(CreateView): 53 model = Pdf 54 form_class = ProcedureForm 55 success_url = reverse_lazy('accounts:dashboard') # なぜかaccounts:dashboardにとばない 56 57 def get_form(self, form_class = None): 58 form_data1 = self.request.session.pop('form_data1', None) 59 return self.form_class(form_data1) 60
不明点1
入力画面にアクセスをすると、一番上にpdfの選択フォームが表示されてしまいます。
これはテーブル設計上pdf_idと紐づけている(つもり)なので、表示されず自動で値が入って欲しいです。
一応、下記のようにfieldsを個別に指定をすれば、pdfは表示されませんが、
最後の登録完了画面でエラーが発生します。
python
1# forms.py 2 class Meta: 3 model = Question1 4# fields = '__all__' 5 fields = ['asking_1', 'asking_2', 'asking_3', 'asking_4', 'asking_5', ]
# エラー内容 NOT NULL constraint failed: pdf_question1.pdf_id
ちなみに、forms.pyを
python
1# model = Question1 2 model = Pdf
のように、モデル対象モデルをPdfにすると、今度はユーザー名を選択できるようになってしまいます。
不明点2
肝心のデータベースへの登録が行われません。(これはテーブル設計に関係がなさそうですね、、。)
データベースの登録はviews.pyで行われていますが、うまくいきません。
(おまけにsuccess_url = reverse_lazy('accounts:dashboard')を指定していても、リダイレクトされない)
python
1# *-----------------------------------------------------* 2# 作成完了時処理 3# *-----------------------------------------------------* 4class ProcedureComplete(CreateView): 5 model = Pdf 6 form_class = ProcedureForm 7 success_url = reverse_lazy('accounts:dashboard') 8 9 def get_form(self, form_class = None): 10 form_data1 = self.request.session.pop('form_data1', None) 11 return self.form_class(form_data1)
以上となりますが、1対多におけるフォーム作成ついてはinlineformset_factoryを使うなど、情報が多く見受けられましたが、1対1についてはあまり存在がしませんでした。
どなたかこの場合はどうすればいいかがわかる方、また見当のつく方がいらっしゃいましたら、ご教授いただけると幸いです。
それではどうぞ宜しくお願いいたします。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。