Understanding how to combine FormView and ListView

I working on creating a view that adds a form to a ListView to be able to add new objects to the database from the view. I followed the example in using mixins with class-based views from the docs. Since I’m not using a DetailView I didn’t use a mixin at all and modified it to work for a ListView. I had to modify my definition of post to include getting the form and saving the form while the examples in the docs doesn’t do that. I’m guessing this is because it states:

we’re not going to store this in a relational database but instead in something more esoteric that we won’t worry about here.

but I think my understanding of how I should be defining the post function is not correct even though it works so far. Do the views I have setup look correct or am I missing something fundamental in how these are inheriting from the GCBVs?

Thanks!

views.py

from django.views.generic import ListView, FormView
from django.views.generic.edit import ModelFormMixin
from django.views import View
from django.urls import reverse_lazy
from .models import Purchase, PurchaseForm

class PurchaseDisplay(ListView):
     model = Purchase
     template_name = 'purchase_list.html'

     def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['form'] = PurchaseForm
        return context

class PurchaseAdd(FormView):
    template_name = 'purchase_list.html'
    form_class = PurchaseForm
    model = Purchase

  def post(self, request, *args, **kwargs):
      form = self.get_form()
      if form.is_valid():
          form.save()
      return super().post(request, *args, **kwargs)

    success_url = reverse_lazy('purchase_list')

class PurchaseListView(View):

    def get(self, request, *args, **kwargs):
        view = PurchaseDisplay.as_view()
        return view(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        view = PurchaseAdd.as_view()
        return view(request, *args, **kwargs)

I’ve never done this - looks interesting. But just comparing what the documentation states for the “get_context_data” method:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['form'] = AuthorInterestForm()
    return context

vs what you list as what you defined:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['form'] = PurchaseForm
    return context

The documentation shows assigning an instance of the AuthorInstanceForm class while you’re assigning the class itself

    context['form'] = AuthorInterestForm()

vs

    context['form'] = PurchaseForm

So perhaps try?

    context['form'] = PurchaseForm()

Also, in the future, it would generally be more helpful if you either provide whatever error message is being thrown or to describe the behavior that you’re seeing. It helps the person responding try to focus on the issue being asked about rather than trying to guess what the problem is.

Ken

Ken,

Good catch on that my error of assigning the class instead of an instance. I’ll fix that.

I’m not getting any actual errors with my code, at least in how I need it to function early on in the project. I’m looking for any feedback in how I’ve defined post in the PurchaseAdd view and if there is a more correct way to define handling POST requests for a FormView.

I’m still trying to understand how CBVs go together and what is best practice to modify. In this case, from what I can find, post defined in the FormView never executes form.save(), but post in CreateView does (which I would use if I wasn’t trying to combine a form with a ListView). So I have to override post to save the form but I don’t know if the way I have implemented that is the best way.

“Best” is clearly a subjective element, and you could get multiple different opinions from different people. What I’m not getting from your post is why you’re looking to take this approach. So I’ll ask: Do you have a specific reason why you want to take this approach?

Personally, I wouldn’t bother mixing ListView with a FormView. Unless I had a situation where there was an obvious benefit to mixing the two, I would use CreateView, but extend it to add the list to the top. (I’m not seeing where the ListView provides sufficient additional benefit to overcome the complexity introduced by mixing it in another view. )

In other words, as I’m reading your original message, it looks like you’re approaching this as “A list view with a form attached to it”. I would take the other approach, and consider this page to be “a create view with a listing of objects attached to it” - build my handing of this page around the CreateView - adding whatever data I want to display in my page template as additional context.

Ken

2 Likes

I hadn’t considered taking the CreateView approach with a list added. I was mainly just trying to use what the docs has suggested for a similar situation. I’ll give that a shot.

Thanks for the help!