How to create a class-based view for multiple file uploads - python

I am uploading multiple files and I would like to use classes in the models.py, forms.py, and also views.py so that I the code is easily changeable in one place and reused.
The issue I am having is not understanding how to implement a class-based view for file uploads that allows me to create instances for reuse.
I have tried to implement a class-based view as as shown below but I believe I need to insert an init method and insert self. on the attributes but I am confused being that there are functions such as reverse_lazy in the attributes.
My views.py
class FileUploadView(View):
form_class = DocumentForm
success_url = reverse_lazy('home') # want different url for every instance
template_name = 'file_upload.html' # same for template_name
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect(self.success_url)
else:
return render(request, self.template_name, {'form': form})
EDIT: As Gasanov suggested, you can insert the success_url and template_name as parameters in the url_patterns as such:
urlpatterns = [
path('stylist/', payroll_views.FileUploadView.as_view(
success_url=reverse_lazy('landing.html'), template_name='stylist.html'), name='stylist'),
]
This allows for reuse of the class-based view in a clean and pragmatic way.

I'm not sure where you want to declare different success_url or template_name, but you can do it in urls.py like this:
urlpatterns = [
path('a/', FileUploadView.as_view(success_url=reverse_lazy('home'), template_name='index1.html')),
path('b/', FileUploadView.as_view(success_url=reverse_lazy('nothome'), template_name='index2.html'))
]

Looks like you could benefit from using FormView instead of View, because you are basically reimplementing post from there.
In order to "overwrite" success_url and template_name, there are already methods that do just that:
class FileUploadView(FormView):
...
def get_success_url(self):
if blahblah:
return something
else:
return something_else
def get_template_names(self):
if blahblah:
return ['some/template.html']
else:
return ['another/template.html']

Related

Initial value of a dictionary while handling forms with a class-based view

I read the Class-Based View page in the Django documentation site and I stumbled with this code example:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View
from .forms import MyForm
class MyFormView(View):
form_class = MyForm
initial = {'key': 'value'}
template_name = 'form_template.html'
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# <process form cleaned data>
return HttpResponseRedirect('/success/')
return render(request, self.template_name, {'form': form})
My question is why I should create the initial = {'key': 'value'} dictionary.
The initial attribute is not mandatory. It's default value is an empty dictionary. You would use it if you want to pre-populate fields in your form, maybe because that's what the majority of user's would select. For example if you have a field named country, you can set initial = {'country':'USA'} to populate that field for the user as the view is rendered.

Django Class-based Form is not displayed

New in Django. I'm trying to show form, but have instead it:
messag.views.CommentAdd object at 0x037860D0
forms.py:
from django.http import JsonResponse
class AjaxableResponseMixin(object):
"""
Mixin to add AJAX support to a form.
Must be used with an object-based FormView (e.g. CreateView)
"""
def form_invalid(self, form):
response = super(AjaxableResponseMixin, self).form_invalid(form)
if self.request.is_ajax():
return JsonResponse(form.errors, status=400)
else:
return response
def form_valid(self, form):
# We make sure to call the parent's form_valid() method because
# it might do some processing (in the case of CreateView, it will
# call form.save() for example).
response = super(AjaxableResponseMixin, self).form_valid(form)
if self.request.is_ajax():
data = {
'pk': self.object.pk,
}
return JsonResponse(data)
else:
return response
views.py:
class CommentAdd(AjaxableResponseMixin, CreateView):
model = Comment
fields = ['author_name', 'text', 'root']
class ShowTree(ListView):
model = Comment
template_name = 'comment_tree.html'
def get_context_data(self, **kwargs):
context = super(ShowTree, self).get_context_data(**kwargs)
context['comment_form'] = CommentAdd()
return context
It doesn't work because you need to pass an form instance and you are passing a class based view. CreateView is a class based view, not a ModelForm.
It could be easier to create a CreateView like in the example and get the data to build the list in get_context_data()

Django. Unbound method in urls

I trying to accept localhost:8000/upload file and run into this problem
unbound method upload_file() must be called with Upload instance as first argument (got WSGIRequest instance instead)
urls.py
from django.conf.urls import include, url
from django.contrib import admin
from upload.views import Upload
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^upload/$', Upload.upload_file),
url(r'^thanks/$', Upload.thanks),
]
views.py
from django.http import HttpResponse
from django.template import RequestContext
from django.shortcuts import render_to_response
from upload.forms import FileForm
from upload.models import upFile
class Upload():
def upload_file(request):
if request.method == "POST":
form = FileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('thanks')
else:
form = FileForm()
return render_to_response('temp1.html', {'form': form}, context_instance = RequestContext(request))
def thanks(request):
return render_to_response('temp2.html')
Your view is not inheriting from a Django class based view: https://docs.djangoproject.com/en/1.8/topics/class-based-views/intro/
from django.views.generic import View
class Upload(View):
def upload_file(self, request):
if request.method == "POST":
form = FileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('thanks')
else:
form = FileForm()
return render_to_response('temp1.html', {'form': form}, context_instance = RequestContext(request))
def thanks(self, request):
return render_to_response('temp2.html')
You need to inherit form one of these view classes otherwise Django will treat your view as a normal class which is why you are getting that error.
you also need to adjust your urls.py:
urlpatterns = [
url(r'^upload/', Upload.as_view()),
]
After looking through the docs and at the structure of your vie I'm not sure you are structuring your views properly for class based views. You may want to read through the docs some more and adjust your structure accordingly.
I believe you will want somethign that looks more like this:
class Upload(View):
def get(self, request):
form = FileForm()
return render_to_response('temp1.html', {'form': form}, context_instance = RequestContext(request))
def post(self, request):
form = FileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('thanks')
else:
return render_to_response('temp2.html')
I don't understand why you have the Upload class at all. Python is not Java, there's no need to put everything into classes. Put upload_file and thanks at the module level, and refer to them directly in the url patterns.

Django: How to provide context for a FormView?

Newbie Question: I have a FormView which displays a sign-up form, using Django's validations, etc. It all works fine, however, I can't seem to work out how to provide any data (context) to the template. Currently, I have:
from django.shortcuts import render
from django.shortcuts import redirect
from django.http import HttpResponse
from accounts.forms import SignUpForm
class SignUpView(FormView):
template_name = 'accounts/signup.html'
form_class = SignUpForm
def form_valid(self, form):
# executes when form validates..
(...)
return redirect('/account/')
I tried adding some context data via get() per below, which I need when the page first displays, which sort of works except the input fields of the form are gone (the labels are all there):
def get(self, request):
return render(self.request, self.template_name, {'data': data })
Could someone please clarify why that is, and how to get it to work? To put it another way: When using FormView with form_valid(), where do I place code intended for the initial GET request?
You have to methods of doing it, if the data is related only to that get view then you can move on:
def get_context_data(self, **kwargs):
context = super(SignUpView, self).get_context_data(**kwargs)
something = something
context['something'] = something
return context
Or use a Mixin:
class SomeMixin(object):
def get_context_data(self, **kwargs):
context = super(SomeMixin, self).get_context_data(**kwargs)
something = something
context['something'] = something
return context
And then:
class SignUpView(SomeMixin, FormView):
def form_valid(self, form):
...

Send form data from views to template

Edit:
I want the 'success_url' (ie, result.html) to display the 'data' from 'form.process()'. The following code obviously doesn't work.
Can anyone please tell me what's wrong with it or suggest another way to basically view the context 'data' in a template (either in the form of list or dict), ie a better way to display data to the user after a form has been submitted.
Many thanks in advance.
-- urls.py --
url(r'^$', view='main_view'),
url(r'^result/$', view='result_view'),
-- views.py --
class ResultView(TemplateView):
template_name = "result.html"
class MainView(FormView):
template_name = 'index.html'
form_class = UserInputForm
success_url = 'result/'
def form_valid(self, form):
data = form.process()
return super(MainView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(MainView, self).get_context_data(**kwargs)
context['data'] = data
return context
main_view = MainView.as_view()
result_view = ResultView.as_view()
As far as I understood your question, you want to show the contents of the user submitted form in the result view. Is that correct?
In this case the method get_context_data won't help you at all, because it will only store data in the current context which is in MainView.
The form_valid method of FormView will make a HttpResponseRedirect to the success_url. So the question now is, how can we give the data to this view.
As explained in Django return redirect() with parameters the easiest way would be to put the data into the session. In the result.html-template you could then access this data as explained in Django: accessing session variables from within a template?
Here is the code:
class ResultView(TemplateView):
template_name = "result.html"
class MainView(FormView):
template_name = 'index.html'
form_class = UserInputForm
success_url = 'result/'
def form_valid(self, form):
self.request.session['temp_data'] = form.cleaned_data
return super(MainView, self).form_valid(form)
in the result.html template you could then access this temp_data so:
{{ request.session.temp_data }}
As suggested above, you can override get_context_data.
For example, you can do something like the below:
def get_context_data(self, **kwargs):
context = super(MainView, self).get_context_data(**kwargs)
#set some more context below.
context['foo'] = bar
...
return context
Look for get_context_data in context the Django class-based view docs. The dict returned by the overridden method will be passed into the templates.
There are a couple of things that could be your problem. First, in form_valid() method, you process the form before you call that class' parent form_valid(). Also, you're no storing the result in a common place for both methods to grab it. Try something like:
def form_valid(self, form):
self.data = form.cleaned_data
return super(MainView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(MainView, self).get_context_data(**kwargs)
context['data'] = self.data
return context

Categories

Resources