Django: How to provide context for a FormView? - python

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):
...

Related

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

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']

Django form is still full after sign up

I have an app in django 1.11. Below is the view with the form where the user can sign up for the event, after saving he gets a message about the details - on the same page, at the same url. But, after saving the form is completed and after pressing F5 the next saving is performed. How can I avoid this?
I think something with the form_valid method is wrong.
class EventDetailView(DetailView, CreateView):
model = models.Event
form_class = forms.ParticipantForm
context_object_name = 'event'
template_name = 'events/event_detail.html'
def get_success_url(self):
return reverse('events:detail', kwargs={'slug': self.kwargs['slug'], 'pk': self.kwargs['pk']})
def form_valid(self, form):
self.object = form.save()
context = self.get_context_data()
context['registered_event'] = context['event']
return self.render_to_response(context)
After a successful form submission, you should redirect to prevent duplicate submissions.
def form_valid(self, form):
self.object = form.save()
return redirect(self.get_success_url())
Remember to add the import
from django.shortcuts import redirect
use flash message to show messages.
from django.contrib import messages as flash_messages
flash_messages.success(request, "Your Message Here")
and refresh page.

django class based view multiple form validatiton

I'm currently trying to have two forms on a single page. I'm using Class Based Views.
class TaskDetailView(FormMixin, generic.DetailView):
model = Task
template_name="tasks/detail.html"
form_class = NoteForm
form_class2 = DurationForm
def get_context_data(self, **kwargs):
context = super(TaskDetailView, self).get_context_data(**kwargs)
context['note_form'] = self.get_form()
context['notes'] = Note.objects.filter(task__slug=self.kwargs['slug'])
context['duration_form'] = self.form_class2()
context['duration'] = Duration.objects.all()
return context
def get_success_url(self):
return reverse('task_detail', kwargs={'slug': self.kwargs['slug']})
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
current_task = get_object_or_404(Task, slug=self.kwargs['slug'])
self.object = form.save(commit=False)
self.object.task = current_task
self.object.save()
return HttpResponse(self.get_success_url())
My current status is that I'm able to display all the forms and save ONLY the Notes form. I'm not able to save the Duration Form despite there's a 200 status POST, the data is not being saved to the database.
I think my mistake is that I'm not validating it but I'm really not sure how to, and there isn't much information on multiple forms on CBVs in Django.
I would really appreciate some guidance and assistance.
Thanks
Your post method doesn't do anything with the second form. You'd need to instantiate it and check its validity as you do with the first one.
A quite simple way of having multiple forms on one page would be to define some hidden parameter to differentiate between your POST actions, like:
<input name="formType" type="hidden" value="note">
In your CBV post method, you could:
form_type = request.POST.get('formType', None)
if form_type == 'note':

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()

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