1

During form processing, I'd like to be able to set a foreign key field on a model object from its base model when the user selects a value from the dropdown list.

models.py

class Question(models.Model):
    question = models.CharField(max_length=200)

class Teacher(models.Model):
        name = models.CharField(max_length=200)
        l_name = models.CharField(max_length=200)
    
class Student_Answer(models.Model):
    teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)   
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    answer = models.SmallIntegerField(choices=RATING_CHOICES)

For example, I have five records in model A, two records in model B and I have two model forms.

forms.py

class AnswerForm(ModelForm):
    class Meta:
        model = Student_Answer
        fields = ('answer',)
        widgets = { 'answer': RadioSelect(choices=RATING_CHOICES),}

class TeacherForm(ModelForm):
    name = ModelChoiceField(queryset=Teacher.objects.all())
    class Meta:
        model = Teacher
        fields = ('name',)

For the questions, I just assign them directly within the view and then giving the instances to the foreign key after validation. Now, if I do the same for Teacher I mean this teacher = Teacher.objects.get(id=2) and then choice.teacher = teacher it's going to work perfectly. But that is not the case which I want. The teacher will be selected by the user. I am looking for a way like the below view.

views.py

def index(request):
question1 = Question.objects.get(id=6)
question2 = Question.objects.get(id=7)
if request.method == "POST":
    teacher_form = TeacherForm(request.POST)
    form = AnswerForm(request.POST, prefix='question1')
    form1 = AnswerForm(request.POST, prefix='question2')

    if (form.is_valid):
        choice = form.save(commit=False)
        choice.question = question1
        choice.teacher = teacher_form
        choice.save()

        choice = form1.save(commit=False)
        choice.teacher = teacher_form
        choice.question = question2
        choice.save()

        return HttpResponseRedirect('/submited/')

else:
    teacher_form = TeacherForm()
    form = AnswerForm(prefix='question1')
    form1 = AnswerForm(prefix='question2')

context = {
    'teacher_form': teacher_form,
    'form': form,
    'form1': form1,
    'all_questions': Question.objects.all(),
}
return render(request, 'index.html', context)

The problem with the above example is this error:

ValueError: Cannot assign "<TeacherForm bound=True, valid=Unknown, fields=(name)>": "Student_Answer.teacher" must be a "Teacher" instance.

If I include instance choice.teacher = teacher_form.instance then this error will come up:

Exception Value:    
save() prohibited to prevent data loss due to unsaved related object 'teacher'.

And if I save the form in the first line after validation it is going to create a new record for the Teacher class. I do not want to save a new record, I just want to store the selected value in the template and store that value for the Student_Answer class. Is there a way to fix this?

index.html

<form action="{% url 'evaluation:index'%}" method="POST">
  {% csrf_token %}
  {{ teacher_form }}
  {{ all_questions.0 }}
  {{ form }}
  {{ all_questions.1}}
  {{ form1 }}
  
  <input type="submit" value="Vote">
</form>

1 Answer 1

1

First of all - a critical problem of your view. You are using several forms (which is not that rare and quite possible) but you validate only single one - but you must validate ALL of them. There was a nice article on doing that, if I find it I'll link it in updates. Your second error comes out exactly because of it - you trying to set instance from a form which is not validated.

 if all([form.is_valid(), form1.is_valid(), teacher_form.is_valid()]):

Second, yes, you don't want to save() that teacher form, you even don't need it to be a ModelForm, just forms.Form with ModelChoiceField will do. You just need to validate it and then access it's cleaned_data

# I'm renaming field to 'teacher' since 'name' is poor choice for Teacher object
teacher = teacher_form.cleaned_data['teacher']

And at last for a number of Question forms, you probably want a model formset (2-3) constant forms may do for current approach but more or varying number of questions will make it really bothersome.

formsets: https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/#model-formsets

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.