[Fixed]-Django – Mixing ListView and CreateView


I found the answer, there is 2 problems:

  • ListView and CreateView are “high level” mixin which aggregate “lower
    level” mixins. But these lower level mixins are not compatible together.
  • The View class calls directly the render_to_response(), but in my scenario, there is 2 view class and render_to_response() should only be called once at the end.

I was able “solve” this issue using the following steps:

Instead of calling ListView and CreateView, I used lower level mixins. Moreover I called explicitly BaseCreateView and BaseListView from which I “extracted” the form and object_list

class FormAndListView(BaseCreateView, BaseListView, TemplateResponseMixin):
    def get(self, request, *args, **kwargs):
        formView = BaseCreateView.get(self, request, *args, **kwargs)
        listView = BaseListView.get(self, request, *args, **kwargs)
        formData = formView.context_data['form']
        listData = listView.context_data['object_list']
        return render_to_response('textfrompdf/index.html', {'form' : formData, 'all_PDF' : listData},

It’s not clean but it works!



I use a lot of views that involve a form and a list of objects. Rather than trying to mixin things I just add the queryset into the context data as below.

class UploadFileView(CreateView):
    form_class = UploadFileForm
    success_url = 'listview'
    template_name = 'textfrompdf/index.html'

    def get_context_data(self, **kwargs):
        kwargs['object_list'] = PdfFile.objects.order_by('id')
        return super(UploadFileView, self).get_context_data(**kwargs)


Do not mix list and update views.
Instead, create two separate views for these tasks:

List view displays the list and a web form with action URL pointing to the create view.
Create view accepts POST data and

  • displays form with error message in case of failure;
  • redirects to the list view in case of success.

Also I’ve tried to use class-based views and found that they are too complex.
I think it is much easier to use old-style function views.


I have made my own class to solve this problem. I don’t know if it’s better or worse, but it works too. I have tried to use the generic mixins and have tested that validation and pagination work.

The code in GitHub

class ListAppendView(MultipleObjectMixin,
    """ A View that displays a list of objects and a form to create a new object.
    The View processes this form. """
    template_name_suffix = '_append'
    allow_empty = True

    def get(self, request, *args, **kwargs):
        self.object_list = self.get_queryset()
        allow_empty = self.get_allow_empty()
        if not allow_empty and len(self.object_list) == 0:
            raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
                          % {'class_name': self.__class__.__name__})
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        context = self.get_context_data(object_list=self.object_list, form=form)
        return self.render_to_response(context)

    def post(self, request, *args, **kwargs):
        self.object = None
        return super(ListAppendView, self).post(request, *args, **kwargs)

    def form_invalid(self, form):
        self.object_list = self.get_queryset()
        return self.render_to_response(self.get_context_data(object_list=self.object_list, form=form))

If you try it and find any errors, please tell me here or in GitHub.


I got into this problem and solve it with the following code, the answer by @jondykeman does not have pagination and other utilities for base classes. the other approaches that are proposed are a little complicated than the following:

class ObjectCreateView(LoginRequiredMixin, MultipleObjectMixin, View):

   queryset = Wallet.objects.all()

   def get(self, request):
       self.object_list = super(ObjectCreateView, self).get_queryset().filter(user=request.user)
       allow_empty = super(ObjectCreateView, self).get_allow_empty()

       if not allow_empty:
           if super(ObjectCreateView, self).get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'):
               is_empty = not self.object_list.exists()
               is_empty = not self.object_list
           if is_empty:
               raise Http404()
       context = super(ObjectCreateView, self).get_context_data()
       form = CreateObjectForm()
       context['form'] = form
       return render(request, 'objects/object-list.html', context=context)

  def post(self, request):
       form = CreateWalletForm(request.POST)
       if form.is_valid():
        Object.objects.create(name=form.cleaned_data['name'], user=request.user)
           messages.success(request, 'Object is created')
           messages.error(request, utility.get_form_errors_as_string(form))
       return redirect('objects:create')

Leave a comment