I'm want to create one page with a form, and every time I submit the form it adds an item to the list below the form.
I can make it work using 2 pages:
- one page using the mixin
CreateView
to add items
- one page
ListView
to have the list.
But I'm trying to have the form and the list on the same page. So I tried to create a class with both mixin:
class FormAndListView(ListView, CreateView):
pass
Then I've used this class:
FormAndListView.as_view(
queryset=PdfFile.objects.order_by('id'),
context_object_name='all_PDF',
success_url = 'listview',
form_class = UploadFileForm,
template_name='textfrompdf/index.html',)),
But when I try to load the page, I get the error: Exception Value: 'FormAndListView' object has no attribute 'object'
Traceback:
File "C:\Program Files\Python_2.7\lib\site-packages\django\core\handlers\base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "C:\Program Files\Python_2.7\lib\site-packages\django\views\generic\base.py" in view
47. return self.dispatch(request, *args, **kwargs)
File "C:\Program Files\Python_2.7\lib\site-packages\django\views\generic\base.py" in dispatch
68. return handler(request, *args, **kwargs)
File "C:\Program Files\Python_2.7\lib\site-packages\django\views\generic\list.py" in get
122. return self.render_to_response(context)
File "C:\Program Files\Python_2.7\lib\site-packages\django\views\generic\base.py" in render_to_response
94. template = self.get_template_names(),
File "C:\Program Files\Python_2.7\lib\site-packages\django\views\generic\list.py" in get_template_names
134. names = super(MultipleObjectTemplateResponseMixin, self).get_template_names()
File "C:\Program Files\Python_2.7\lib\site-packages\django\views\generic\detail.py" in get_template_names
122. if self.object and self.template_name_field:
Exception Type: AttributeError at /PDF/
Exception Value: 'FormAndListView' object has no attribute 'object'
I've no idea how to debug that. Where to start?
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 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},
context_instance=RequestContext(request))
It's not clean but it works!
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,
MultipleObjectTemplateResponseMixin,
ModelFormMixin,
ProcessFormView):
""" 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.