View help with a form, post method and passing an id

I’ve successfully used forms before but this implementation is not working. My intention is the following. A particular classroom is shown at /gradebook/id. I want my view to take classroom_id and then save it with student names using the form.

#models.py
    class Student(models.Model):
        classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE)
        student_first = models.CharField(default='John', max_length=30)
        student_last = models.CharField(default='Smith', max_length=30)
        nickname = models.CharField(default='JohnS', max_length=31)
        attend = models.BooleanField(default=True)
        do_not_pick = models.BooleanField(default=False)

#forms.py
class SingleStudentForm(ModelForm):
    class Meta:
        model = Student
        fields = ('student_first', 'student_last', 'classroom',)
        widgets = {'classroom': forms.HiddenInput,}

#urls.py
app_name = 'gradebook'
urlpatterns = [path('<int:classroom_id>/', views.classdetail, name='classdetail'),]

#views.py
def classdetail(request, classroom_id):
    context = {'classroom': classroom_id}
    print("start")

    if request.method ==  'POST':
        form = SingleStudentForm(request.POST)
        print("post")

        if form.is_valid():
            student = form.save(commit=False)
            student.save()
            print("valid")

            #form = SingleStudentForm(None)
            #context['form'] = form
            return render(request, "gradebook/classdetail.html", context)

        else:
            context['form'] = form
            print("not valid")
            return render(request, "gradebook/classdetail.html", context)
    
    else:
        form = SingleStudentForm(None)
        context['form'] = form
        print("here")
        return render(request, "gradebook/classroom.html", context)

#templates
#classdetail.html
<div class="form-group">
      <form action="{% url 'gradebook:classdetail' classblock.id %}" method="post">
        {% csrf_token %} {{ form }}
        <div class="form-group">
          <button type="submit" class="btn btn-default">Submit</button>
        </div>
      </form>
</div>

#originating html template that takes you to the form/classroomdetail
<ul>
    {% for classroom in classroom_list %}
      <li><a href="{% url 'gradebook:classdetail' classroom.id %}">{{ classroom.classroom_name }}</a></li>
    {% endfor %}
 </ul>

When I run this, my terminal prints out “start” and “here”. This tells me that the my view does not think this is a post method since “post” never prints out. As well, I don’t think I’m passing the classroom_id to the SingleStudentForm.

Thank you.

You would only be posting when you fill out the form and press the submit button. Your first retrieval of the page by going to that URL is going to be a get.

When I press submit, “here” prints out and the student object isn’t saved.

Ok, there is some other missing information that may help here (like your complete urls.py file, both root and app). But, based upon just what you’ve posted so far, I’m going to guess at this time that you’re not rendering the action url correctly. (I’m surprised you’re not getting an actual error, but seeing the remainder of your urls would clear up my confusion there.)

In your template you have:

So you’re referencing an object named classblock in the url reference, but I don’t see where you’re inserting an object named classblock into your context for rendering.

1 Like

Thank you. I’m never sure how much code I should include in my questions. Rather than correct my code above, here is what I’ve updated it to. Your comment on the form action really got me thinking and I realized a few things. First, I was trying to pass the classroom object to the form but that is not necessary. I can just save it in the view (student.classroom = classblock). Secondly, I don’t need to specify a new URL in the template’s form action.

I’m a bit surprised that this code runs though. When the form.is_valid(), with the return render(request, "gradebook/classdetail.html", context), I don’t know how the classroom_id is persistent. I thought this reloads the template with the form, but how does it know what classroom_id to use? ie the url is …/gradebook/id but how does django know what id use when it reloads?

project/urls.py
urlpatterns = [
    path('', TemplateView.as_view(template_name='home.html'), name='home'),
    path('admin/', admin.site.urls),
    path('gradebook/', include('gradebook.urls')),
    path('gradebook/', include('django.contrib.auth.urls')),
    path("select2/", include("django_select2.urls")),
]

gradebook/urls.py
app_name = 'gradebook'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('signup/', SignUpView.as_view(), name='signup'),
    path('courses/', views.courses, name='courses'),
    path('classroom/', views.classroom, name='classroom'),
    path('<int:classroom_id>/', views.classdetail, name='classdetail'),
]

views.py
def classdetail(request, classroom_id):
    classblock  = Classroom.objects.get(id=classroom_id)

    if request.method ==  'POST':
        form = SingleStudentForm(request.POST)

        if form.is_valid():
            student = form.save(commit=False)
            student.classroom = classblock
            student.save()

            form = SingleStudentForm(None)
            context = {'form': form}
            return render(request, "gradebook/classdetail.html", context)

        else:
            context = {'form': form}
            return render(request, "gradebook/classdetail.html", context)

    else:
        print("here")
        form = SingleStudentForm(None)
        context = {'form': form}
        return render(request, "gradebook/classdetail.html", context)

template classdetail.html
<form action="" method="post">
  {% csrf_token %} {{ form }}
  <div class="form-group">
    <button type="submit" class="btn btn-default">Submit</button>
  </div>
</form>

In your form, you have this:

This creates a hidden field in the form, containing the primary key of a Classroom object. That field will then be submitted along with the rest of the form.

This:

creates an instance of the form object with the data being submitted - including the primary key stored in the hidden input field.

1 Like