Reputation: 95
I am trying to build a Survey App. I have defined a model for Survey, Questions, Responses as below.
What I want to do is create a Survey (in admin, which I can do) then display this Survey as a page which users can fill in.
I'm using Class Based Views for the first time and I'm stuck with how to render the Survey form along with the associated Questions and allow input of Answers.
In Admin, I've created a Survey and successfully added Questions to it. FormView (and UpdateView, tried as part of another SO answer) allows me to display the Survey model's 'name' and 'description' attributes - but the questions don't appear.
What do I need to do to make the Survey and it's Questions available in my form (and allow the user to enter a response)?
MODELS
class Survey(models.Model):
name = models.CharField(max_length=400)
description = models.TextField()
def survey_questions(self):
if self.pk:
return Question.objects.filter(survey=self.pk)
else:
return None
def publish(self):
self.published_date = timezone.now()
self.save()
class Question(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
question = models.TextField()
class Response(models.Model):
member = models.ForeignKey(user_model, on_delete=models.SET_NULL, null=True, blank=True)
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now_add=True)
class AnswerBase(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
response = models.ForeignKey(Response, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class AnswerText(AnswerBase):
body = models.TextField(blank=True, null=True)
URLS
urlpatterns = [path('survey/<int:pk>/', views.SurveyResponseView.as_view(), name='survey_detail')]
VIEWS
class SurveyResponseView(FormView):
template_name = 'survey/survey_detail.html'
form_class = ResponseForm
FORMS
class ResponseForm(forms.ModelForm):
class Meta():
model = Survey
fields = '__all__'
Upvotes: 0
Views: 42
Reputation: 290
Okay so the way to do this is to use FormSets. They're a bit of a faff to setup, but shouldn't take you too long. I recommend using this small js library to help.
I also rewrote some of your code to hopefully make life a bit easier for you:
models.py
class SurveyTemplate(models.Model):
name = models.CharField(max_length=400)
description = models.TextField()
# You were missing this I believe
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
class Question(models.Model):
# Now you can do SurveyTemplate.questions.all()
survey = models.ForeignKey(Survey, on_delete=models.CASCADE, related_name='questions')
question = models.TextField()
class SurveyResponse(models.Model):
member = models.ForeignKey(user_model, on_delete=models.SET_NULL, null=True, blank=True)
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now_add=True)
class Answer(models.Model):
# Now you can do Question.answers.all()
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers')
# Now you can do Response.answers.all()
response = models.ForeignKey(Response, on_delete=models.CASCADE, related_name='answers')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
views.py
from django.views.generic import CreateView
# CreateView is Django's built in View for creating an object, you would be best to use it
class SurveyResponseView(CreateView):
model = Survey
template = 'survey/survey_detail.html'
form_class = ResponseForm
forms.py
class ResponseForm(forms.ModelForm):
class Meta():
model = Survey
fields = '__all__'
Upvotes: 1